martong created this revision. martong added reviewers: steakhal, NoQ, vsavchenko, Szelethus. Herald added subscribers: ASDenysPetrov, Charusso, gamesh411, dkrupp, donat.nagy, mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun, whisperity. martong requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
It makes sense to track rvalue expressions in the case of special concrete integer values. The most notable special value is zero (later we may find other values). By tracking the origin of 0, we can provide a better explanation for users e.g. in case of division by 0 warnings. When the divisor is a product of a multiplication then now we can show which operand (or both) was (were) zero and why. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D99344 Files: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp clang/test/Analysis/division-by-zero-track-zero.c clang/test/Analysis/division-by-zero-track-zero.cpp clang/test/Analysis/nullptr.cpp
Index: clang/test/Analysis/nullptr.cpp =================================================================== --- clang/test/Analysis/nullptr.cpp +++ 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}} } Index: clang/test/Analysis/division-by-zero-track-zero.cpp =================================================================== --- /dev/null +++ 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 Index: clang/test/Analysis/division-by-zero-track-zero.c =================================================================== --- /dev/null +++ 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}} +} Index: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1924,6 +1924,62 @@ return N; } +void bugreporter::trackRValueExpression(const ExplodedNode *InputNode, + const Expr *E, + PathSensitiveBugReport &report, + bugreporter::TrackingKind TKind, + bool EnableNullFPSuppression) { + if (!E->isRValue()) + return; + const ExplodedNode *RVNode = findNodeForExpression(InputNode, E); + if (!RVNode) + return; + ProgramStateRef RVState = RVNode->getState(); + SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext()); + auto *BO = dyn_cast<BinaryOperator>(E); + if (!BO) + return; + ProgramStateManager &Mgr = RVState->getStateManager(); + SValBuilder &SVB = Mgr.getSValBuilder(); + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + + auto IsZero = [&BVF](const Optional<nonloc::ConcreteInt> &CI) { + if (!CI) + return false; + const llvm::APSInt &Zero = BVF.getValue(0, BVF.getContext().IntTy); + // Do the comparison with the same canonical ASPInt. + return CI->getValue() == BVF.Convert(CI->getValue(), Zero); + }; + + auto CI = V.getAs<nonloc::ConcreteInt>(); + 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<nonloc::ConcreteInt>())) { + trackExpressionValue(InputNode, BO->getLHS(), report, TKind, + EnableNullFPSuppression); + } + if (IsZero(RHSV.getAs<nonloc::ConcreteInt>())) { + trackExpressionValue(InputNode, BO->getRHS(), report, TKind, + EnableNullFPSuppression); + } + // Track only the LHS of divisions. + } else { + if (IsZero(LHSV.getAs<nonloc::ConcreteInt>())) { + trackExpressionValue(InputNode, BO->getLHS(), report, TKind, + EnableNullFPSuppression); + } + } +} + bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, const Expr *E, PathSensitiveBugReport &report, @@ -2069,6 +2125,9 @@ loc::MemRegionVal(RegionRVal), /*assumption=*/false)); } } + + trackRValueExpression(LVNode, Inner, report, TKind, EnableNullFPSuppression); + return true; } 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 @@ -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
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits