Index: lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -17,22 +17,26 @@ using namespace ento; namespace { -class ExprInspectionChecker : public Checker< eval::Call > { +class ExprInspectionChecker : public Checker { mutable std::unique_ptr BT; void analyzerEval(const CallExpr *CE, CheckerContext &C) const; void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; + void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, CheckerContext &C) const; public: bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; }; } +REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, const void *) + bool ExprInspectionChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // These checks should have no effect on the surrounding environment @@ -42,7 +46,10 @@ .Case("clang_analyzer_checkInlined", &ExprInspectionChecker::analyzerCheckInlined) .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) - .Case("clang_analyzer_warnIfReached", &ExprInspectionChecker::analyzerWarnIfReached) + .Case("clang_analyzer_warnIfReached", + &ExprInspectionChecker::analyzerWarnIfReached) + .Case("clang_analyzer_warnOnDeadSymbol", + &ExprInspectionChecker::analyzerWarnOnDeadSymbol) .Default(nullptr); if (!Handler) @@ -137,6 +144,40 @@ llvm::make_unique(*BT, getArgumentValueString(CE, C), N)); } +void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) + return; + SVal Val = C.getSVal(CE->getArg(0)); + SymbolRef Sym = Val.getAsSymbol(); + if (!Sym) + return; + + ProgramStateRef State = C.getState(); + State = State->add(Sym); + C.addTransition(State); +} + +void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const MarkedSymbolsTy &Syms = State->get(); + for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { + SymbolRef Sym = static_cast(*I); + if (!SymReaper.isDead(Sym)) + continue; + + if (!BT) + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + C.emitReport(llvm::make_unique(*BT, "SYMBOL DEAD", N)); + } +} + void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, CheckerContext &C) const { LLVM_BUILTIN_TRAP; Index: lib/StaticAnalyzer/Core/Environment.cpp =================================================================== --- lib/StaticAnalyzer/Core/Environment.cpp +++ lib/StaticAnalyzer/Core/Environment.cpp @@ -171,8 +171,15 @@ EBMapRef = EBMapRef.add(BlkExpr, X); // If the block expr's value is a memory region, then mark that region. - if (Optional R = X.getAs()) - SymReaper.markLive(R->getRegion()); + if (Optional R = X.getAs()) { + const MemRegion *MR = R->getRegion(); + SymReaper.markLive(MR); + + // Mark the element index as live. + if (const ElementRegion *ER = dyn_cast(MR)) + if (SymbolRef Sym = ER->getIndex().getAsSymbol()) + SymReaper.markLive(Sym); + } // Mark all symbols in the block expr's value live. RSScaner.scan(X); Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -2237,8 +2237,20 @@ if (const SymbolicRegion *SymR = dyn_cast(baseR)) SymReaper.markLive(SymR->getSymbol()); - for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) { + + // Mark the index symbol of any ElementRegion key as live. + const BindingKey &K = I.getKey(); + auto subR = dyn_cast(K.getRegion()); + while (subR) { + if (auto elemR = dyn_cast(subR)) + if (SymbolRef Sym = elemR->getIndex().getAsSymbol()) + SymReaper.markLive(Sym); + subR = dyn_cast(subR->getSuperRegion()); + } + VisitBinding(I.getData()); + } } void removeDeadBindingsWorker::VisitBinding(SVal V) { @@ -2260,6 +2272,16 @@ if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); + // Element index of an element region is live. + if (const ElementRegion *ER = dyn_cast(R)) { + SVal Idx = ER->getIndex(); + for (SymExpr::symbol_iterator SI = Idx.symbol_begin(), + SE = Idx.symbol_end(); + SI != SE; ++SI) { + SymReaper.markLive(*SI); + } + } + // All regions captured by a block are also live. if (const BlockDataRegion *BR = dyn_cast(R)) { BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), Index: test/Analysis/return-ptr-range.cpp =================================================================== --- test/Analysis/return-ptr-range.cpp +++ test/Analysis/return-ptr-range.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.security.ReturnPtrRange -verify %s + +int arr[10]; +int *ptr; + +int conjure_index(); + +int *test_element_index_lifetime() { + do { + int x = conjure_index(); + ptr = arr + x; + if (x != 20) + return arr; // no-warning + } while (0); + return ptr; // expected-warning{{Returned pointer value points outside the original object (potential buffer overflow)}} +} + +int *test_element_index_lifetime_with_local_ptr() { + int *local_ptr; + do { + int x = conjure_index(); + local_ptr = arr + x; + if (x != 20) + return arr; // no-warning + } while (0); + return local_ptr; // expected-warning{{Returned pointer value points outside the original object (potential buffer overflow)}} +} Index: test/Analysis/symbol-reaper.c =================================================================== --- test/Analysis/symbol-reaper.c +++ test/Analysis/symbol-reaper.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.ExprInspection -verify %s + +void clang_analyzer_warnOnDeadSymbol(int); + +int arr[3]; + +int conjure_index(); + +void test_that_expr_inspection_works() { + do { + int x = conjure_index(); + clang_analyzer_warnOnDeadSymbol(x); + } while(0); // expected-warning{{SYMBOL DEAD}} +} + +void test_element_index_lifetime() { + do { + int x = conjure_index(); + clang_analyzer_warnOnDeadSymbol(x); + arr[x] = 1; + if (x) {} + } while(0); // no-warning +}