diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 150 |
1 files changed, 110 insertions, 40 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 12a576e5d80d5..8bffada69b9b6 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "InterCheckerAPI.h" #include "clang/Basic/CharInfo.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -124,6 +124,7 @@ public: void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const; void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const; void evalMemset(CheckerContext &C, const CallExpr *CE) const; + void evalBzero(CheckerContext &C, const CallExpr *CE) const; // Utility methods std::pair<ProgramStateRef , ProgramStateRef > @@ -158,7 +159,7 @@ public: static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); - static bool memsetAux(const Expr *DstBuffer, const Expr *CharE, + static bool memsetAux(const Expr *DstBuffer, SVal CharE, const Expr *Size, CheckerContext &C, ProgramStateRef &State); @@ -187,7 +188,7 @@ public: const Expr *Buf, const char *message = nullptr, bool WarnAboutSize = false) const { - // This is a convenience override. + // This is a convenience overload. return CheckBufferAccess(C, state, Size, Buf, nullptr, message, nullptr, WarnAboutSize); } @@ -553,7 +554,8 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get()); auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N); Report->addRange(S->getSourceRange()); - bugreporter::trackNullOrUndefValue(N, S, *Report); + if (const auto *Ex = dyn_cast<Expr>(S)) + bugreporter::trackExpressionValue(N, Ex, *Report); C.emitReport(std::move(Report)); } } @@ -1004,11 +1006,10 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, } } -bool CStringChecker::memsetAux(const Expr *DstBuffer, const Expr *CharE, +bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, const Expr *Size, CheckerContext &C, ProgramStateRef &State) { SVal MemVal = C.getSVal(DstBuffer); - SVal CharVal = C.getSVal(CharE); SVal SizeVal = C.getSVal(Size); const MemRegion *MR = MemVal.getAsRegion(); if (!MR) @@ -2183,13 +2184,59 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { // According to the values of the arguments, bind the value of the second // argument to the destination buffer and set string length, or just // invalidate the destination buffer. - if (!memsetAux(Mem, CharE, Size, C, State)) + if (!memsetAux(Mem, C.getSVal(CharE), Size, C, State)) return; State = State->BindExpr(CE, LCtx, MemVal); C.addTransition(State); } +void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() != 2) + return; + + CurrentFunctionDescription = "memory clearance function"; + + const Expr *Mem = CE->getArg(0); + const Expr *Size = CE->getArg(1); + SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy); + + ProgramStateRef State = C.getState(); + + // See if the size argument is zero. + SVal SizeVal = C.getSVal(Size); + QualType SizeTy = Size->getType(); + + ProgramStateRef StateZeroSize, StateNonZeroSize; + std::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, State, SizeVal, SizeTy); + + // If the size is zero, there won't be any actual memory access, + // In this case we just return. + if (StateZeroSize && !StateNonZeroSize) { + C.addTransition(StateZeroSize); + return; + } + + // Get the value of the memory area. + SVal MemVal = C.getSVal(Mem); + + // Ensure the memory area is not null. + // If it is NULL there will be a NULL pointer dereference. + State = checkNonNull(C, StateNonZeroSize, Mem, MemVal); + if (!State) + return; + + State = CheckBufferAccess(C, State, Size, Mem); + if (!State) + return; + + if (!memsetAux(Mem, Zero, Size, C, State)) + return; + + C.addTransition(State); +} + static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { IdentifierInfo *II = FD->getIdentifier(); if (!II) @@ -2207,60 +2254,86 @@ static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { // The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// -bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { +static CStringChecker::FnCheck identifyCall(const CallExpr *CE, + CheckerContext &C) { const FunctionDecl *FDecl = C.getCalleeDecl(CE); - if (!FDecl) - return false; + return nullptr; + + // 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()) + 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. - FnCheck evalFunction = nullptr; if (C.isCLibraryFunction(FDecl, "memcpy")) - evalFunction = &CStringChecker::evalMemcpy; + return &CStringChecker::evalMemcpy; else if (C.isCLibraryFunction(FDecl, "mempcpy")) - evalFunction = &CStringChecker::evalMempcpy; + return &CStringChecker::evalMempcpy; else if (C.isCLibraryFunction(FDecl, "memcmp")) - evalFunction = &CStringChecker::evalMemcmp; + return &CStringChecker::evalMemcmp; else if (C.isCLibraryFunction(FDecl, "memmove")) - evalFunction = &CStringChecker::evalMemmove; - else if (C.isCLibraryFunction(FDecl, "memset")) - evalFunction = &CStringChecker::evalMemset; + return &CStringChecker::evalMemmove; + else if (C.isCLibraryFunction(FDecl, "memset") || + C.isCLibraryFunction(FDecl, "explicit_memset")) + return &CStringChecker::evalMemset; else if (C.isCLibraryFunction(FDecl, "strcpy")) - evalFunction = &CStringChecker::evalStrcpy; + return &CStringChecker::evalStrcpy; else if (C.isCLibraryFunction(FDecl, "strncpy")) - evalFunction = &CStringChecker::evalStrncpy; + return &CStringChecker::evalStrncpy; else if (C.isCLibraryFunction(FDecl, "stpcpy")) - evalFunction = &CStringChecker::evalStpcpy; + return &CStringChecker::evalStpcpy; else if (C.isCLibraryFunction(FDecl, "strlcpy")) - evalFunction = &CStringChecker::evalStrlcpy; + return &CStringChecker::evalStrlcpy; else if (C.isCLibraryFunction(FDecl, "strcat")) - evalFunction = &CStringChecker::evalStrcat; + return &CStringChecker::evalStrcat; else if (C.isCLibraryFunction(FDecl, "strncat")) - evalFunction = &CStringChecker::evalStrncat; + return &CStringChecker::evalStrncat; else if (C.isCLibraryFunction(FDecl, "strlcat")) - evalFunction = &CStringChecker::evalStrlcat; + return &CStringChecker::evalStrlcat; else if (C.isCLibraryFunction(FDecl, "strlen")) - evalFunction = &CStringChecker::evalstrLength; + return &CStringChecker::evalstrLength; else if (C.isCLibraryFunction(FDecl, "strnlen")) - evalFunction = &CStringChecker::evalstrnLength; + return &CStringChecker::evalstrnLength; else if (C.isCLibraryFunction(FDecl, "strcmp")) - evalFunction = &CStringChecker::evalStrcmp; + return &CStringChecker::evalStrcmp; else if (C.isCLibraryFunction(FDecl, "strncmp")) - evalFunction = &CStringChecker::evalStrncmp; + return &CStringChecker::evalStrncmp; else if (C.isCLibraryFunction(FDecl, "strcasecmp")) - evalFunction = &CStringChecker::evalStrcasecmp; + return &CStringChecker::evalStrcasecmp; else if (C.isCLibraryFunction(FDecl, "strncasecmp")) - evalFunction = &CStringChecker::evalStrncasecmp; + return &CStringChecker::evalStrncasecmp; else if (C.isCLibraryFunction(FDecl, "strsep")) - evalFunction = &CStringChecker::evalStrsep; + return &CStringChecker::evalStrsep; else if (C.isCLibraryFunction(FDecl, "bcopy")) - evalFunction = &CStringChecker::evalBcopy; + return &CStringChecker::evalBcopy; else if (C.isCLibraryFunction(FDecl, "bcmp")) - evalFunction = &CStringChecker::evalMemcmp; - else if (isCPPStdLibraryFunction(FDecl, "copy")) - evalFunction = &CStringChecker::evalStdCopy; - else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) - evalFunction = &CStringChecker::evalStdCopyBackward; + return &CStringChecker::evalMemcmp; + else if (C.isCLibraryFunction(FDecl, "bzero") || + C.isCLibraryFunction(FDecl, "explicit_bzero")) + return &CStringChecker::evalBzero; + + return nullptr; +} + +bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { + + FnCheck evalFunction = identifyCall(CE, C); // If the callee isn't a string function, let another checker handle it. if (!evalFunction) @@ -2384,9 +2457,6 @@ void CStringChecker::checkLiveSymbols(ProgramStateRef state, void CStringChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { - if (!SR.hasDeadSymbols()) - return; - ProgramStateRef state = C.getState(); CStringLengthTy Entries = state->get<CStringLength>(); if (Entries.isEmpty()) |