diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp | 105 |
1 files changed, 94 insertions, 11 deletions
diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 4b78c2058341..26bf597bd950 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" #include <fcntl.h> @@ -28,6 +29,16 @@ 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 UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; @@ -37,17 +48,24 @@ public: 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 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; typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, const CallExpr *) const; private: + + void CheckOpenVariant(CheckerContext &C, + const CallExpr *CE, OpenVariant Variant) const; + bool ReportZeroByteAllocation(CheckerContext &C, ProgramStateRef falseState, const Expr *arg, @@ -89,25 +107,71 @@ void UnixAPIChecker::ReportOpenBug(CheckerContext &C, } void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { + CheckOpenVariant(C, CE, OpenVariant::Open); +} + +void UnixAPIChecker::CheckOpenAt(CheckerContext &C, const CallExpr *CE) const { + CheckOpenVariant(C, CE, OpenVariant::OpenAt); +} + +void UnixAPIChecker::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() < 2) { + if (CE->getNumArgs() < MinArgCount) { // The frontend should issue a warning for this case, so this is a sanity // check. return; - } else if (CE->getNumArgs() == 3) { - const Expr *Arg = CE->getArg(2); + } 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, - "Third argument to 'open' is not an integer", + SBuf.c_str(), Arg->getSourceRange()); return; } - } else if (CE->getNumArgs() > 3) { + } 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, - "Call to 'open' with more than three arguments", - CE->getArg(3)->getSourceRange()); + SBuf.c_str(), + CE->getArg(MaxArgCount)->getSourceRange()); return; } @@ -127,7 +191,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { } // Now check if oflags has O_CREAT set. - const Expr *oflagsEx = CE->getArg(1); + const Expr *oflagsEx = CE->getArg(FlagsArgIndex); const SVal V = state->getSVal(oflagsEx, C.getLocationContext()); if (!V.getAs<NonLoc>()) { // The case where 'V' can be a location can only be due to a bad header, @@ -153,10 +217,15 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { if (!(trueState && !falseState)) return; - if (CE->getNumArgs() < 3) { + 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, - "Call to 'open' requires a third argument when " - "the 'O_CREAT' flag is set", + SBuf.c_str(), oflagsEx->getSourceRange()); } } @@ -337,6 +406,11 @@ void UnixAPIChecker::CheckAllocaZero(CheckerContext &C, BasicAllocationCheck(C, CE, 1, 0, "alloca"); } +void UnixAPIChecker::CheckAllocaWithAlignZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align"); +} + void UnixAPIChecker::CheckVallocZero(CheckerContext &C, const CallExpr *CE) const { BasicAllocationCheck(C, CE, 1, 0, "valloc"); @@ -353,6 +427,12 @@ void UnixAPIChecker::checkPreStmt(const CallExpr *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; @@ -360,12 +440,15 @@ void UnixAPIChecker::checkPreStmt(const CallExpr *CE, SubChecker SC = llvm::StringSwitch<SubChecker>(FName) .Case("open", &UnixAPIChecker::CheckOpen) + .Case("openat", &UnixAPIChecker::CheckOpenAt) .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) .Case("calloc", &UnixAPIChecker::CheckCallocZero) .Case("malloc", &UnixAPIChecker::CheckMallocZero) .Case("realloc", &UnixAPIChecker::CheckReallocZero) .Case("reallocf", &UnixAPIChecker::CheckReallocfZero) .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero) + .Case("__builtin_alloca_with_align", + &UnixAPIChecker::CheckAllocaWithAlignZero) .Case("valloc", &UnixAPIChecker::CheckVallocZero) .Default(nullptr); |