diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp | 509 |
1 files changed, 0 insertions, 509 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp deleted file mode 100644 index 2ccb519891f3..000000000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ /dev/null @@ -1,509 +0,0 @@ -//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// This defines UnixAPIChecker, which is an assortment of checks on calls -// to various, widely used UNIX/Posix functions. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace ento; - -enum class OpenVariant { - /// The standard open() call: - /// int open(const char *path, int oflag, ...); - Open, - - /// The variant taking a directory file descriptor and a relative path: - /// int openat(int fd, const char *path, int oflag, ...); - OpenAt -}; - -namespace { - -class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { - mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; - mutable Optional<uint64_t> Val_O_CREAT; - -public: - DefaultBool CheckMisuse, CheckPortability; - - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - - void CheckOpen(CheckerContext &C, const CallExpr *CE) const; - void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const; - void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; - - void CheckOpenVariant(CheckerContext &C, - const CallExpr *CE, OpenVariant Variant) const; - - void ReportOpenBug(CheckerContext &C, - ProgramStateRef State, - const char *Msg, - SourceRange SR) const; - -}; - -class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > { -public: - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - -private: - mutable std::unique_ptr<BugType> BT_mallocZero; - - void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; - void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; - void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; - void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const; - void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const; - void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const; - void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; - - bool ReportZeroByteAllocation(CheckerContext &C, - ProgramStateRef falseState, - const Expr *arg, - const char *fn_name) const; - void BasicAllocationCheck(CheckerContext &C, - const CallExpr *CE, - const unsigned numArgs, - const unsigned sizeArg, - const char *fn) const; -}; - -} //end anonymous namespace - -static void LazyInitialize(const CheckerBase *Checker, - std::unique_ptr<BugType> &BT, - const char *name) { - if (BT) - return; - BT.reset(new BugType(Checker, name, categories::UnixAPI)); -} - -//===----------------------------------------------------------------------===// -// "open" (man 2 open) -//===----------------------------------------------------------------------===/ - -void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE, - CheckerContext &C) const { - const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD || FD->getKind() != Decl::Function) - return; - - // Don't treat functions in namespaces with the same name a Unix function - // as a call to the Unix function. - const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); - if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) - return; - - StringRef FName = C.getCalleeName(FD); - if (FName.empty()) - return; - - if (FName == "open") - CheckOpen(C, CE); - - else if (FName == "openat") - CheckOpenAt(C, CE); - - else if (FName == "pthread_once") - CheckPthreadOnce(C, CE); -} -void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, - ProgramStateRef State, - const char *Msg, - SourceRange SR) const { - ExplodedNode *N = C.generateErrorNode(State); - if (!N) - return; - - LazyInitialize(this, BT_open, "Improper use of 'open'"); - - auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N); - Report->addRange(SR); - C.emitReport(std::move(Report)); -} - -void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C, - const CallExpr *CE) const { - CheckOpenVariant(C, CE, OpenVariant::Open); -} - -void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C, - const CallExpr *CE) const { - CheckOpenVariant(C, CE, OpenVariant::OpenAt); -} - -void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, - const CallExpr *CE, - OpenVariant Variant) const { - // The index of the argument taking the flags open flags (O_RDONLY, - // O_WRONLY, O_CREAT, etc.), - unsigned int FlagsArgIndex; - const char *VariantName; - switch (Variant) { - case OpenVariant::Open: - FlagsArgIndex = 1; - VariantName = "open"; - break; - case OpenVariant::OpenAt: - FlagsArgIndex = 2; - VariantName = "openat"; - break; - }; - - // All calls should at least provide arguments up to the 'flags' parameter. - unsigned int MinArgCount = FlagsArgIndex + 1; - - // If the flags has O_CREAT set then open/openat() require an additional - // argument specifying the file mode (permission bits) for the created file. - unsigned int CreateModeArgIndex = FlagsArgIndex + 1; - - // The create mode argument should be the last argument. - unsigned int MaxArgCount = CreateModeArgIndex + 1; - - ProgramStateRef state = C.getState(); - - if (CE->getNumArgs() < MinArgCount) { - // The frontend should issue a warning for this case, so this is a sanity - // check. - return; - } else if (CE->getNumArgs() == MaxArgCount) { - const Expr *Arg = CE->getArg(CreateModeArgIndex); - QualType QT = Arg->getType(); - if (!QT->isIntegerType()) { - SmallString<256> SBuf; - llvm::raw_svector_ostream OS(SBuf); - OS << "The " << CreateModeArgIndex + 1 - << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) - << " argument to '" << VariantName << "' is not an integer"; - - ReportOpenBug(C, state, - SBuf.c_str(), - Arg->getSourceRange()); - return; - } - } else if (CE->getNumArgs() > MaxArgCount) { - SmallString<256> SBuf; - llvm::raw_svector_ostream OS(SBuf); - OS << "Call to '" << VariantName << "' with more than " << MaxArgCount - << " arguments"; - - ReportOpenBug(C, state, - SBuf.c_str(), - CE->getArg(MaxArgCount)->getSourceRange()); - return; - } - - // The definition of O_CREAT is platform specific. We need a better way - // of querying this information from the checking environment. - if (!Val_O_CREAT.hasValue()) { - if (C.getASTContext().getTargetInfo().getTriple().getVendor() - == llvm::Triple::Apple) - Val_O_CREAT = 0x0200; - else { - // FIXME: We need a more general way of getting the O_CREAT value. - // We could possibly grovel through the preprocessor state, but - // that would require passing the Preprocessor object to the ExprEngine. - // See also: MallocChecker.cpp / M_ZERO. - return; - } - } - - // Now check if oflags has O_CREAT set. - const Expr *oflagsEx = CE->getArg(FlagsArgIndex); - const SVal V = C.getSVal(oflagsEx); - if (!V.getAs<NonLoc>()) { - // The case where 'V' can be a location can only be due to a bad header, - // so in this case bail out. - return; - } - NonLoc oflags = V.castAs<NonLoc>(); - NonLoc ocreateFlag = C.getSValBuilder() - .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); - SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, - oflags, ocreateFlag, - oflagsEx->getType()); - if (maskedFlagsUC.isUnknownOrUndef()) - return; - DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); - - // Check if maskedFlags is non-zero. - ProgramStateRef trueState, falseState; - std::tie(trueState, falseState) = state->assume(maskedFlags); - - // Only emit an error if the value of 'maskedFlags' is properly - // constrained; - if (!(trueState && !falseState)) - return; - - if (CE->getNumArgs() < MaxArgCount) { - SmallString<256> SBuf; - llvm::raw_svector_ostream OS(SBuf); - OS << "Call to '" << VariantName << "' requires a " - << CreateModeArgIndex + 1 - << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) - << " argument when the 'O_CREAT' flag is set"; - ReportOpenBug(C, trueState, - SBuf.c_str(), - oflagsEx->getSourceRange()); - } -} - -//===----------------------------------------------------------------------===// -// pthread_once -//===----------------------------------------------------------------------===// - -void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, - const CallExpr *CE) const { - - // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. - // They can possibly be refactored. - - if (CE->getNumArgs() < 1) - return; - - // Check if the first argument is stack allocated. If so, issue a warning - // because that's likely to be bad news. - ProgramStateRef state = C.getState(); - const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion(); - if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) - return; - - ExplodedNode *N = C.generateErrorNode(state); - if (!N) - return; - - SmallString<256> S; - llvm::raw_svector_ostream os(S); - os << "Call to 'pthread_once' uses"; - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) - os << " the local variable '" << VR->getDecl()->getName() << '\''; - else - os << " stack allocated memory"; - os << " for the \"control\" value. Using such transient memory for " - "the control value is potentially dangerous."; - if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) - os << " Perhaps you intended to declare the variable as 'static'?"; - - LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'"); - - auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N); - report->addRange(CE->getArg(0)->getSourceRange()); - C.emitReport(std::move(report)); -} - -//===----------------------------------------------------------------------===// -// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc" -// with allocation size 0 -//===----------------------------------------------------------------------===// - -// FIXME: Eventually these should be rolled into the MallocChecker, but right now -// they're more basic and valuable for widespread use. - -// Returns true if we try to do a zero byte allocation, false otherwise. -// Fills in trueState and falseState. -static bool IsZeroByteAllocation(ProgramStateRef state, - const SVal argVal, - ProgramStateRef *trueState, - ProgramStateRef *falseState) { - std::tie(*trueState, *falseState) = - state->assume(argVal.castAs<DefinedSVal>()); - - return (*falseState && !*trueState); -} - -// Generates an error report, indicating that the function whose name is given -// will perform a zero byte allocation. -// Returns false if an error occurred, true otherwise. -bool UnixAPIPortabilityChecker::ReportZeroByteAllocation( - CheckerContext &C, - ProgramStateRef falseState, - const Expr *arg, - const char *fn_name) const { - ExplodedNode *N = C.generateErrorNode(falseState); - if (!N) - return false; - - LazyInitialize(this, BT_mallocZero, - "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); - - SmallString<256> S; - llvm::raw_svector_ostream os(S); - os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; - auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N); - - report->addRange(arg->getSourceRange()); - bugreporter::trackExpressionValue(N, arg, *report); - C.emitReport(std::move(report)); - - return true; -} - -// Does a basic check for 0-sized allocations suitable for most of the below -// functions (modulo "calloc") -void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C, - const CallExpr *CE, - const unsigned numArgs, - const unsigned sizeArg, - const char *fn) const { - // Sanity check for the correct number of arguments - if (CE->getNumArgs() != numArgs) - return; - - // Check if the allocation size is 0. - ProgramStateRef state = C.getState(); - ProgramStateRef trueState = nullptr, falseState = nullptr; - const Expr *arg = CE->getArg(sizeArg); - SVal argVal = C.getSVal(arg); - - if (argVal.isUnknownOrUndef()) - return; - - // Is the value perfectly constrained to zero? - if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { - (void) ReportZeroByteAllocation(C, falseState, arg, fn); - return; - } - // Assume the value is non-zero going forward. - assert(trueState); - if (trueState != state) - C.addTransition(trueState); -} - -void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C, - const CallExpr *CE) const { - unsigned int nArgs = CE->getNumArgs(); - if (nArgs != 2) - return; - - ProgramStateRef state = C.getState(); - ProgramStateRef trueState = nullptr, falseState = nullptr; - - unsigned int i; - for (i = 0; i < nArgs; i++) { - const Expr *arg = CE->getArg(i); - SVal argVal = C.getSVal(arg); - if (argVal.isUnknownOrUndef()) { - if (i == 0) - continue; - else - return; - } - - if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { - if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) - return; - else if (i == 0) - continue; - else - return; - } - } - - // Assume the value is non-zero going forward. - assert(trueState); - if (trueState != state) - C.addTransition(trueState); -} - -void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 1, 0, "malloc"); -} - -void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 2, 1, "realloc"); -} - -void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 2, 1, "reallocf"); -} - -void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 1, 0, "alloca"); -} - -void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero( - CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align"); -} - -void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 1, 0, "valloc"); -} - -void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE, - CheckerContext &C) const { - const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD || FD->getKind() != Decl::Function) - return; - - // Don't treat functions in namespaces with the same name a Unix function - // as a call to the Unix function. - const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); - if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) - return; - - StringRef FName = C.getCalleeName(FD); - if (FName.empty()) - return; - - if (FName == "calloc") - CheckCallocZero(C, CE); - - else if (FName == "malloc") - CheckMallocZero(C, CE); - - else if (FName == "realloc") - CheckReallocZero(C, CE); - - else if (FName == "reallocf") - CheckReallocfZero(C, CE); - - else if (FName == "alloca" || FName == "__builtin_alloca") - CheckAllocaZero(C, CE); - - else if (FName == "__builtin_alloca_with_align") - CheckAllocaWithAlignZero(C, CE); - - else if (FName == "valloc") - CheckVallocZero(C, CE); -} - -//===----------------------------------------------------------------------===// -// Registration. -//===----------------------------------------------------------------------===// - -#define REGISTER_CHECKER(CHECKERNAME) \ - void ento::register##CHECKERNAME(CheckerManager &mgr) { \ - mgr.registerChecker<CHECKERNAME>(); \ - } \ - \ - bool ento::shouldRegister##CHECKERNAME(const LangOptions &LO) { \ - return true; \ - } - -REGISTER_CHECKER(UnixAPIMisuseChecker) -REGISTER_CHECKER(UnixAPIPortabilityChecker) |