Author: dergachev Date: Wed Sep 27 02:50:45 2017 New Revision: 314290 URL: http://llvm.org/viewvc/llvm-project?rev=314290&view=rev Log: [analyzer] Match more patterns in bugreporter::getDerefExpr() API.
This function can now track null pointer through simple pointer arithmetic, such as '*&*(p + 2)' => 'p' and so on, displaying intermediate diagnostic pieces for the user to understand where the null pointer is coming from. Differential Revision: https://reviews.llvm.org/D37025 Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c cfe/trunk/test/Analysis/null-deref-path-notes.c cfe/trunk/test/Analysis/nullptr.cpp Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=314290&r1=314289&r2=314290&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Wed Sep 27 02:50:45 2017 @@ -66,12 +66,24 @@ const Expr *bugreporter::getDerefExpr(co break; } E = CE->getSubExpr(); - } else if (isa<BinaryOperator>(E)) { - // Probably more arithmetic can be pattern-matched here, - // but for now give up. - break; + } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E)) { + // Pointer arithmetic: '*(x + 2)' -> 'x') etc. + if (B->getType()->isPointerType()) { + if (B->getLHS()->getType()->isPointerType()) { + E = B->getLHS(); + } else if (B->getRHS()->getType()->isPointerType()) { + E = B->getRHS(); + } else { + break; + } + } else { + // Probably more arithmetic can be pattern-matched here, + // but for now give up. + break; + } } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(E)) { - if (U->getOpcode() == UO_Deref) { + if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf || + (U->isIncrementDecrementOp() && U->getType()->isPointerType())) { // Operators '*' and '&' don't actually mean anything. // We look at casts instead. E = U->getSubExpr(); Modified: cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c?rev=314290&r1=314289&r2=314290&view=diff ============================================================================== --- cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c (original) +++ cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c Wed Sep 27 02:50:45 2017 @@ -169,6 +169,18 @@ void idcTrackZeroValueThroughUnaryPointe *x = 7; // no-warning } +void idcTrackZeroValueThroughManyUnaryPointerOperatorsWithAssignment(struct S *s) { + idc(s); + int *x = &*&(s->f1); + *x = 7; // no-warning +} + +void idcTrackZeroValueThroughManyUnaryPointerOperatorsWithAssignmentAndUnaryIncrement(struct S *s) { + idc(s); + int *x = &*&((++s)->f1); + *x = 7; // no-warning +} + struct S2 { int a[1]; Modified: cfe/trunk/test/Analysis/null-deref-path-notes.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/null-deref-path-notes.c?rev=314290&r1=314289&r2=314290&view=diff ============================================================================== --- cfe/trunk/test/Analysis/null-deref-path-notes.c (original) +++ cfe/trunk/test/Analysis/null-deref-path-notes.c Wed Sep 27 02:50:45 2017 @@ -4,7 +4,7 @@ // of the null pointer for path notes. Apparently, not much actual tracking // needs to be done in this example. void pr34373() { - int *a = 0; + int *a = 0; // expected-note{{'a' initialized to a null pointer value}} (a + 0)[0]; // expected-warning{{Array access results in a null pointer dereference}} // expected-note@-1{{Array access results in a null pointer dereference}} } Modified: cfe/trunk/test/Analysis/nullptr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/nullptr.cpp?rev=314290&r1=314289&r2=314290&view=diff ============================================================================== --- cfe/trunk/test/Analysis/nullptr.cpp (original) +++ cfe/trunk/test/Analysis/nullptr.cpp Wed Sep 27 02:50:45 2017 @@ -1,11 +1,12 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -Wno-conversion-null -analyzer-checker=core,debug.ExprInspection -analyzer-store region -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -Wno-conversion-null -analyzer-checker=core,debug.ExprInspection -analyzer-store region -analyzer-output=text -verify %s void clang_analyzer_eval(int); // test to see if nullptr is detected as a null pointer void foo1(void) { - char *np = nullptr; + char *np = nullptr; // expected-note{{'np' initialized to a null pointer value}} *np = 0; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} } // check if comparing nullptr to nullptr is detected properly @@ -23,10 +24,11 @@ void foo3(void) { struct foo { int a, f; }; - char *np = nullptr; + char *np = nullptr; // expected-note{{'np' initialized to a null pointer value}} // casting a nullptr to anything should be caught eventually - int *ip = &(((struct foo *)np)->f); + int *ip = &(((struct foo *)np)->f); // expected-note{{'ip' initialized to a null pointer value}} *ip = 0; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} // should be error here too, but analysis gets stopped // *np = 0; } @@ -49,16 +51,31 @@ int pr10372(void *& x) { } void zoo1() { - char **p = 0; + char **p = 0; // expected-note{{'p' initialized to a null pointer value}} delete *(p + 0); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} +} + +void zoo1backwards() { + char **p = 0; // expected-note{{'p' initialized to a null pointer value}} + delete *(0 + p); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} +} + +typedef __INTPTR_TYPE__ intptr_t; +void zoo1multiply() { + char **p = 0; // FIXME-should-be-note:{{'p' initialized to a null pointer value}} + delete *((char **)((intptr_t)p * 2)); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} } void zoo2() { int **a = 0; - int **b = 0; + int **b = 0; // expected-note{{'b' initialized to a null pointer value}} asm ("nop" :"=r"(*a) :"0"(*b) // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} ); } @@ -70,17 +87,19 @@ int exprWithCleanups() { int a; }; - int *x = 0; + int *x = 0; // expected-note{{'x' initialized to a null pointer value}} return S(*x).a; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} } int materializeTempExpr() { - int *n = 0; + int *n = 0; // expected-note{{'n' initialized to a null pointer value}} struct S { int a; S(int i): a(i) {} }; const S &s = S(*n); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} return s.a; } @@ -98,6 +117,7 @@ struct X { void invokeF(X* x) { x->f(); // expected-warning{{Called C++ object pointer is null}} + // expected-note@-1{{Called C++ object pointer is null}} } struct Type { @@ -105,26 +125,41 @@ struct Type { }; void shouldNotCrash() { - decltype(nullptr) p; - if (getSymbol()) - invokeF(p); // expected-warning{{1st function call argument is an uninit}} - if (getSymbol()) - invokeF(nullptr); - if (getSymbol()) { - X *x = Type().x; + decltype(nullptr) p; // expected-note{{'p' declared without an initial value}} + if (getSymbol()) // expected-note {{Assuming the condition is false}} + // expected-note@-1{{Taking false branch}} + // expected-note@-2{{Assuming the condition is false}} + // expected-note@-3{{Taking false branch}} + // expected-note@-4{{Assuming the condition is true}} + // expected-note@-5{{Taking true branch}} + invokeF(p); // expected-warning{{1st function call argument is an uninitialized value}} + // expected-note@-1{{1st function call argument is an uninitialized value}} + if (getSymbol()) // expected-note {{Assuming the condition is false}} + // expected-note@-1{{Taking false branch}} + // expected-note@-2{{Assuming the condition is true}} + // expected-note@-3{{Taking true branch}} + invokeF(nullptr); // expected-note {{Calling 'invokeF'}} + // expected-note@-1{{Passing null pointer value via 1st parameter 'x'}} + if (getSymbol()) { // expected-note {{Assuming the condition is true}} + // expected-note@-1{{Taking true branch}} + X *x = Type().x; // expected-note{{'x' initialized to a null pointer value}} x->f(); // expected-warning{{Called C++ object pointer is null}} + // expected-note@-1{{Called C++ object pointer is null}} } } void f(decltype(nullptr) p) { int *q = nullptr; clang_analyzer_eval(p == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} clang_analyzer_eval(q == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} } decltype(nullptr) returnsNullPtrType(); void fromReturnType() { ((X *)returnsNullPtrType())->f(); // expected-warning{{Called C++ object pointer is null}} + // expected-note@-1{{Called C++ object pointer is null}} } #define AS_ATTRIBUTE __attribute__((address_space(256))) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits