diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -116,6 +116,13 @@ TrackingKind TKind = TrackingKind::Thorough, bool EnableNullFPSuppression = true); +/// Attempts to add visitors to track an RValue expression back to its point of +/// origin. Works similarly to trackExpressionValue, but accepts only RValues. +void trackRValueExpression(const ExplodedNode *N, const Expr *E, + PathSensitiveBugReport &R, + TrackingKind TKind = TrackingKind::Thorough, + bool EnableNullFPSuppression = true); + const Expr *getDerefExpr(const Stmt *S); } // namespace bugreporter diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1924,6 +1924,61 @@ return N; } +void bugreporter::trackRValueExpression(const ExplodedNode *InputNode, + const Expr *E, + PathSensitiveBugReport &report, + bugreporter::TrackingKind TKind, + bool EnableNullFPSuppression) { + assert(E->isRValue() && "The expression is not an rvalue!"); + const ExplodedNode *RVNode = findNodeForExpression(InputNode, E); + if (!RVNode) + return; + ProgramStateRef RVState = RVNode->getState(); + SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext()); + auto *BO = dyn_cast(E); + if (!BO) + return; + ProgramStateManager &Mgr = RVState->getStateManager(); + SValBuilder &SVB = Mgr.getSValBuilder(); + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + + auto IsZero = [&BVF](const Optional &CI) { + if (!CI) + return false; + const llvm::APSInt &Zero = BVF.getValue(0, BVF.getContext().IntTy); + // Do the comparison with the same canonical APSInt. + return CI->getValue() == BVF.Convert(CI->getValue(), Zero); + }; + + auto CI = V.getAs(); + if (!IsZero(CI)) + return; + + if (!BO->isMultiplicativeOp()) + return; + + SVal RHSV = RVState->getSVal(BO->getRHS(), RVNode->getLocationContext()); + SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext()); + + // Track both LHS and RHS of a multiplication. + if (BO->getOpcode() == BO_Mul || BO->getOpcode() == BO_MulAssign) { + if (IsZero(LHSV.getAs())) { + trackExpressionValue(InputNode, BO->getLHS(), report, TKind, + EnableNullFPSuppression); + } + if (IsZero(RHSV.getAs())) { + trackExpressionValue(InputNode, BO->getRHS(), report, TKind, + EnableNullFPSuppression); + } + // Track only the LHS of divisions. + } else { + if (IsZero(LHSV.getAs())) { + trackExpressionValue(InputNode, BO->getLHS(), report, TKind, + EnableNullFPSuppression); + } + } +} + bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, const Expr *E, PathSensitiveBugReport &report, @@ -2069,6 +2124,11 @@ loc::MemRegionVal(RegionRVal), /*assumption=*/false)); } } + + if (Inner->isRValue()) + trackRValueExpression(LVNode, Inner, report, TKind, + EnableNullFPSuppression); + return true; } diff --git a/clang/test/Analysis/division-by-zero-track-zero.c b/clang/test/Analysis/division-by-zero-track-zero.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/division-by-zero-track-zero.c @@ -0,0 +1,11 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core \ +// RUN: -analyzer-output=text \ +// RUN: -verify %s + +int track_mul_lhs_0(int x, int y) { + int p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = p0 * y; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} +} diff --git a/clang/test/Analysis/division-by-zero-track-zero.cpp b/clang/test/Analysis/division-by-zero-track-zero.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/division-by-zero-track-zero.cpp @@ -0,0 +1,98 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core \ +// RUN: -analyzer-output=text \ +// RUN: -verify %s + +namespace test_tracking_of_lhs_multiplier { + int f(int x, int y) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = p0 * y; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_of_lhs_multiplier + +namespace test_tracking_of_rhs_multiplier { + int f(int x, int y) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = y * p0; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_of_rhs_multiplier + +namespace test_tracking_of_nested_multiplier { + int f(int x, int y, int z) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = y*z*p0; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_of_nested_multiplier + +namespace test_tracking_through_multiple_stmts { + int f(int x, int y) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} + bool p1 = p0 ? 0 : 1; // expected-note {{'p0' is false}} \ + // expected-note {{'?' condition is false}} + bool p2 = 1 - p1; // expected-note {{'p2' initialized to 0}} + int div = p2 * y; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_through_multiple_stmts + +namespace test_tracking_both_lhs_and_rhs { + int f(int x, int y) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + bool p1 = y < 0; // expected-note {{Assuming 'y' is >= 0}} \ + // expected-note {{'p1' initialized to 0}} + int div = p0 * p1; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_both_lhs_and_rhs + +namespace test_tracking_of_multiplier_and_parens { + int f(int x, int y, int z) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = y*(z*p0); // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_of_multiplier_and_parens + +namespace test_tracking_of_divisible { + int f(int x, int y) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = p0 / y; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_of_divisible + +namespace test_tracking_of_modulo { + int f(int x, int y) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = p0 % y; // expected-note {{'div' initialized to 0}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_of_modulo + +namespace test_tracking_of_assignment { + int f(int x) { + bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \ + // expected-note {{'p0' initialized to 0}} + int div = 1; + div *= p0; // expected-note {{The value 0 is assigned to 'div'}} + return 1 / div; // expected-note {{Division by zero}} \ + // expected-warning {{Division by zero}} + } +} // namespace test_tracking_of_assignment diff --git a/clang/test/Analysis/nullptr.cpp b/clang/test/Analysis/nullptr.cpp --- a/clang/test/Analysis/nullptr.cpp +++ b/clang/test/Analysis/nullptr.cpp @@ -64,7 +64,7 @@ typedef __INTPTR_TYPE__ intptr_t; void zoo1multiply() { - char **p = 0; // FIXME-should-be-note:{{'p' initialized to a null pointer value}} + char **p = 0; // expected-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}} }