diff options
-rw-r--r-- | include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h | 25 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h | 10 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporterVisitors.cpp | 55 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ProgramState.cpp | 10 | ||||
-rw-r--r-- | test/Analysis/diagnostics/deref-track-symbolic-region.c | 197 | ||||
-rw-r--r-- | test/Analysis/inlining/inline-defensive-checks.c | 99 | ||||
-rw-r--r-- | test/Analysis/inlining/path-notes.c | 1004 |
7 files changed, 530 insertions, 870 deletions
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h index bef4b30a35..c1b5594209 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h @@ -280,6 +280,31 @@ public: BugReport &BR); }; +class SuppressInlineDefensiveChecksVisitor +: public BugReporterVisitorImpl<SuppressInlineDefensiveChecksVisitor> +{ + // The symbolic value for which we are tracking constraints. + // This value is constrained to null in the end of path. + DefinedSVal V; + + // Track if we found the node where the constraint was first added. + bool IsSatisfied; + +public: + SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N); + + void Profile(llvm::FoldingSetNodeID &ID) const; + + /// Return the tag associated with this visitor. This tag will be used + /// to make all PathDiagnosticPieces created by this visitor. + static const char *getTag(); + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR); +}; + namespace bugreporter { /// Attempts to add visitors to trace a null or undefined value back to its diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index eab248bac2..798090735b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -173,8 +173,8 @@ public: ProgramStateRef assume(DefinedOrUnknownSVal cond, bool assumption) const; /// This method assumes both "true" and "false" for 'cond', and - /// returns both corresponding states. It's shorthand for doing - /// 'assume' twice. + /// returns both corresponding states. It's shorthand for doing + /// 'assume' twice. std::pair<ProgramStateRef , ProgramStateRef > assume(DefinedOrUnknownSVal cond) const; @@ -182,7 +182,11 @@ public: DefinedOrUnknownSVal upperBound, bool assumption, QualType IndexType = QualType()) const; - + + /// \brief Check if the given SVal is constrained to zero or is a zero + /// constant. + ConditionTruthVal isNull(SVal V) const; + /// Utility method for getting regions. const VarRegion* getRegion(const VarDecl *D, const LocationContext *LC) const; diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index bb20c0d3c6..b4ba2d4789 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -27,6 +27,8 @@ using namespace clang; using namespace ento; +using llvm::FoldingSetNodeID; + //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// @@ -663,6 +665,49 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, return NULL; } +SuppressInlineDefensiveChecksVisitor:: +SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) + : V(Value), IsSatisfied(false) { + + assert(N->getState()->isNull(V).isConstrainedTrue() && + "The visitor only tracks the cases where V is constrained to 0"); +} + +void SuppressInlineDefensiveChecksVisitor::Profile(FoldingSetNodeID &ID) const { + static int id = 0; + ID.AddPointer(&id); + ID.Add(V); +} + +const char *SuppressInlineDefensiveChecksVisitor::getTag() { + return "IDCVisitor"; +} + +PathDiagnosticPiece * +SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + if (IsSatisfied) + return 0; + + // Check if in the previous state it was feasible for this value + // to *not* be null. + if (PrevN->getState()->assume(V, true)) { + IsSatisfied = true; + + // TODO: Investigate if missing the transition point, where V + // is non-null in N could lead to false negatives. + + // Check if this is inline defensive checks. + const LocationContext *CurLC = PrevN->getLocationContext(); + const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext(); + if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) + BR.markInvalid("Suppress IDC", CurLC); + } + return 0; +} + bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &report, bool IsArg) { if (!S || !N) @@ -772,8 +817,16 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = - new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); + new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); report.addVisitor(ConstraintTracker); + + // Add visitor, which will suppress inline defensive checks. + if (N->getState()->isNull(V).isConstrainedTrue()) { + BugReporterVisitor *IDCSuppressor = + new SuppressInlineDefensiveChecksVisitor(V.castAs<DefinedSVal>(), + N); + report.addVisitor(IDCSuppressor); + } } if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 400569e49a..64205f8d99 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -324,6 +324,16 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption); } +ConditionTruthVal ProgramState::isNull(SVal V) const { + if (V.isZeroConstant()) + return true; + + SymbolRef Sym = V.getAsSymbol(); + if (!Sym) + return false; + return getStateManager().ConstraintMgr->isNull(this, Sym); +} + ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) { ProgramState State(this, EnvMgr.getInitialEnvironment(), diff --git a/test/Analysis/diagnostics/deref-track-symbolic-region.c b/test/Analysis/diagnostics/deref-track-symbolic-region.c index e2ec8fc5f3..94774dd61d 100644 --- a/test/Analysis/diagnostics/deref-track-symbolic-region.c +++ b/test/Analysis/diagnostics/deref-track-symbolic-region.c @@ -9,22 +9,19 @@ struct S { int *foo(); -void inlined(struct S *s, int m) { - if (s->x) +void test(struct S syz, int *pp) { + int m = 0; + syz.x = foo(); // expected-note{{Value assigned to 'syz.x'}} + + struct S *ps = &syz; + if (ps->x) //expected-note@-1{{Taking false branch}} //expected-note@-2{{Assuming pointer value is null}} m++; -} -void test(struct S syz, int *pp) { - int m = 0; - syz.x = foo(); // expected-note{{Value assigned to 'syz.x'}} - inlined(&syz, m); - // expected-note@-1{{Calling 'inlined'}} - // expected-note@-2{{Returning from 'inlined'}} m += *syz.x; // expected-warning{{Dereference of null pointer (loaded from field 'x')}} - // expected-note@-1{{Dereference of null pointer (loaded from field 'x')}} + // expected-note@-1{{Dereference of null pointer (loaded from field 'x')}} } // CHECK: <key>diagnostics</key> @@ -40,12 +37,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>start</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>21</integer> +// CHECK-NEXT: <key>line</key><integer>13</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>21</integer> +// CHECK-NEXT: <key>line</key><integer>13</integer> // CHECK-NEXT: <key>col</key><integer>5</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -53,12 +50,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>end</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> +// CHECK-NEXT: <key>line</key><integer>14</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> +// CHECK-NEXT: <key>line</key><integer>14</integer> // CHECK-NEXT: <key>col</key><integer>5</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -70,7 +67,7 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>kind</key><string>event</string> // CHECK-NEXT: <key>location</key> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> +// CHECK-NEXT: <key>line</key><integer>14</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -78,12 +75,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <array> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> +// CHECK-NEXT: <key>line</key><integer>14</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> +// CHECK-NEXT: <key>line</key><integer>14</integer> // CHECK-NEXT: <key>col</key><integer>15</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -103,12 +100,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>start</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> +// CHECK-NEXT: <key>line</key><integer>14</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> +// CHECK-NEXT: <key>line</key><integer>14</integer> // CHECK-NEXT: <key>col</key><integer>5</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -116,89 +113,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>end</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Calling 'inlined'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Calling 'inlined'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>12</integer> -// CHECK-NEXT: <key>col</key><integer>1</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Entered call from 'test'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Entered call from 'test'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>12</integer> -// CHECK-NEXT: <key>col</key><integer>1</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>12</integer> -// CHECK-NEXT: <key>col</key><integer>4</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> // CHECK-NEXT: <key>col</key><integer>4</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -214,12 +134,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>start</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> // CHECK-NEXT: <key>col</key><integer>4</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -227,13 +147,13 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>end</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> // CHECK-NEXT: <key>col</key><integer>7</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>8</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: </array> @@ -244,7 +164,7 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>kind</key><string>event</string> // CHECK-NEXT: <key>location</key> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> // CHECK-NEXT: <key>col</key><integer>7</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -252,53 +172,24 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <array> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> // CHECK-NEXT: <key>col</key><integer>7</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>13</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>11</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>1</integer> +// CHECK-NEXT: <key>depth</key><integer>0</integer> // CHECK-NEXT: <key>extended_message</key> // CHECK-NEXT: <string>Assuming pointer value is null</string> // CHECK-NEXT: <key>message</key> // CHECK-NEXT: <string>Assuming pointer value is null</string> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Returning from 'inlined'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Returning from 'inlined'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> // CHECK-NEXT: <key>kind</key><string>control</string> // CHECK-NEXT: <key>edges</key> // CHECK-NEXT: <array> @@ -306,25 +197,25 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>start</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>7</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>23</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>8</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>end</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -340,12 +231,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>start</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -353,12 +244,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>end</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>8</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>8</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -370,7 +261,7 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>kind</key><string>event</string> // CHECK-NEXT: <key>location</key> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>8</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -378,12 +269,12 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <array> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>13</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>13</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> @@ -401,12 +292,14 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: <key>type</key><string>Dereference of null pointer</string> // CHECK-NEXT: <key>issue_context_kind</key><string>function</string> // CHECK-NEXT: <key>issue_context</key><string>test</string> -// CHECK-NEXT: <key>issue_hash</key><string>6</string> +// CHECK-NEXT: <key>issue_hash</key><string>11</string> // CHECK-NEXT: <key>location</key> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> +// CHECK-NEXT: <key>line</key><integer>23</integer> // CHECK-NEXT: <key>col</key><integer>8</integer> // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: </dict> // CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </plist> diff --git a/test/Analysis/inlining/inline-defensive-checks.c b/test/Analysis/inlining/inline-defensive-checks.c new file mode 100644 index 0000000000..a91d6a3daf --- /dev/null +++ b/test/Analysis/inlining/inline-defensive-checks.c @@ -0,0 +1,99 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify %s + +// Perform inline defensive checks. +void idc(int *p) { + if (p) + ; +} + +int test01(int *p) { + if (p) + ; + return *p; // expected-warning {{Dereference of null pointer}} +} + +int test02(int *p, int *x) { + if (p) + ; + idc(p); + if (x) + ; + return *p; // expected-warning {{Dereference of null pointer}} +} + +int test03(int *p, int *x) { + idc(p); + if (p) + ; + return *p; // False negative +} + +int deref04(int *p) { + return *p; // expected-warning {{Dereference of null pointer}} +} + +int test04(int *p) { + if (p) + ; + idc(p); + return deref04(p); +} + +int test11(int *q, int *x) { + int *p = q; + if (q) + ; + if (x) + ; + return *p; // expected-warning{{Dereference of null pointer}} +} + +int test12(int *q) { + int *p = q; + idc(q); + return *p; +} + +int test13(int *q) { + int *p = q; + idc(p); + return *p; +} + +int test21(int *q, int *x) { + if (q) + ; + if (x) + ; + int *p = q; + return *p; // expected-warning{{Dereference of null pointer}} +} + +int test22(int *q, int *x) { + idc(q); + if (x) + ; + int *p = q; + return *p; +} + +int test23(int *q, int *x) { + idc(q); + if (x) + ; + int *p = q; + if (!p) + ; + return *p; // False negative +} + +void use(char *p) { + if (!p) + return; + p[0] = 'a'; +} + +void test24(char *buffer) { + use(buffer); + buffer[1] = 'b'; +} diff --git a/test/Analysis/inlining/path-notes.c b/test/Analysis/inlining/path-notes.c index a2d603c00a..b128aabf7e 100644 --- a/test/Analysis/inlining/path-notes.c +++ b/test/Analysis/inlining/path-notes.c @@ -15,20 +15,12 @@ void testZero(int *a) { // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}} } - -void check(int *p) { - if (p) { - // expected-note@-1 + {{Assuming 'p' is null}} +void testCheck(int *a) { + if (a) { + // expected-note@-1 + {{Assuming 'a' is null}} // expected-note@-2 + {{Taking false branch}} - return; + ; } - return; -} - -void testCheck(int *a) { - check(a); - // expected-note@-1 {{Calling 'check'}} - // expected-note@-2 {{Returning from 'check'}} *a = 1; // expected-warning{{Dereference of null pointer}} // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}} } @@ -39,9 +31,11 @@ int *getPointer(); void testInitCheck() { int *a = getPointer(); // expected-note@-1 {{'a' initialized here}} - check(a); - // expected-note@-1 {{Calling 'check'}} - // expected-note@-2 {{Returning from 'check'}} + if (a) { + // expected-note@-1 + {{Assuming 'a' is null}} + // expected-note@-2 + {{Taking false branch}} + ; + } *a = 1; // expected-warning{{Dereference of null pointer}} // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}} } @@ -49,9 +43,11 @@ void testInitCheck() { void testStoreCheck(int *a) { a = getPointer(); // expected-note@-1 {{Value assigned to 'a'}} - check(a); - // expected-note@-1 {{Calling 'check'}} - // expected-note@-2 {{Returning from 'check'}} + if (a) { + // expected-note@-1 + {{Assuming 'a' is null}} + // expected-note@-2 + {{Taking false branch}} + ; + } *a = 1; // expected-warning{{Dereference of null pointer}} // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}} } @@ -332,49 +328,6 @@ void testUseOfNullPointer() { // CHECK-NEXT: <key>path</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>29</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>29</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>29</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Calling 'check'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Calling 'check'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>1</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Entered call from 'testCheck'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Entered call from 'testCheck'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> // CHECK-NEXT: <key>kind</key><string>control</string> // CHECK-NEXT: <key>edges</key> // CHECK-NEXT: <array> @@ -383,45 +336,11 @@ void testUseOfNullPointer() { // CHECK-NEXT: <array> // CHECK-NEXT: <dict> // CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>1</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>4</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>20</integer> // CHECK-NEXT: <key>col</key><integer>3</integer> // CHECK-NEXT: <key>file</key><integer>0</integer&g |