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 4b78c20583419..26bf597bd950c 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);  | 
