summaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp237
1 files changed, 80 insertions, 157 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 8bffada69b9b6..44f4530781a8e 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -1,9 +1,8 @@
//= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -18,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/STLExtras.h"
@@ -58,7 +58,7 @@ public:
static void *getTag() { static int tag; return &tag; }
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
@@ -73,7 +73,38 @@ public:
typedef void (CStringChecker::*FnCheck)(CheckerContext &,
const CallExpr *) const;
+ CallDescriptionMap<FnCheck> Callbacks = {
+ {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy},
+ {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy},
+ {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp},
+ {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove},
+ {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy},
+ {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy},
+ {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy},
+ {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy},
+ {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat},
+ {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat},
+ {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat},
+ {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength},
+ {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength},
+ {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp},
+ {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp},
+ {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp},
+ {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp},
+ {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep},
+ {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy},
+ {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp},
+ {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero},
+ {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero},
+ };
+
+ // These require a bit of special handling.
+ CallDescription StdCopy{{"std", "copy"}, 3},
+ StdCopyBackward{{"std", "copy_backward"}, 3};
+ FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;
void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
@@ -1201,9 +1232,6 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
// void *memcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is the address of the destination buffer.
const Expr *Dest = CE->getArg(0);
@@ -1213,9 +1241,6 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
// void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is a pointer to the byte following the last written byte.
const Expr *Dest = CE->getArg(0);
@@ -1225,9 +1250,6 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
// void *memmove(void *dst, const void *src, size_t n);
// The return value is the address of the destination buffer.
const Expr *Dest = CE->getArg(0);
@@ -1237,18 +1259,12 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
// void bcopy(const void *src, void *dst, size_t n);
evalCopyCommon(C, CE, C.getState(),
CE->getArg(2), CE->getArg(1), CE->getArg(0));
}
void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
// int memcmp(const void *s1, const void *s2, size_t n);
CurrentFunctionDescription = "memory comparison function";
@@ -1323,18 +1339,12 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
void CStringChecker::evalstrLength(CheckerContext &C,
const CallExpr *CE) const {
- if (CE->getNumArgs() < 1)
- return;
-
// size_t strlen(const char *s);
evalstrLengthCommon(C, CE, /* IsStrnlen = */ false);
}
void CStringChecker::evalstrnLength(CheckerContext &C,
const CallExpr *CE) const {
- if (CE->getNumArgs() < 2)
- return;
-
// size_t strnlen(const char *s, size_t maxlen);
evalstrLengthCommon(C, CE, /* IsStrnlen = */ true);
}
@@ -1459,9 +1469,6 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
}
void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 2)
- return;
-
// char *strcpy(char *restrict dst, const char *restrict src);
evalStrcpyCommon(C, CE,
/* returnEnd = */ false,
@@ -1470,9 +1477,6 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
// char *strncpy(char *restrict dst, const char *restrict src, size_t n);
evalStrcpyCommon(C, CE,
/* returnEnd = */ false,
@@ -1481,9 +1485,6 @@ void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 2)
- return;
-
// char *stpcpy(char *restrict dst, const char *restrict src);
evalStrcpyCommon(C, CE,
/* returnEnd = */ true,
@@ -1492,9 +1493,6 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
// char *strlcpy(char *dst, const char *src, size_t n);
evalStrcpyCommon(C, CE,
/* returnEnd = */ true,
@@ -1504,9 +1502,6 @@ void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 2)
- return;
-
//char *strcat(char *restrict s1, const char *restrict s2);
evalStrcpyCommon(C, CE,
/* returnEnd = */ false,
@@ -1515,9 +1510,6 @@ void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
//char *strncat(char *restrict s1, const char *restrict s2, size_t n);
evalStrcpyCommon(C, CE,
/* returnEnd = */ false,
@@ -1526,8 +1518,9 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
+ // FIXME: strlcat() uses a different rule for bound checking, i.e. 'n' means
+ // a different thing as compared to strncat(). This currently causes
+ // false positives in the alpha string bound checker.
//char *strlcat(char *s1, const char *s2, size_t n);
evalStrcpyCommon(C, CE,
@@ -1881,35 +1874,23 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
}
void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 2)
- return;
-
//int strcmp(const char *s1, const char *s2);
evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false);
}
void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
//int strncmp(const char *s1, const char *s2, size_t n);
evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false);
}
void CStringChecker::evalStrcasecmp(CheckerContext &C,
const CallExpr *CE) const {
- if (CE->getNumArgs() < 2)
- return;
-
//int strcasecmp(const char *s1, const char *s2);
evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true);
}
void CStringChecker::evalStrncasecmp(CheckerContext &C,
const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
-
//int strncasecmp(const char *s1, const char *s2, size_t n);
evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true);
}
@@ -2043,9 +2024,6 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
//char *strsep(char **stringp, const char *delim);
- if (CE->getNumArgs() < 2)
- return;
-
// Sanity: does the search string parameter match the return type?
const Expr *SearchStrPtr = CE->getArg(0);
QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType();
@@ -2114,7 +2092,7 @@ void CStringChecker::evalStdCopyBackward(CheckerContext &C,
void CStringChecker::evalStdCopyCommon(CheckerContext &C,
const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
+ if (!CE->getArg(2)->getType()->isPointerType())
return;
ProgramStateRef State = C.getState();
@@ -2141,9 +2119,6 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C,
}
void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() != 3)
- return;
-
CurrentFunctionDescription = "memory set function";
const Expr *Mem = CE->getArg(0);
@@ -2192,9 +2167,6 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() != 2)
- return;
-
CurrentFunctionDescription = "memory clearance function";
const Expr *Mem = CE->getArg(0);
@@ -2237,110 +2209,53 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
C.addTransition(State);
}
-static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) {
- IdentifierInfo *II = FD->getIdentifier();
- if (!II)
- return false;
-
- if (!AnalysisDeclContext::isInStdNamespace(FD))
- return false;
-
- if (II->getName().equals(Name))
- return true;
-
- return false;
-}
//===----------------------------------------------------------------------===//
// The driver method, and other Checker callbacks.
//===----------------------------------------------------------------------===//
-static CStringChecker::FnCheck identifyCall(const CallExpr *CE,
- CheckerContext &C) {
- const FunctionDecl *FDecl = C.getCalleeDecl(CE);
- if (!FDecl)
+CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return nullptr;
+
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
return nullptr;
+ if (Call.isCalled(StdCopy)) {
+ return &CStringChecker::evalStdCopy;
+ } else if (Call.isCalled(StdCopyBackward)) {
+ return &CStringChecker::evalStdCopyBackward;
+ }
+
// Pro-actively check that argument types are safe to do arithmetic upon.
// We do not want to crash if someone accidentally passes a structure
- // into, say, a C++ overload of any of these functions.
- if (isCPPStdLibraryFunction(FDecl, "copy")) {
- if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType())
+ // into, say, a C++ overload of any of these functions. We could not check
+ // that for std::copy because they may have arguments of other types.
+ for (auto I : CE->arguments()) {
+ QualType T = I->getType();
+ if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
return nullptr;
- return &CStringChecker::evalStdCopy;
- } else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) {
- if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType())
- return nullptr;
- return &CStringChecker::evalStdCopyBackward;
- } else {
- // An umbrella check for all C library functions.
- for (auto I: CE->arguments()) {
- QualType T = I->getType();
- if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
- return nullptr;
- }
}
- // FIXME: Poorly-factored string switches are slow.
- if (C.isCLibraryFunction(FDecl, "memcpy"))
- return &CStringChecker::evalMemcpy;
- else if (C.isCLibraryFunction(FDecl, "mempcpy"))
- return &CStringChecker::evalMempcpy;
- else if (C.isCLibraryFunction(FDecl, "memcmp"))
- return &CStringChecker::evalMemcmp;
- else if (C.isCLibraryFunction(FDecl, "memmove"))
- return &CStringChecker::evalMemmove;
- else if (C.isCLibraryFunction(FDecl, "memset") ||
- C.isCLibraryFunction(FDecl, "explicit_memset"))
- return &CStringChecker::evalMemset;
- else if (C.isCLibraryFunction(FDecl, "strcpy"))
- return &CStringChecker::evalStrcpy;
- else if (C.isCLibraryFunction(FDecl, "strncpy"))
- return &CStringChecker::evalStrncpy;
- else if (C.isCLibraryFunction(FDecl, "stpcpy"))
- return &CStringChecker::evalStpcpy;
- else if (C.isCLibraryFunction(FDecl, "strlcpy"))
- return &CStringChecker::evalStrlcpy;
- else if (C.isCLibraryFunction(FDecl, "strcat"))
- return &CStringChecker::evalStrcat;
- else if (C.isCLibraryFunction(FDecl, "strncat"))
- return &CStringChecker::evalStrncat;
- else if (C.isCLibraryFunction(FDecl, "strlcat"))
- return &CStringChecker::evalStrlcat;
- else if (C.isCLibraryFunction(FDecl, "strlen"))
- return &CStringChecker::evalstrLength;
- else if (C.isCLibraryFunction(FDecl, "strnlen"))
- return &CStringChecker::evalstrnLength;
- else if (C.isCLibraryFunction(FDecl, "strcmp"))
- return &CStringChecker::evalStrcmp;
- else if (C.isCLibraryFunction(FDecl, "strncmp"))
- return &CStringChecker::evalStrncmp;
- else if (C.isCLibraryFunction(FDecl, "strcasecmp"))
- return &CStringChecker::evalStrcasecmp;
- else if (C.isCLibraryFunction(FDecl, "strncasecmp"))
- return &CStringChecker::evalStrncasecmp;
- else if (C.isCLibraryFunction(FDecl, "strsep"))
- return &CStringChecker::evalStrsep;
- else if (C.isCLibraryFunction(FDecl, "bcopy"))
- return &CStringChecker::evalBcopy;
- else if (C.isCLibraryFunction(FDecl, "bcmp"))
- return &CStringChecker::evalMemcmp;
- else if (C.isCLibraryFunction(FDecl, "bzero") ||
- C.isCLibraryFunction(FDecl, "explicit_bzero"))
- return &CStringChecker::evalBzero;
+ const FnCheck *Callback = Callbacks.lookup(Call);
+ if (Callback)
+ return *Callback;
return nullptr;
}
-bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
-
- FnCheck evalFunction = identifyCall(CE, C);
+bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
+ FnCheck Callback = identifyCall(Call, C);
// If the callee isn't a string function, let another checker handle it.
- if (!evalFunction)
+ if (!Callback)
return false;
// Check and evaluate the call.
- (this->*evalFunction)(C, CE);
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+ (this->*Callback)(C, CE);
// If the evaluate call resulted in no change, chain to the next eval call
// handler.
@@ -2476,18 +2391,26 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
C.addTransition(state);
}
+void ento::registerCStringModeling(CheckerManager &Mgr) {
+ Mgr.registerChecker<CStringChecker>();
+}
+
+bool ento::shouldRegisterCStringModeling(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
- CStringChecker *checker = mgr.registerChecker<CStringChecker>(); \
+ CStringChecker *checker = mgr.getChecker<CStringChecker>(); \
checker->Filter.Check##name = true; \
checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(CStringNullArg)
REGISTER_CHECKER(CStringOutOfBounds)
REGISTER_CHECKER(CStringBufferOverlap)
REGISTER_CHECKER(CStringNotNullTerm)
-
- void ento::registerCStringCheckerBasic(CheckerManager &Mgr) {
- Mgr.registerChecker<CStringChecker>();
- }