Skip to content

Commit efa7df1

Browse files
author
Gabor Marton
committed
[Analyzer] Track RValue expressions
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. Differential Revision: https://reviews.llvm.org/D99344
1 parent 1696b8a commit efa7df1

File tree

4 files changed

+153
-1
lines changed

4 files changed

+153
-1
lines changed

clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,6 +1924,44 @@ static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
19241924
return N;
19251925
}
19261926

1927+
/// Attempts to add visitors to track an RValue expression back to its point of
1928+
/// origin. Works similarly to trackExpressionValue, but accepts only RValues.
1929+
static void trackRValueExpression(const ExplodedNode *InputNode, const Expr *E,
1930+
PathSensitiveBugReport &report,
1931+
bugreporter::TrackingKind TKind,
1932+
bool EnableNullFPSuppression) {
1933+
assert(E->isRValue() && "The expression is not an rvalue!");
1934+
const ExplodedNode *RVNode = findNodeForExpression(InputNode, E);
1935+
if (!RVNode)
1936+
return;
1937+
ProgramStateRef RVState = RVNode->getState();
1938+
SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext());
1939+
const auto *BO = dyn_cast<BinaryOperator>(E);
1940+
if (!BO)
1941+
return;
1942+
if (!V.isZeroConstant())
1943+
return;
1944+
if (!BO->isMultiplicativeOp())
1945+
return;
1946+
1947+
SVal RHSV = RVState->getSVal(BO->getRHS(), RVNode->getLocationContext());
1948+
SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext());
1949+
1950+
// Track both LHS and RHS of a multiplication.
1951+
if (BO->getOpcode() == BO_Mul) {
1952+
if (LHSV.isZeroConstant())
1953+
trackExpressionValue(InputNode, BO->getLHS(), report, TKind,
1954+
EnableNullFPSuppression);
1955+
if (RHSV.isZeroConstant())
1956+
trackExpressionValue(InputNode, BO->getRHS(), report, TKind,
1957+
EnableNullFPSuppression);
1958+
} else { // Track only the LHS of a division or a modulo.
1959+
if (LHSV.isZeroConstant())
1960+
trackExpressionValue(InputNode, BO->getLHS(), report, TKind,
1961+
EnableNullFPSuppression);
1962+
}
1963+
}
1964+
19271965
bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
19281966
const Expr *E,
19291967
PathSensitiveBugReport &report,
@@ -2069,6 +2107,11 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
20692107
loc::MemRegionVal(RegionRVal), /*assumption=*/false));
20702108
}
20712109
}
2110+
2111+
if (Inner->isRValue())
2112+
trackRValueExpression(LVNode, Inner, report, TKind,
2113+
EnableNullFPSuppression);
2114+
20722115
return true;
20732116
}
20742117

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core \
2+
// RUN: -analyzer-output=text \
3+
// RUN: -verify %s
4+
5+
int track_mul_lhs_0(int x, int y) {
6+
int p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
7+
// expected-note {{'p0' initialized to 0}}
8+
int div = p0 * y; // expected-note {{'div' initialized to 0}}
9+
return 1 / div; // expected-note {{Division by zero}} \
10+
// expected-warning {{Division by zero}}
11+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core \
2+
// RUN: -analyzer-output=text \
3+
// RUN: -verify %s
4+
5+
namespace test_tracking_of_lhs_multiplier {
6+
int f(int x, int y) {
7+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
8+
// expected-note {{'p0' initialized to 0}}
9+
int div = p0 * y; // expected-note {{'div' initialized to 0}}
10+
return 1 / div; // expected-note {{Division by zero}} \
11+
// expected-warning {{Division by zero}}
12+
}
13+
} // namespace test_tracking_of_lhs_multiplier
14+
15+
namespace test_tracking_of_rhs_multiplier {
16+
int f(int x, int y) {
17+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
18+
// expected-note {{'p0' initialized to 0}}
19+
int div = y * p0; // expected-note {{'div' initialized to 0}}
20+
return 1 / div; // expected-note {{Division by zero}} \
21+
// expected-warning {{Division by zero}}
22+
}
23+
} // namespace test_tracking_of_rhs_multiplier
24+
25+
namespace test_tracking_of_nested_multiplier {
26+
int f(int x, int y, int z) {
27+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
28+
// expected-note {{'p0' initialized to 0}}
29+
int div = y*z*p0; // expected-note {{'div' initialized to 0}}
30+
return 1 / div; // expected-note {{Division by zero}} \
31+
// expected-warning {{Division by zero}}
32+
}
33+
} // namespace test_tracking_of_nested_multiplier
34+
35+
namespace test_tracking_through_multiple_stmts {
36+
int f(int x, int y) {
37+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}}
38+
bool p1 = p0 ? 0 : 1; // expected-note {{'p0' is false}} \
39+
// expected-note {{'?' condition is false}}
40+
bool p2 = 1 - p1; // expected-note {{'p2' initialized to 0}}
41+
int div = p2 * y; // expected-note {{'div' initialized to 0}}
42+
return 1 / div; // expected-note {{Division by zero}} \
43+
// expected-warning {{Division by zero}}
44+
}
45+
} // namespace test_tracking_through_multiple_stmts
46+
47+
namespace test_tracking_both_lhs_and_rhs {
48+
int f(int x, int y) {
49+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
50+
// expected-note {{'p0' initialized to 0}}
51+
bool p1 = y < 0; // expected-note {{Assuming 'y' is >= 0}} \
52+
// expected-note {{'p1' initialized to 0}}
53+
int div = p0 * p1; // expected-note {{'div' initialized to 0}}
54+
return 1 / div; // expected-note {{Division by zero}} \
55+
// expected-warning {{Division by zero}}
56+
}
57+
} // namespace test_tracking_both_lhs_and_rhs
58+
59+
namespace test_tracking_of_multiplier_and_parens {
60+
int f(int x, int y, int z) {
61+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
62+
// expected-note {{'p0' initialized to 0}}
63+
int div = y*(z*p0); // expected-note {{'div' initialized to 0}}
64+
return 1 / div; // expected-note {{Division by zero}} \
65+
// expected-warning {{Division by zero}}
66+
}
67+
} // namespace test_tracking_of_multiplier_and_parens
68+
69+
namespace test_tracking_of_divisible {
70+
int f(int x, int y) {
71+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
72+
// expected-note {{'p0' initialized to 0}}
73+
int div = p0 / y; // expected-note {{'div' initialized to 0}}
74+
return 1 / div; // expected-note {{Division by zero}} \
75+
// expected-warning {{Division by zero}}
76+
}
77+
} // namespace test_tracking_of_divisible
78+
79+
namespace test_tracking_of_modulo {
80+
int f(int x, int y) {
81+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
82+
// expected-note {{'p0' initialized to 0}}
83+
int div = p0 % y; // expected-note {{'div' initialized to 0}}
84+
return 1 / div; // expected-note {{Division by zero}} \
85+
// expected-warning {{Division by zero}}
86+
}
87+
} // namespace test_tracking_of_modulo
88+
89+
namespace test_tracking_of_assignment {
90+
int f(int x) {
91+
bool p0 = x < 0; // expected-note {{Assuming 'x' is >= 0}} \
92+
// expected-note {{'p0' initialized to 0}}
93+
int div = 1;
94+
div *= p0; // expected-note {{The value 0 is assigned to 'div'}}
95+
return 1 / div; // expected-note {{Division by zero}} \
96+
// expected-warning {{Division by zero}}
97+
}
98+
} // namespace test_tracking_of_assignment

clang/test/Analysis/nullptr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ void zoo1backwards() {
6464

6565
typedef __INTPTR_TYPE__ intptr_t;
6666
void zoo1multiply() {
67-
char **p = 0; // FIXME-should-be-note:{{'p' initialized to a null pointer value}}
67+
char **p = 0; // expected-note{{'p' initialized to a null pointer value}}
6868
delete *((char **)((intptr_t)p * 2)); // expected-warning{{Dereference of null pointer}}
6969
// expected-note@-1{{Dereference of null pointer}}
7070
}

0 commit comments

Comments
 (0)