From 8c80061535d925df462e05aed212091ff7dcce6b Mon Sep 17 00:00:00 2001 From: Artem Dergachev Date: Wed, 27 Sep 2017 09:50:45 +0000 Subject: [PATCH] [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 llvm-svn: 314290 --- .../StaticAnalyzer/Core/BugReporterVisitors.cpp | 22 ++++++-- .../Analysis/inlining/inline-defensive-checks.c | 12 ++++ clang/test/Analysis/null-deref-path-notes.c | 2 +- clang/test/Analysis/nullptr.cpp | 65 +++++++++++++++++----- 4 files changed, 80 insertions(+), 21 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 716f23b..5f00b51 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -66,12 +66,24 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { break; } E = CE->getSubExpr(); - } else if (isa(E)) { - // Probably more arithmetic can be pattern-matched here, - // but for now give up. - break; + } else if (const BinaryOperator *B = dyn_cast(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(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(); diff --git a/clang/test/Analysis/inlining/inline-defensive-checks.c b/clang/test/Analysis/inlining/inline-defensive-checks.c index 010d3a7..9f211b5 100644 --- a/clang/test/Analysis/inlining/inline-defensive-checks.c +++ b/clang/test/Analysis/inlining/inline-defensive-checks.c @@ -169,6 +169,18 @@ void idcTrackZeroValueThroughUnaryPointerOperatorsWithAssignment(struct S *s) { *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]; diff --git a/clang/test/Analysis/null-deref-path-notes.c b/clang/test/Analysis/null-deref-path-notes.c index 7070bc4..8f9109b 100644 --- a/clang/test/Analysis/null-deref-path-notes.c +++ b/clang/test/Analysis/null-deref-path-notes.c @@ -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}} } diff --git a/clang/test/Analysis/nullptr.cpp b/clang/test/Analysis/nullptr.cpp index 229ad7b..b3e61c9 100644 --- a/clang/test/Analysis/nullptr.cpp +++ b/clang/test/Analysis/nullptr.cpp @@ -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))) -- 2.7.4