Index: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -153,6 +153,9 @@ /// \sa removeInvalidation llvm::SmallSet Invalidations; + /// Conditions we're already tracking. + llvm::SmallPtrSet TrackedConditions; + private: // Used internally by BugReporter. Symbols &getInterestingSymbols(); @@ -349,6 +352,10 @@ visitor_iterator visitor_begin() { return Callbacks.begin(); } visitor_iterator visitor_end() { return Callbacks.end(); } + bool addTrackedCondition(const Expr *Cond) { + return TrackedConditions.insert(Cond).second; + } + /// Profile to identify equivalent bug reports for error report coalescing. /// Reports are uniqued to ensure that we do not emit multiple diagnostics /// for each bug. Index: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H +#include "clang/Analysis/Analyses/Dominators.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" @@ -159,6 +160,35 @@ static const Expr *getNilReceiver(const Stmt *S, const ExplodedNode *N); }; +/// Tracks the expressions that are a control dependency of the node that was +/// supplied to the constructor. +/// For example: +/// +/// cond = 1; +/// if (cond) +/// 10 / 0; +/// +/// An error is emitted at line 3. This visitor realizes that the branch +/// on line 2 is a control dependency of line 3, and tracks it's condition via +/// trackExpressionValue(). +class TrackControlDependencyCondBRVisitor final : public BugReporterVisitor { + const ExplodedNode *Origin; + CFGControlDependencyTree ControlDepTree; + llvm::SmallSet VisitedBlocks; + +public: + TrackControlDependencyCondBRVisitor(const ExplodedNode *O); + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int x = 0; + ID.AddPointer(&x); + } + + std::shared_ptr VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; +}; + /// Visitor that tries to report interesting diagnostics from conditions. class ConditionBRVisitor final : public BugReporterVisitor { // FIXME: constexpr initialization isn't supported by MSVC2013. Index: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1668,6 +1668,9 @@ if (!LVNode) return false; + report.addVisitor(llvm::make_unique( + InputNode)); + ProgramStateRef LVState = LVNode->getState(); // The message send could be nil due to the receiver being nil. @@ -1831,6 +1834,73 @@ return std::make_shared(L, OS.str()); } +//===----------------------------------------------------------------------===// +// Implementation of TrackControlDependencyCondBRVisitor. +//===----------------------------------------------------------------------===// + +TrackControlDependencyCondBRVisitor::TrackControlDependencyCondBRVisitor( + const ExplodedNode *O) + : Origin(O), ControlDepTree(&O->getCFG()) {} + +static CFGBlock *GetRelevantBlock(const ExplodedNode *Node) { + if (auto SP = Node->getLocationAs()) { + const Stmt *S = SP->getStmt(); + assert(S); + + return const_cast(Node->getLocationContext() + ->getAnalysisDeclContext()->getCFGStmtMap()->getBlock(S)); + } + + return nullptr; +} + +static const Expr *getTerminatorCondition(CFGBlock *B) { + // If the terminator is a temporary dtor or a virtual base, etc, we can't + // retrieve a meaningful condition, bail out. + if (B->rbegin()->getKind() != CFGElement::Kind::Statement) + return nullptr; + + // This should be the condition of the terminator block. + const Stmt *S = B->rbegin()->castAs().getStmt(); + assert(S); + + if (const auto *Cond = dyn_cast(S)) + return Cond; + + assert(isa(S) && + "Only ObjCForCollectionStmt is known not to be a non-Expr terminator!"); + + // TODO: Return the collection. + return nullptr; +} + +std::shared_ptr +TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) { + // We can only reason about control dependencies within the same stack frame. + if (Origin->getStackFrame() != N->getStackFrame()) + return nullptr; + + CFGBlock *NB = GetRelevantBlock(N); + + // Skip if we already inspected this block. + if (!VisitedBlocks.insert(NB).second) + return nullptr; + + CFGBlock *OriginB = GetRelevantBlock(Origin); + if (!OriginB || !NB) + return nullptr; + + if (ControlDepTree.isControlDependency(OriginB, NB)) + if (const Expr *Condition = getTerminatorCondition(NB)) + if (BR.addTrackedCondition(Condition)) + bugreporter::trackExpressionValue( + N, Condition, BR, /*EnableNullFPSuppression=*/false); + + return nullptr; +} + //===----------------------------------------------------------------------===// // Implementation of FindLastStoreBRVisitor. //===----------------------------------------------------------------------===// Index: clang/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist =================================================================== --- clang/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist +++ clang/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist @@ -677,6 +677,69 @@ file0 + end + + + line88 + col20 + file0 + + + line88 + col23 + file0 + + + + + + + kindevent + location + + line88 + col20 + file0 + + ranges + + + + line88 + col20 + file0 + + + line88 + col23 + file0 + + + + depth0 + extended_message + Passing the value 1 via 1st parameter 'fail' + message + Passing the value 1 via 1st parameter 'fail' + + + kindcontrol + edges + + + start + + + line88 + col20 + file0 + + + line88 + col23 + file0 + + end Index: clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist =================================================================== --- clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist +++ clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist @@ -12057,6 +12057,35 @@ + + kindevent + location + + line459 + col5 + file0 + + ranges + + + + line459 + col5 + file0 + + + line459 + col13 + file0 + + + + depth0 + extended_message + The value 0 is assigned to 'first' + message + The value 0 is assigned to 'first' + kindcontrol edges Index: clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist =================================================================== --- clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist +++ clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist @@ -1883,6 +1883,35 @@ path + + kindevent + location + + line439 + col3 + file0 + + ranges + + + + line439 + col3 + file0 + + + line439 + col16 + file0 + + + + depth0 + extended_message + 'date' initialized here + message + 'date' initialized here + kindcontrol edges @@ -9276,6 +9305,69 @@ file0 + end + + + line731 + col3 + file0 + + + line731 + col10 + file0 + + + + + + + kindevent + location + + line731 + col3 + file0 + + ranges + + + + line731 + col3 + file0 + + + line731 + col16 + file0 + + + + depth0 + extended_message + 'name' initialized here + message + 'name' initialized here + + + kindcontrol + edges + + + start + + + line731 + col3 + file0 + + + line731 + col10 + file0 + + end Index: clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist =================================================================== --- clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist +++ clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist @@ -1883,6 +1883,35 @@ path + + kindevent + location + + line439 + col3 + file0 + + ranges + + + + line439 + col3 + file0 + + + line439 + col16 + file0 + + + + depth0 + extended_message + 'date' initialized here + message + 'date' initialized here + kindcontrol edges @@ -9276,6 +9305,69 @@ file0 + end + + + line731 + col3 + file0 + + + line731 + col10 + file0 + + + + + + + kindevent + location + + line731 + col3 + file0 + + ranges + + + + line731 + col3 + file0 + + + line731 + col16 + file0 + + + + depth0 + extended_message + 'name' initialized here + message + 'name' initialized here + + + kindcontrol + edges + + + start + + + line731 + col3 + file0 + + + line731 + col10 + file0 + + end Index: clang/test/Analysis/Inputs/expected-plists/unix-fns.c.plist =================================================================== --- clang/test/Analysis/Inputs/expected-plists/unix-fns.c.plist +++ clang/test/Analysis/Inputs/expected-plists/unix-fns.c.plist @@ -1593,6 +1593,35 @@ + + kindevent + location + + line223 + col24 + file0 + + ranges + + + + line223 + col24 + file0 + + + line227 + col3 + file0 + + + + depth0 + extended_message + 'q' + message + 'q' + kindevent location Index: clang/test/Analysis/diagnostics/Inputs/expected-plists/undef-value-param.m.plist =================================================================== --- clang/test/Analysis/diagnostics/Inputs/expected-plists/undef-value-param.m.plist +++ clang/test/Analysis/diagnostics/Inputs/expected-plists/undef-value-param.m.plist @@ -137,6 +137,69 @@ file0 + end + + + line55 + col5 + file0 + + + line55 + col21 + file0 + + + + + + + kindevent + location + + line55 + col29 + file0 + + ranges + + + + line55 + col29 + file0 + + + line55 + col53 + file0 + + + + depth1 + extended_message + Value assigned to 'err' + message + Value assigned to 'err' + + + kindcontrol + edges + + + start + + + line55 + col5 + file0 + + + line55 + col21 + file0 + + end Index: clang/test/Analysis/diagnostics/no-store-func-path-notes.m =================================================================== --- clang/test/Analysis/diagnostics/no-store-func-path-notes.m +++ clang/test/Analysis/diagnostics/no-store-func-path-notes.m @@ -16,17 +16,20 @@ return 0; } return 1; // expected-note{{Returning without writing to '*var'}} + // expected-note@-1{{Returning the value 1}} } @end int foo(I *i) { - int x; //expected-note{{'x' declared without an initial value}} - int out = [i initVar:&x param:0]; //expected-note{{Calling 'initVar:param:'}} - //expected-note@-1{{Returning from 'initVar:param:'}} - if (out) //expected-note{{'out' is 1}} - //expected-note@-1{{Taking true branch}} - return x; //expected-warning{{Undefined or garbage value returned to caller}} - //expected-note@-1{{Undefined or garbage value returned to caller}} + int x; // expected-note{{'x' declared without an initial value}} + int out = [i initVar:&x param:0]; // expected-note{{Calling 'initVar:param:'}} + // expected-note@-1{{Returning from 'initVar:param:'}} + // expected-note@-2{{Passing the value 0 via 2nd parameter 'param'}} + // expected-note@-3{{'out' initialized to 1}} + if (out) // expected-note{{Taking true branch}} + // expected-note@-1{{'out' is 1}} + return x; // expected-warning{{Undefined or garbage value returned to caller}} + // expected-note@-1{{Undefined or garbage value returned to caller}} return 0; } Index: clang/test/Analysis/diagnostics/undef-value-param.m =================================================================== --- clang/test/Analysis/diagnostics/undef-value-param.m +++ clang/test/Analysis/diagnostics/undef-value-param.m @@ -52,7 +52,7 @@ static void CreateRef(SCDynamicStoreRef *storeRef, unsigned x) { unsigned err = 0; - SCDynamicStoreRef ref = anotherCreateRef(&err, x); + SCDynamicStoreRef ref = anotherCreateRef(&err, x); // expected-note{{Value assigned to 'err'}} if (err) { //expected-note@-1{{Assuming 'err' is not equal to 0}} //expected-note@-2{{Taking true branch}} Index: clang/test/Analysis/track-control-dependency-conditions.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/track-control-dependency-conditions.cpp @@ -0,0 +1,87 @@ +// RUN: %clang_analyze_cc1 %s -verify \ +// RUN: -analyzer-output=text \ +// RUN: -analyzer-checker=core + +namespace example_1 { +int flag; +bool coin(); + +void foo() { + flag = coin(); // expected-note {{Value assigned to 'flag'}} +} + +void test() { + int *x = 0; // expected-note{{'x' initialized to a null pointer value}} + flag = 1; + + foo(); // TODO: Add nodes here about flag's value being invalidated. + if (flag) // expected-note {{Taking false branch}} + // expected-note@-1{{Assuming 'flag' is 0}} + x = new int; + + foo(); // expected-note {{Calling 'foo'}} + // expected-note@-1{{Returning from 'foo'}} + + if (flag) // expected-note {{Taking true branch}} + // expected-note@-1{{Assuming 'flag' is not equal to 0}} + *x = 5; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} +} +} // end of namespace example_1 + +namespace example_2 { +int flag; +bool coin(); + +void foo() { + flag = coin(); // expected-note {{Value assigned to 'flag'}} +} + +void test() { + int *x = 0; + flag = 1; + + foo(); // TODO: Add nodes here about flag's value being invalidated. + if (flag) // expected-note {{Taking false branch}} + // expected-note@-1{{Assuming 'flag' is 0}} + x = new int; + + x = 0; // expected-note{{Null pointer value stored to 'x'}} + + foo(); // expected-note {{Calling 'foo'}} + // expected-note@-1{{Returning from 'foo'}} + + if (flag) // expected-note {{Taking true branch}} + // expected-note@-1{{Assuming 'flag' is not equal to 0}} + *x = 5; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} +} +} // end of namespace example_2 + +namespace example_3 { +int flag; +bool coin(); + +void foo() { + // TODO: It makes no sense at all for bar to have been assigned here. + flag = coin(); // expected-note {{Value assigned to 'flag'}} + // expected-note@-1 {{Value assigned to 'bar'}} +} + +int bar; + +void test() { + int *x = 0; // expected-note{{'x' initialized to a null pointer value}} + flag = 1; + + foo(); // expected-note {{Calling 'foo'}} + // expected-note@-1{{Returning from 'foo'}} + + if (bar) // expected-note {{Taking true branch}} + // expected-note@-1{{Assuming 'bar' is not equal to 0}} + if (flag) // expected-note {{Taking true branch}} + // expected-note@-1{{Assuming 'flag' is not equal to 0}} + *x = 5; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} +} +} // end of namespace example_3