diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 | 
| commit | 5ffd83dbcc34f10e07f6d3e968ae6365869615f4 (patch) | |
| tree | 0e9f5cf729dde39f949698fddef45a34e2bc7f44 /contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | |
| parent | 1799696096df87b52968b8996d00c91e0a5de8d9 (diff) | |
| parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) | |
Notes
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp')
| -rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 357 | 
1 files changed, 250 insertions, 107 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 2fcb765cd4ee..3e46e2372516 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -11,9 +11,10 @@  //  //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ExprCXX.h"  #include "clang/AST/ParentMap.h"  #include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"  #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"  #include "clang/StaticAnalyzer/Core/Checker.h"  #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -21,6 +22,7 @@  #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"  #include "llvm/ADT/SmallString.h"  #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Casting.h"  #include "llvm/Support/raw_ostream.h"  using namespace clang; @@ -29,11 +31,8 @@ using namespace ento;  namespace {  class CallAndMessageChecker -  : public Checker< check::PreStmt<CallExpr>, -                    check::PreStmt<CXXDeleteExpr>, -                    check::PreObjCMessage, -                    check::ObjCMessageNil, -                    check::PreCall > { +    : public Checker<check::PreObjCMessage, check::ObjCMessageNil, +                     check::PreCall> {    mutable std::unique_ptr<BugType> BT_call_null;    mutable std::unique_ptr<BugType> BT_call_undef;    mutable std::unique_ptr<BugType> BT_cxx_call_null; @@ -48,11 +47,37 @@ class CallAndMessageChecker    mutable std::unique_ptr<BugType> BT_call_few_args;  public: -  DefaultBool Check_CallAndMessageUnInitRefArg; -  CheckerNameRef CheckName_CallAndMessageUnInitRefArg; +  // These correspond with the checker options. Looking at other checkers such +  // as MallocChecker and CStringChecker, this is similar as to how they pull +  // off having a modeling class, but emitting diagnostics under a smaller +  // checker's name that can be safely disabled without disturbing the +  // underlaying modeling engine. +  // The reason behind having *checker options* rather then actual *checkers* +  // here is that CallAndMessage is among the oldest checkers out there, and can +  // be responsible for the majority of the reports on any given project. This +  // is obviously not ideal, but changing checker name has the consequence of +  // changing the issue hashes associated with the reports, and databases +  // relying on this (CodeChecker, for instance) would suffer greatly. +  // If we ever end up making changes to the issue hash generation algorithm, or +  // the warning messages here, we should totally jump on the opportunity to +  // convert these to actual checkers. +  enum CheckKind { +    CK_FunctionPointer, +    CK_ParameterCount, +    CK_CXXThisMethodCall, +    CK_CXXDeallocationArg, +    CK_ArgInitializedness, +    CK_ArgPointeeInitializedness, +    CK_NilReceiver, +    CK_UndefReceiver, +    CK_NumCheckKinds +  }; + +  DefaultBool ChecksEnabled[CK_NumCheckKinds]; +  // The original core.CallAndMessage checker name. This should rather be an +  // array, as seen in MallocChecker and CStringChecker. +  CheckerNameRef OriginalName; -  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; -  void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;    void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;    /// Fill in the return value that results from messaging nil based on the @@ -62,6 +87,25 @@ public:    void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +  ProgramStateRef checkFunctionPointerCall(const CallExpr *CE, +                                           CheckerContext &C, +                                           ProgramStateRef State) const; + +  ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC, +                                     CheckerContext &C, +                                     ProgramStateRef State) const; + +  ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C, +                                      ProgramStateRef State) const; + +  ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC, +                                       CheckerContext &C, +                                       ProgramStateRef State) const; + +  ProgramStateRef checkArgInitializedness(const CallEvent &Call, +                                          CheckerContext &C, +                                          ProgramStateRef State) const; +  private:    bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,                            const Expr *ArgEx, int ArgumentNumber, @@ -79,7 +123,7 @@ private:    void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {      if (!BT) -      BT.reset(new BuiltinBug(this, desc)); +      BT.reset(new BuiltinBug(OriginalName, desc));    }    bool uninitRefOrPointer(CheckerContext &C, const SVal &V,                            SourceRange ArgRange, const Expr *ArgEx, @@ -144,7 +188,10 @@ bool CallAndMessageChecker::uninitRefOrPointer(      CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx,      std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD,      int ArgumentNumber) const { -  if (!Check_CallAndMessageUnInitRefArg) + +  // The pointee being uninitialized is a sign of code smell, not a bug, no need +  // to sink here. +  if (!ChecksEnabled[CK_ArgPointeeInitializedness])      return false;    // No parameter declaration available, i.e. variadic function argument. @@ -246,6 +293,10 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,      return true;    if (V.isUndef()) { +    if (!ChecksEnabled[CK_ArgInitializedness]) { +      C.addSink(); +      return true; +    }      if (ExplodedNode *N = C.generateErrorNode()) {        LazyInit_BT(BD, BT);        // Generate a report for this bug. @@ -272,6 +323,10 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,                               D->getStore());      if (F.Find(D->getRegion())) { +      if (!ChecksEnabled[CK_ArgInitializedness]) { +        C.addSink(); +        return true; +      }        if (ExplodedNode *N = C.generateErrorNode()) {          LazyInit_BT(BD, BT);          SmallString<512> Str; @@ -311,126 +366,158 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,    return false;  } -void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, -                                         CheckerContext &C) const{ +ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall( +    const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const {    const Expr *Callee = CE->getCallee()->IgnoreParens(); -  ProgramStateRef State = C.getState();    const LocationContext *LCtx = C.getLocationContext();    SVal L = State->getSVal(Callee, LCtx);    if (L.isUndef()) { +    if (!ChecksEnabled[CK_FunctionPointer]) { +      C.addSink(State); +      return nullptr; +    }      if (!BT_call_undef)        BT_call_undef.reset(new BuiltinBug( -          this, "Called function pointer is an uninitialized pointer value")); +          OriginalName, +          "Called function pointer is an uninitialized pointer value"));      emitBadCall(BT_call_undef.get(), C, Callee); -    return; +    return nullptr;    }    ProgramStateRef StNonNull, StNull;    std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());    if (StNull && !StNonNull) { +    if (!ChecksEnabled[CK_FunctionPointer]) { +      C.addSink(StNull); +      return nullptr; +    }      if (!BT_call_null)        BT_call_null.reset(new BuiltinBug( -          this, "Called function pointer is null (null dereference)")); +          OriginalName, "Called function pointer is null (null dereference)"));      emitBadCall(BT_call_null.get(), C, Callee); -    return; +    return nullptr;    } -  C.addTransition(StNonNull); +  return StNonNull;  } -void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE, -                                         CheckerContext &C) const { +ProgramStateRef CallAndMessageChecker::checkParameterCount( +    const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const { -  SVal Arg = C.getSVal(DE->getArgument()); -  if (Arg.isUndef()) { -    StringRef Desc; -    ExplodedNode *N = C.generateErrorNode(); -    if (!N) -      return; -    if (!BT_cxx_delete_undef) -      BT_cxx_delete_undef.reset( -          new BuiltinBug(this, "Uninitialized argument value")); -    if (DE->isArrayFormAsWritten()) -      Desc = "Argument to 'delete[]' is uninitialized"; -    else -      Desc = "Argument to 'delete' is uninitialized"; -    BugType *BT = BT_cxx_delete_undef.get(); -    auto R = std::make_unique<PathSensitiveBugReport>(*BT, Desc, N); -    bugreporter::trackExpressionValue(N, DE, *R); -    C.emitReport(std::move(R)); -    return; +  // If we have a function or block declaration, we can make sure we pass +  // enough parameters. +  unsigned Params = Call.parameters().size(); +  if (Call.getNumArgs() >= Params) +    return State; + +  if (!ChecksEnabled[CK_ParameterCount]) { +    C.addSink(State); +    return nullptr; +  } + +  ExplodedNode *N = C.generateErrorNode(); +  if (!N) +    return nullptr; + +  LazyInit_BT("Function call with too few arguments", BT_call_few_args); + +  SmallString<512> Str; +  llvm::raw_svector_ostream os(Str); +  if (isa<AnyFunctionCall>(Call)) { +    os << "Function "; +  } else { +    assert(isa<BlockCall>(Call)); +    os << "Block ";    } +  os << "taking " << Params << " argument" << (Params == 1 ? "" : "s") +     << " is called with fewer (" << Call.getNumArgs() << ")"; + +  C.emitReport( +      std::make_unique<PathSensitiveBugReport>(*BT_call_few_args, os.str(), N)); +  return nullptr;  } -void CallAndMessageChecker::checkPreCall(const CallEvent &Call, -                                         CheckerContext &C) const { -  ProgramStateRef State = C.getState(); +ProgramStateRef CallAndMessageChecker::checkCXXMethodCall( +    const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const { -  // If this is a call to a C++ method, check if the callee is null or -  // undefined. -  if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { -    SVal V = CC->getCXXThisVal(); -    if (V.isUndef()) { -      if (!BT_cxx_call_undef) -        BT_cxx_call_undef.reset( -            new BuiltinBug(this, "Called C++ object pointer is uninitialized")); -      emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); -      return; +  SVal V = CC->getCXXThisVal(); +  if (V.isUndef()) { +    if (!ChecksEnabled[CK_CXXThisMethodCall]) { +      C.addSink(State); +      return nullptr;      } +    if (!BT_cxx_call_undef) +      BT_cxx_call_undef.reset(new BuiltinBug( +          OriginalName, "Called C++ object pointer is uninitialized")); +    emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); +    return nullptr; +  } -    ProgramStateRef StNonNull, StNull; -    std::tie(StNonNull, StNull) = -        State->assume(V.castAs<DefinedOrUnknownSVal>()); +  ProgramStateRef StNonNull, StNull; +  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); -    if (StNull && !StNonNull) { -      if (!BT_cxx_call_null) -        BT_cxx_call_null.reset( -            new BuiltinBug(this, "Called C++ object pointer is null")); -      emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); -      return; +  if (StNull && !StNonNull) { +    if (!ChecksEnabled[CK_CXXThisMethodCall]) { +      C.addSink(StNull); +      return nullptr;      } - -    State = StNonNull; +    if (!BT_cxx_call_null) +      BT_cxx_call_null.reset( +          new BuiltinBug(OriginalName, "Called C++ object pointer is null")); +    emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); +    return nullptr;    } -  const Decl *D = Call.getDecl(); -  if (D && (isa<FunctionDecl>(D) || isa<BlockDecl>(D))) { -    // If we have a function or block declaration, we can make sure we pass -    // enough parameters. -    unsigned Params = Call.parameters().size(); -    if (Call.getNumArgs() < Params) { -      ExplodedNode *N = C.generateErrorNode(); -      if (!N) -        return; - -      LazyInit_BT("Function call with too few arguments", BT_call_few_args); - -      SmallString<512> Str; -      llvm::raw_svector_ostream os(Str); -      if (isa<FunctionDecl>(D)) { -        os << "Function "; -      } else { -        assert(isa<BlockDecl>(D)); -        os << "Block "; -      } -      os << "taking " << Params << " argument" -         << (Params == 1 ? "" : "s") << " is called with fewer (" -         << Call.getNumArgs() << ")"; +  return StNonNull; +} -      C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT_call_few_args, -                                                            os.str(), N)); -    } +ProgramStateRef +CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC, +                                            CheckerContext &C, +                                            ProgramStateRef State) const { +  const CXXDeleteExpr *DE = DC->getOriginExpr(); +  assert(DE); +  SVal Arg = C.getSVal(DE->getArgument()); +  if (!Arg.isUndef()) +    return State; + +  if (!ChecksEnabled[CK_CXXDeallocationArg]) { +    C.addSink(State); +    return nullptr;    } +  StringRef Desc; +  ExplodedNode *N = C.generateErrorNode(); +  if (!N) +    return nullptr; +  if (!BT_cxx_delete_undef) +    BT_cxx_delete_undef.reset( +        new BuiltinBug(OriginalName, "Uninitialized argument value")); +  if (DE->isArrayFormAsWritten()) +    Desc = "Argument to 'delete[]' is uninitialized"; +  else +    Desc = "Argument to 'delete' is uninitialized"; +  BugType *BT = BT_cxx_delete_undef.get(); +  auto R = std::make_unique<PathSensitiveBugReport>(*BT, Desc, N); +  bugreporter::trackExpressionValue(N, DE, *R); +  C.emitReport(std::move(R)); +  return nullptr; +} + +ProgramStateRef CallAndMessageChecker::checkArgInitializedness( +    const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const { + +  const Decl *D = Call.getDecl(); +    // Don't check for uninitialized field values in arguments if the    // caller has a body that is available and we have the chance to inline it.    // This is a hack, but is a reasonable compromise betweens sometimes warning    // and sometimes not depending on if we decide to inline a function.    const bool checkUninitFields = -    !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); +      !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));    std::unique_ptr<BugType> *BT;    if (isa<ObjCMethodCall>(Call)) @@ -441,13 +528,45 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,    const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);    for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {      const ParmVarDecl *ParamDecl = nullptr; -    if(FD && i < FD->getNumParams()) +    if (FD && i < FD->getNumParams())        ParamDecl = FD->getParamDecl(i);      if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), -                           Call.getArgExpr(i), i, -                           checkUninitFields, Call, *BT, ParamDecl)) -      return; +                           Call.getArgExpr(i), i, checkUninitFields, Call, *BT, +                           ParamDecl)) +      return nullptr;    } +  return State; +} + +void CallAndMessageChecker::checkPreCall(const CallEvent &Call, +                                         CheckerContext &C) const { +  ProgramStateRef State = C.getState(); + +  if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr())) +    State = checkFunctionPointerCall(CE, C, State); + +  if (!State) +    return; + +  if (Call.getDecl()) +    State = checkParameterCount(Call, C, State); + +  if (!State) +    return; + +  if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call)) +    State = checkCXXMethodCall(CC, C, State); + +  if (!State) +    return; + +  if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call)) +    State = checkCXXDeallocation(DC, C, State); + +  if (!State) +    return; + +  State = checkArgInitializedness(Call, C, State);    // If we make it here, record our assumptions about the callee.    C.addTransition(State); @@ -457,12 +576,16 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,                                                  CheckerContext &C) const {    SVal recVal = msg.getReceiverSVal();    if (recVal.isUndef()) { +    if (!ChecksEnabled[CK_UndefReceiver]) { +      C.addSink(); +      return; +    }      if (ExplodedNode *N = C.generateErrorNode()) {        BugType *BT = nullptr;        switch (msg.getMessageKind()) {        case OCM_Message:          if (!BT_msg_undef) -          BT_msg_undef.reset(new BuiltinBug(this, +          BT_msg_undef.reset(new BuiltinBug(OriginalName,                                              "Receiver in message expression "                                              "is an uninitialized value"));          BT = BT_msg_undef.get(); @@ -470,13 +593,15 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,        case OCM_PropertyAccess:          if (!BT_objc_prop_undef)            BT_objc_prop_undef.reset(new BuiltinBug( -              this, "Property access on an uninitialized object pointer")); +              OriginalName, +              "Property access on an uninitialized object pointer"));          BT = BT_objc_prop_undef.get();          break;        case OCM_Subscript:          if (!BT_objc_subscript_undef)            BT_objc_subscript_undef.reset(new BuiltinBug( -              this, "Subscript access on an uninitialized object pointer")); +              OriginalName, +              "Subscript access on an uninitialized object pointer"));          BT = BT_objc_subscript_undef.get();          break;        } @@ -503,10 +628,14 @@ void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,  void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,                                                 const ObjCMethodCall &msg,                                                 ExplodedNode *N) const { +  if (!ChecksEnabled[CK_NilReceiver]) { +    C.addSink(); +    return; +  }    if (!BT_msg_ret) -    BT_msg_ret.reset( -        new BuiltinBug(this, "Receiver in message expression is 'nil'")); +    BT_msg_ret.reset(new BuiltinBug(OriginalName, +                                    "Receiver in message expression is 'nil'"));    const ObjCMessageExpr *ME = msg.getOriginExpr(); @@ -601,20 +730,34 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,    C.addTransition(state);  } -void ento::registerCallAndMessageChecker(CheckerManager &mgr) { +void ento::registerCallAndMessageModeling(CheckerManager &mgr) {    mgr.registerChecker<CallAndMessageChecker>();  } -bool ento::shouldRegisterCallAndMessageChecker(const LangOptions &LO) { +bool ento::shouldRegisterCallAndMessageModeling(const CheckerManager &mgr) {    return true;  } -void ento::registerCallAndMessageUnInitRefArg(CheckerManager &mgr) { -  CallAndMessageChecker *Checker = mgr.getChecker<CallAndMessageChecker>(); -  Checker->Check_CallAndMessageUnInitRefArg = true; -  Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckerName(); +void ento::registerCallAndMessageChecker(CheckerManager &mgr) { +  CallAndMessageChecker *checker = mgr.getChecker<CallAndMessageChecker>(); + +  checker->OriginalName = mgr.getCurrentCheckerName(); + +#define QUERY_CHECKER_OPTION(OPTION)                                           \ +  checker->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] =                 \ +      mgr.getAnalyzerOptions().getCheckerBooleanOption(                        \ +          mgr.getCurrentCheckerName(), #OPTION); + +  QUERY_CHECKER_OPTION(FunctionPointer) +  QUERY_CHECKER_OPTION(ParameterCount) +  QUERY_CHECKER_OPTION(CXXThisMethodCall) +  QUERY_CHECKER_OPTION(CXXDeallocationArg) +  QUERY_CHECKER_OPTION(ArgInitializedness) +  QUERY_CHECKER_OPTION(ArgPointeeInitializedness) +  QUERY_CHECKER_OPTION(NilReceiver) +  QUERY_CHECKER_OPTION(UndefReceiver)  } -bool ento::shouldRegisterCallAndMessageUnInitRefArg(const LangOptions &LO) { +bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) {    return true;  }  | 
