diff options
| author | Ted Kremenek <kremenek@apple.com> | 2010-03-18 02:17:27 +0000 | 
|---|---|---|
| committer | Ted Kremenek <kremenek@apple.com> | 2010-03-18 02:17:27 +0000 | 
| commit | 9c05f4ef691888bf40ff7d8f0f76c6e1d5bf2199 (patch) | |
| tree | d8c7f55ae7efff7a3619c01f1fe7e6888f49dd71 /clang | |
| parent | e174fda979998fb9f49f669b6a4ca2005e770318 (diff) | |
| download | bcm5719-llvm-9c05f4ef691888bf40ff7d8f0f76c6e1d5bf2199.tar.gz bcm5719-llvm-9c05f4ef691888bf40ff7d8f0f76c6e1d5bf2199.zip  | |
Detect pass-by-value arguments that are structs that contain
uninitialized data.
llvm-svn: 98796
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/lib/Checker/CallAndMessageChecker.cpp | 137 | ||||
| -rw-r--r-- | clang/test/Analysis/uninit-vals-ps-region.c | 6 | 
2 files changed, 119 insertions, 24 deletions
diff --git a/clang/lib/Checker/CallAndMessageChecker.cpp b/clang/lib/Checker/CallAndMessageChecker.cpp index 9013c3818b0..f0729cd5208 100644 --- a/clang/lib/Checker/CallAndMessageChecker.cpp +++ b/clang/lib/Checker/CallAndMessageChecker.cpp @@ -24,7 +24,7 @@ namespace {  class CallAndMessageChecker    : public CheckerVisitor<CallAndMessageChecker> {    BugType *BT_call_null; -  BugType *BT_call_undef;   +  BugType *BT_call_undef;    BugType *BT_call_arg;    BugType *BT_msg_undef;    BugType *BT_msg_arg; @@ -47,9 +47,15 @@ private:    void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);    void EmitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME,                            ExplodedNode *N); -     +    void HandleNilReceiver(CheckerContext &C, const GRState *state, -                         const ObjCMessageExpr *ME);     +                         const ObjCMessageExpr *ME); + +  void LazyInit_BT_call_arg() { +    if (!BT_call_arg) +      BT_call_arg = new BuiltinBug("Pass-by-value argument in function call" +                                   " is undefined"); +  }  };  } // end anonymous namespace @@ -62,19 +68,19 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,    ExplodedNode *N = C.GenerateSink();    if (!N)      return; -     +    EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N);    R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,                         bugreporter::GetCalleeExpr(N));    C.EmitReport(R);  } -void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,  +void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,                                               const CallExpr *CE){ -   +    const Expr *Callee = CE->getCallee()->IgnoreParens();    SVal L = C.getState()->getSVal(Callee); -   +    if (L.isUndef()) {      if (!BT_call_undef)        BT_call_undef = @@ -82,21 +88,21 @@ void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,      EmitBadCall(BT_call_undef, C, CE);      return;    } -   +    if (isa<loc::ConcreteInt>(L)) {      if (!BT_call_null)        BT_call_null =          new BuiltinBug("Called function pointer is null (null dereference)");      EmitBadCall(BT_call_null, C, CE); -  }   -   +  } +    for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end();         I != E; ++I) { -    if (C.getState()->getSVal(*I).isUndef()) { +    const SVal &V = C.getState()->getSVal(*I); +    if (V.isUndef()) {        if (ExplodedNode *N = C.GenerateSink()) { -        if (!BT_call_arg) -          BT_call_arg = new BuiltinBug("Pass-by-value argument in function call" -                                       " is undefined"); +        LazyInit_BT_call_arg(); +          // Generate a report for this bug.          EnhancedBugReport *R = new EnhancedBugReport(*BT_call_arg,                                                       BT_call_arg->getName(), N); @@ -106,6 +112,89 @@ void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,          return;        }      } +    if (const nonloc::LazyCompoundVal *LV = +          dyn_cast<nonloc::LazyCompoundVal>(&V)) { +      const LazyCompoundValData *D = LV->getCVData(); + +      class FindUninitializedField { +      public: +        llvm::SmallVector<const FieldDecl *, 10> FieldChain; +      private: +        ASTContext &C; +        StoreManager &StoreMgr; +        MemRegionManager &MrMgr; +        Store store; +      public: +        FindUninitializedField(ASTContext &c, StoreManager &storeMgr, +                               MemRegionManager &mrMgr, Store s) +          : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} + +        bool Find(const TypedRegion *R) { +          QualType T = R->getValueType(C); +          if (const RecordType *RT = T->getAsStructureType()) { +            const RecordDecl *RD = RT->getDecl()->getDefinition(); +            assert(RD && "Referred record has no definition"); +            for (RecordDecl::field_iterator I = +                  RD->field_begin(), E = RD->field_end(); I!=E; ++I) { +              const FieldRegion *FR = MrMgr.getFieldRegion(*I, R); +              FieldChain.push_back(*I); +              T = (*I)->getType(); +              if (T->getAsStructureType()) { +                if (Find(FR)) +                  return true; +              } +              else { +                const SVal &V = StoreMgr.Retrieve(store, loc::MemRegionVal(FR)); +                if (V.isUndef()) +                  return true; +              } +              FieldChain.pop_back(); +            } +          } + +          return false; +        } +      }; + +      FindUninitializedField F(C.getASTContext(), +                               C.getState()->getStateManager().getStoreManager(), +                               C.getValueManager().getRegionManager(), +                               D->getStore()); + +      if (F.Find(D->getRegion())) { +        if (ExplodedNode *N = C.GenerateSink()) { +          LazyInit_BT_call_arg(); +          llvm::SmallString<512> Str; +          llvm::raw_svector_ostream os(Str); +          os << "Passed-by-value struct argument contains uninitialized data"; + +          if (F.FieldChain.size() == 1) +            os << " (e.g., field: '" << F.FieldChain[0]->getNameAsString() << "')"; +          else { +            os << " (e.g., via the field chain: '"; +            bool first = true; +            for (llvm::SmallVectorImpl<const FieldDecl *>::iterator +                 DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ +              if (first) +                first = false; +              else +                os << '.'; +              os << (*DI)->getNameAsString(); +            } +            os << "')"; +          } + +          // Generate a report for this bug. +          EnhancedBugReport *R = +            new EnhancedBugReport(*BT_call_arg, os.str(), N); +          R->addRange((*I)->getSourceRange()); + +          // FIXME: enhance track back for uninitialized value for arbitrary +          // memregions +          C.EmitReport(R); +        } +      } +    }    }  } @@ -138,7 +227,7 @@ void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C,          if (!BT_msg_arg)            BT_msg_arg =              new BuiltinBug("Pass-by-value argument in message expression" -                           " is undefined");       +                           " is undefined");          // Generate a report for this bug.          EnhancedBugReport *R = new EnhancedBugReport(*BT_msg_arg,                                                       BT_msg_arg->getName(), N); @@ -160,24 +249,24 @@ bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C,  void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C,                                                 const ObjCMessageExpr *ME,                                                 ExplodedNode *N) { -   +    if (!BT_msg_ret)      BT_msg_ret =        new BuiltinBug("Receiver in message expression is "                       "'nil' and returns a garbage value"); -   +    llvm::SmallString<200> buf;    llvm::raw_svector_ostream os(buf);    os << "The receiver of message '" << ME->getSelector().getAsString()       << "' is nil and returns a value of type '"       << ME->getType().getAsString() << "' that will be garbage"; -   +    EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N);    const Expr *receiver = ME->getReceiver();    report->addRange(receiver->getSourceRange()); -  report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,  +  report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,                              receiver); -  C.EmitReport(report);   +  C.EmitReport(report);  }  static bool SupportsNilWithFloatRet(const llvm::Triple &triple) { @@ -188,11 +277,11 @@ static bool SupportsNilWithFloatRet(const llvm::Triple &triple) {  void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,                                                const GRState *state,                                                const ObjCMessageExpr *ME) { -   +    // Check the return type of the message expression.  A message to nil will    // return different values depending on the return type and the architecture.    QualType RetTy = ME->getType(); -   +    ASTContext &Ctx = C.getASTContext();    CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); @@ -216,7 +305,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,    if (CanRetTy != Ctx.VoidTy &&        C.getPredecessor()->getParentMap().isConsumedExpr(ME)) {      // Compute: sizeof(void *) and sizeof(return type) -    const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);     +    const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);      const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);      if (voidPtrSize < returnTypeSize && @@ -247,6 +336,6 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,      C.GenerateNode(state->BindExpr(ME, V));      return;    } -   +    C.addTransition(state);  } diff --git a/clang/test/Analysis/uninit-vals-ps-region.c b/clang/test/Analysis/uninit-vals-ps-region.c index 44f506efadb..aabf75c3f40 100644 --- a/clang/test/Analysis/uninit-vals-ps-region.c +++ b/clang/test/Analysis/uninit-vals-ps-region.c @@ -45,3 +45,9 @@ void test_uninit_neg() {    test_unit_aux2(v2.x + v1.y); // no-warning  } +extern void test_uninit_struct_arg_aux(struct TestUninit arg); +void test_uninit_struct_arg() { +  struct TestUninit x; +  test_uninit_struct_arg_aux(x); // expected-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'x')}} +} +  | 

