Skip to content

Commit 111de0e

Browse files
committed
[analyzer] Improve constraint inferring on concrete div/mod
1. Reduce constraint on modulo with small concrete range when assigned; 2. Improve constraint inferring on modulo over concrete value; 3. Improve constraint inferring on division over concrete value. Fixes #54377
1 parent 4a030f5 commit 111de0e

File tree

4 files changed

+427
-0
lines changed

4 files changed

+427
-0
lines changed

clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,9 @@ class SymbolicRangeInferrer
13381338
RangeSet VisitBinaryOperator(RangeSet LHS, BinaryOperator::Opcode Op,
13391339
RangeSet RHS, QualType T);
13401340

1341+
RangeSet handleConcreteModulo(Range LHS, llvm::APSInt Modulo, QualType T);
1342+
RangeSet handleRangeModulo(Range LHS, Range RHS, QualType T);
1343+
13411344
//===----------------------------------------------------------------------===//
13421345
// Ranges and operators
13431346
//===----------------------------------------------------------------------===//
@@ -1771,6 +1774,14 @@ template <>
17711774
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
17721775
Range RHS,
17731776
QualType T) {
1777+
if (const llvm::APSInt *ModOrNull = RHS.getConcreteValue())
1778+
return handleConcreteModulo(LHS, *ModOrNull, T);
1779+
1780+
return handleRangeModulo(LHS, RHS, T);
1781+
}
1782+
1783+
RangeSet SymbolicRangeInferrer::handleRangeModulo(Range LHS, Range RHS,
1784+
QualType T) {
17741785
llvm::APSInt Zero = ValueFactory.getAPSIntType(T).getZeroValue();
17751786

17761787
Range ConservativeRange = getSymmetricalRange(RHS, T);
@@ -1824,6 +1835,101 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
18241835
return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
18251836
}
18261837

1838+
RangeSet SymbolicRangeInferrer::handleConcreteModulo(Range LHS,
1839+
llvm::APSInt Modulo,
1840+
QualType T) {
1841+
APSIntType ResultType = ValueFactory.getAPSIntType(T);
1842+
llvm::APSInt Zero = ResultType.getZeroValue();
1843+
llvm::APSInt One = ResultType.getValue(1);
1844+
1845+
if (Modulo == Zero)
1846+
return RangeFactory.getEmptySet();
1847+
1848+
// Quick path, this also avoids unary '-' overflow on MinValue modulo.
1849+
if (Modulo == ValueFactory.getMinValue(T) ||
1850+
Modulo == ValueFactory.getMaxValue(T))
1851+
return RangeFactory.getRangeSet(LHS);
1852+
1853+
// Normalize to positive modulo since a % N == a % -N
1854+
if (Modulo < 0)
1855+
Modulo = -Modulo;
1856+
1857+
auto ComputeModuloN = [&](llvm::APSInt From, llvm::APSInt To,
1858+
llvm::APSInt N) -> RangeSet {
1859+
assert(N > Zero && "Positive N expected!");
1860+
bool NonNegative = From >= Zero;
1861+
assert(NonNegative == (To >= Zero) && "Signedness mismatch!");
1862+
1863+
if (From > To)
1864+
return RangeFactory.getEmptySet();
1865+
1866+
llvm::APSInt N1 = N - One;
1867+
1868+
// spans [0, N - 1] if NonNegative or [-(N - 1), 0] otherwise.
1869+
if ((To - From) / N > Zero)
1870+
return {RangeFactory, ValueFactory.getValue(NonNegative ? Zero : -N1),
1871+
ValueFactory.getValue(NonNegative ? N1 : Zero)};
1872+
1873+
llvm::APSInt Min = From % N;
1874+
llvm::APSInt Max = To % N;
1875+
1876+
if (Min < Max) // [Min, Max]
1877+
return {RangeFactory, ValueFactory.getValue(Min),
1878+
ValueFactory.getValue(Max)};
1879+
1880+
// [0, Max] U [Min, N - 1] if NonNegative, or
1881+
// [-(N - 1), Max] U [Min, 0] otherwise.
1882+
const llvm::APSInt &Min1 = ValueFactory.getValue(NonNegative ? Zero : -N1);
1883+
const llvm::APSInt &Max1 = ValueFactory.getValue(Max);
1884+
RangeSet RS1{RangeFactory, Min1, Max1};
1885+
1886+
const llvm::APSInt &Min2 = ValueFactory.getValue(Min);
1887+
const llvm::APSInt &Max2 = ValueFactory.getValue(NonNegative ? N1 : Zero);
1888+
RangeSet RS2{RangeFactory, Min2, Max2};
1889+
1890+
return RangeFactory.unite(RS1, RS2);
1891+
};
1892+
1893+
if (!LHS.Includes(Zero))
1894+
return ComputeModuloN(LHS.From(), LHS.To(), Modulo);
1895+
1896+
// split over [From, -1] U [0, To] for easy handling.
1897+
return RangeFactory.unite(ComputeModuloN(LHS.From(), -One, Modulo),
1898+
ComputeModuloN(Zero, LHS.To(), Modulo));
1899+
}
1900+
1901+
template <>
1902+
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Div>(Range LHS,
1903+
Range RHS,
1904+
QualType T) {
1905+
const llvm::APSInt *DividerOrNull = RHS.getConcreteValue();
1906+
if (!DividerOrNull)
1907+
return infer(T);
1908+
1909+
const llvm::APSInt &D = *DividerOrNull;
1910+
1911+
APSIntType ResultType = ValueFactory.getAPSIntType(T);
1912+
llvm::APSInt Zero = ResultType.getZeroValue();
1913+
llvm::APSInt One = ResultType.getValue(1);
1914+
if (D == Zero)
1915+
return RangeFactory.getEmptySet();
1916+
1917+
if (D == -One) { // might overflow
1918+
if (LHS.To().isMinSignedValue())
1919+
return {RangeFactory, LHS.To(), LHS.To()};
1920+
else if (LHS.From().isMinSignedValue()) {
1921+
const llvm::APSInt &From = ValueFactory.getValue((LHS.From() + One) / D);
1922+
const llvm::APSInt &To = ValueFactory.getValue(LHS.To() / D);
1923+
RangeSet RS{RangeFactory, To, From};
1924+
return RangeFactory.unite(RS, LHS.From());
1925+
}
1926+
}
1927+
1928+
const llvm::APSInt &From = ValueFactory.getValue(LHS.From() / D);
1929+
const llvm::APSInt &To = ValueFactory.getValue(LHS.To() / D);
1930+
return {RangeFactory, D < Zero ? To : From, D < Zero ? From : To};
1931+
}
1932+
18271933
RangeSet SymbolicRangeInferrer::VisitBinaryOperator(RangeSet LHS,
18281934
BinaryOperator::Opcode Op,
18291935
RangeSet RHS, QualType T) {
@@ -1842,6 +1948,8 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator(RangeSet LHS,
18421948
return VisitBinaryOperator<BO_And>(LHS, RHS, T);
18431949
case BO_Rem:
18441950
return VisitBinaryOperator<BO_Rem>(LHS, RHS, T);
1951+
case BO_Div:
1952+
return VisitBinaryOperator<BO_Div>(LHS, RHS, T);
18451953
default:
18461954
return infer(T);
18471955
}
@@ -2073,13 +2181,62 @@ class ConstraintAssignor : public ConstraintAssignorBase<ConstraintAssignor> {
20732181
if (Sym->getOpcode() != BO_Rem)
20742182
return true;
20752183
// a % b != 0 implies that a != 0.
2184+
// Z3 verification:
2185+
// (declare-const a Int)
2186+
// (declare-const m Int)
2187+
// (assert (not (= m 0)))
2188+
// (assert (not (= (mod a m) 0)))
2189+
// (assert (= a 0))
2190+
// (check-sat)
2191+
// ; unsat
20762192
if (!Constraint.containsZero()) {
20772193
SVal SymSVal = Builder.makeSymbolVal(Sym->getLHS());
20782194
if (auto NonLocSymSVal = SymSVal.getAs<nonloc::SymbolVal>()) {
20792195
State = State->assume(*NonLocSymSVal, true);
20802196
if (!State)
20812197
return false;
20822198
}
2199+
} else if (const auto *SIE = dyn_cast<SymIntExpr>(Sym);
2200+
SIE && Constraint.encodesFalseRange()) {
2201+
// a % m == 0 && a in [x, y] && y - x < m implies that
2202+
// a = (y < 0 ? x : y) / m * m which is a 'ConcreteInt'
2203+
// where x and m are 'ConcreteInt'.
2204+
//
2205+
// Z3 verification:
2206+
// (declare-const a Int)
2207+
// (declare-const m Int)
2208+
// (declare-const x Int)
2209+
// (declare-const y Int)
2210+
// (assert (= (mod a m) 0))
2211+
// (assert (< (- y x) m))
2212+
// (assert (and (>= a x) (<= a y)))
2213+
// (assert (not (= a (* (div y m) m))))
2214+
// (check-sat)
2215+
// ; unsat
2216+
BasicValueFactory &ValueFactory = RangeFactory.getValueFactory();
2217+
APSIntType ResultType = ValueFactory.getAPSIntType(SIE->getType());
2218+
llvm::APSInt N = SIE->getRHS();
2219+
if (N < 0)
2220+
N = -N;
2221+
N = ResultType.convert(N);
2222+
2223+
SymbolRef Sym = SIE->getLHS();
2224+
RangeSet RS = SymbolicRangeInferrer::inferRange(RangeFactory, State, Sym);
2225+
if (RS.size() == 1) {
2226+
Range R = *RS.begin();
2227+
llvm::APSInt Distance = ResultType.convert(R.To()).extend(64) -
2228+
ResultType.convert(R.From()).extend(64);
2229+
if (Distance < 0) // overflows
2230+
return true;
2231+
2232+
if (Distance < N.extend(64)) {
2233+
const llvm::APSInt &Point = ValueFactory.getValue(
2234+
(ResultType.convert(R.To() > 0 ? R.To() : R.From())) / N * N);
2235+
State = assign(Sym, {RangeFactory, Point, Point});
2236+
if (!State)
2237+
return false;
2238+
}
2239+
}
20832240
}
20842241
return true;
20852242
}

clang/test/Analysis/constraint-assignor.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
void clang_analyzer_warnIfReached(void);
77
void clang_analyzer_eval(int);
8+
void clang_analyzer_dump(int);
89

910
void rem_constant_rhs_ne_zero(int x, int y) {
1011
if (x % 3 == 0) // x % 3 != 0 -> x != 0
@@ -82,3 +83,110 @@ void remainder_with_adjustment_of_composit_lhs(int x, int y) {
8283
clang_analyzer_eval(x + y != -1); // expected-warning{{TRUE}}
8384
(void)(x * y); // keep the constraints alive.
8485
}
86+
87+
void remainder_infeasible_positive_range(int x) {
88+
if (x <= 2 || x >= 5)
89+
return;
90+
if (x % 5 != 0)
91+
return;
92+
clang_analyzer_warnIfReached(); // no-warning
93+
(void)x; // keep the constraints alive.
94+
}
95+
96+
void remainder_infeasible_negative_range(int x) {
97+
if (x <= -14 || x >= -1)
98+
return;
99+
if (x % 15 != 0)
100+
return;
101+
clang_analyzer_warnIfReached(); // no-warning
102+
(void)x; // keep the constraints alive.
103+
}
104+
105+
void remainder_within_modulo_positive_range_unsigned_1(unsigned x) {
106+
if (x <= 2 || x > 6)
107+
return;
108+
if (x % 5 != 0)
109+
return;
110+
clang_analyzer_dump(x); // expected-warning{{5 S32b}}
111+
(void)x; // keep the constraints alive.
112+
}
113+
114+
void remainder_within_modulo_positive_range_unsigned_2(unsigned char x) {
115+
if (x < 252 || x > 254)
116+
return;
117+
if (x % 5 != 0)
118+
return;
119+
clang_analyzer_dump(x); // no-warning
120+
(void)x; // keep the constraints alive.
121+
}
122+
123+
void remainder_within_modulo_positive_range_unsigned_3(unsigned x) {
124+
if (x < 4294967289 || x > 4294967294)
125+
return;
126+
if (x % 10 != 0)
127+
return;
128+
clang_analyzer_eval(x == 4294967290); // expected-warning{{TRUE}}
129+
(void)x; // keep the constraints alive.
130+
}
131+
132+
void remainder_within_modulo_positive_range(int x) {
133+
if (x <= 2 || x > 6)
134+
return;
135+
if (x % 5 != 0)
136+
return;
137+
clang_analyzer_dump(x); // expected-warning{{5 S32b}}
138+
(void)x; // keep the constraints alive.
139+
}
140+
141+
void remainder_within_modulo_range_spans_zero(int x) {
142+
if (x <= -2 || x > 2)
143+
return;
144+
if (x % 5 != 0)
145+
return;
146+
clang_analyzer_dump(x); // expected-warning{{0 S32b}}
147+
(void)x; // keep the constraints alive.
148+
}
149+
150+
void remainder_within_modulo_negative_range(int x) {
151+
if (x <= -7 || x > -2)
152+
return;
153+
if (x % 5 != 0)
154+
return;
155+
clang_analyzer_dump(x); // expected-warning{{-5 S32b}}
156+
(void)x; // keep the constraints alive.
157+
}
158+
159+
void remainder_within_modulo_range_neg_mod(int x) {
160+
if (x <= 2 || x > 6)
161+
return;
162+
if (x % -5 != 0)
163+
return;
164+
clang_analyzer_dump(x); // expected-warning{{5 S32b}}
165+
(void)x; // keep the constraints alive.
166+
}
167+
168+
#define LONG_MAX 0x7fffffffffffffffLL
169+
#define LONG_MIN (-LONG_MAX - 1LL)
170+
171+
void remainder_within_modulo_distance_overflow(long long x) {
172+
if (x < LONG_MIN + 1 || x > LONG_MAX - 1)
173+
return;
174+
175+
if (x % 10 != 0)
176+
return;
177+
clang_analyzer_dump(x); // expected-warning{{reg_$0<long long x>}}
178+
(void)x; // keep the constraints alive.
179+
}
180+
181+
#define CHAR_MAX 0x7f
182+
#define CHAR_MIN (-CHAR_MAX - 1)
183+
184+
void remainder_within_modulo_not_overflow(char x) {
185+
if (x < CHAR_MIN + 1 || x > CHAR_MAX - 1)
186+
return;
187+
188+
if (x % (CHAR_MAX * 2) != 0)
189+
return;
190+
clang_analyzer_dump(x); // expected-warning{{0 S32b}}
191+
(void)x; // keep the constraints alive.
192+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %clang_analyze_cc1 %s \
2+
// RUN: -analyzer-checker=core \
3+
// RUN: -analyzer-checker=debug.ExprInspection \
4+
// RUN: -verify
5+
6+
void clang_analyzer_eval(int);
7+
8+
void div_infer_positive_divider(int x) {
9+
if (x < 1 || x > 5)
10+
return;
11+
12+
int i = x / 2;
13+
clang_analyzer_eval(i >= 0 && i <= 2); // expected-warning{{TRUE}}
14+
}
15+
16+
void div_infer_negative_divider_positive_range(int x) {
17+
if (x < 1 || x > 2)
18+
return;
19+
20+
int i = x / -2;
21+
clang_analyzer_eval(i >= -1 && i <= 0); // expected-warning{{TRUE}}
22+
}
23+
24+
void div_infer_negative_divider_negative_range(int x) {
25+
if (x < -2 || x > 0)
26+
return;
27+
28+
int i = x / -2;
29+
clang_analyzer_eval(i >= 0 && i <= 1); // expected-warning{{TRUE}}
30+
}
31+
32+
void div_infer_positive_divider_positive_range(int x) {
33+
if (x < 0 || x > 5)
34+
return;
35+
36+
int i = x / 6;
37+
clang_analyzer_eval(i == 0); // expected-warning{{TRUE}}
38+
}
39+
40+
#define LONG_MAX 0x7fffffffffffffffLL
41+
#define LONG_MIN (-LONG_MAX - 1LL)
42+
43+
void div_infer_overflow_long(long long x) {
44+
if (x > LONG_MIN + 1)
45+
return;
46+
47+
// x in [LONG_MIN, LONG_MIN + 1]
48+
clang_analyzer_eval(x >= LONG_MIN && x <= LONG_MIN + 1); // expected-warning{{TRUE}}
49+
long long i = x / -1;
50+
clang_analyzer_eval(i == LONG_MIN || i == LONG_MAX); // expected-warning{{TRUE}}
51+
}
52+
53+
#define INT_MAX 0x7fffffff
54+
#define INT_MIN (-INT_MAX - 1)
55+
56+
void div_infer_overflow_int(int x) {
57+
if (x > INT_MIN + 1)
58+
return;
59+
60+
// x in [INT_MIN, INT_MIN + 1]
61+
clang_analyzer_eval(x >= INT_MIN && x <= INT_MIN + 1); // expected-warning{{TRUE}}
62+
int i = x / -1;
63+
clang_analyzer_eval(i == INT_MIN || i == INT_MAX); // expected-warning{{TRUE}}
64+
}

0 commit comments

Comments
 (0)