Skip to content

Commit 2241d7b

Browse files
authored
Merge pull request github#5616 from geoffw0/unsigneddiff2
C++: Improve cpp/unsigned-difference-expression-compared-zero
2 parents 10e76ff + 75edcf0 commit 2241d7b

File tree

4 files changed

+290
-28
lines changed

4 files changed

+290
-28
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The 'Unsigned difference expression compared to zero' (cpp/unsigned-difference-expression-compared-zero) query has been improved to produce fewer false positive results.

cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,60 @@
1212

1313
import cpp
1414
import semmle.code.cpp.commons.Exclusions
15-
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
1615
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
16+
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
1717
import semmle.code.cpp.controlflow.Guards
18+
import semmle.code.cpp.dataflow.DataFlow
1819

19-
/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */
20+
/**
21+
* Holds if `sub` is guarded by a condition which ensures that
22+
* `left >= right`.
23+
*/
2024
pragma[noinline]
2125
predicate isGuarded(SubExpr sub, Expr left, Expr right) {
22-
exists(GuardCondition guard |
23-
guard.controls(sub.getBasicBlock(), true) and
24-
guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false)
26+
exists(GuardCondition guard, int k |
27+
guard.controls(sub.getBasicBlock(), _) and
28+
guard.ensuresLt(left, right, k, sub.getBasicBlock(), false) and
29+
k >= 0
2530
)
2631
}
2732

28-
/** Holds if `sub` will never be negative. */
29-
predicate nonNegative(SubExpr sub) {
30-
not exprMightOverflowNegatively(sub.getFullyConverted())
33+
/**
34+
* Holds if `n` is known or suspected to be less than or equal to
35+
* `sub.getLeftOperand()`.
36+
*/
37+
predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) {
38+
n.asExpr() = sub.getLeftOperand()
39+
or
40+
exists(DataFlow::Node other |
41+
// dataflow
42+
exprIsSubLeftOrLess(sub, other) and
43+
(
44+
DataFlow::localFlowStep(n, other) or
45+
DataFlow::localFlowStep(other, n)
46+
)
47+
)
48+
or
49+
exists(DataFlow::Node other |
50+
// guard constraining `sub`
51+
exprIsSubLeftOrLess(sub, other) and
52+
isGuarded(sub, other.asExpr(), n.asExpr()) // other >= n
53+
)
54+
or
55+
exists(DataFlow::Node other, float p, float q |
56+
// linear access of `other`
57+
exprIsSubLeftOrLess(sub, other) and
58+
linearAccess(n.asExpr(), other.asExpr(), p, q) and // n = p * other + q
59+
p <= 1 and
60+
q <= 0
61+
)
3162
or
32-
// The subtraction is guarded by a check of the form `left >= right`.
33-
exists(GVN left, GVN right |
34-
// This is basically a poor man's version of a directional unbind operator.
35-
strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and
36-
strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and
37-
isGuarded(sub, left.getAnExpr(), right.getAnExpr())
63+
exists(DataFlow::Node other, float p, float q |
64+
// linear access of `n`
65+
exprIsSubLeftOrLess(sub, other) and
66+
linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * n + q
67+
p >= 1 and
68+
q >= 0
3869
)
3970
}
4071

@@ -45,5 +76,6 @@ where
4576
ro.getLesserOperand().getValue().toInt() = 0 and
4677
ro.getGreaterOperand() = sub and
4778
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and
48-
not nonNegative(sub)
79+
exprMightOverflowNegatively(sub.getFullyConverted()) and // generally catches false positives involving constants
80+
not exprIsSubLeftOrLess(sub, DataFlow::exprNode(sub.getRightOperand())) // generally catches false positives where there's a relation between the left and right operands
4981
select ro, "Unsigned subtraction can never be negative."
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
| test.cpp:6:5:6:13 | ... > ... | Unsigned subtraction can never be negative. |
22
| test.cpp:10:8:10:24 | ... > ... | Unsigned subtraction can never be negative. |
3-
| test.cpp:15:9:15:25 | ... > ... | Unsigned subtraction can never be negative. |
4-
| test.cpp:32:12:32:20 | ... > ... | Unsigned subtraction can never be negative. |
5-
| test.cpp:39:12:39:20 | ... > ... | Unsigned subtraction can never be negative. |
6-
| test.cpp:47:5:47:13 | ... > ... | Unsigned subtraction can never be negative. |
7-
| test.cpp:55:5:55:13 | ... > ... | Unsigned subtraction can never be negative. |
83
| test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. |
9-
| test.cpp:69:5:69:13 | ... > ... | Unsigned subtraction can never be negative. |
104
| test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. |
5+
| test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. |
6+
| test.cpp:128:6:128:14 | ... > ... | Unsigned subtraction can never be negative. |
7+
| test.cpp:137:6:137:14 | ... > ... | Unsigned subtraction can never be negative. |
8+
| test.cpp:146:7:146:15 | ... > ... | Unsigned subtraction can never be negative. |
9+
| test.cpp:152:7:152:15 | ... > ... | Unsigned subtraction can never be negative. |
10+
| test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. |
11+
| test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. |
12+
| test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. |
13+
| test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. |
14+
| test.cpp:276:11:276:19 | ... > ... | Unsigned subtraction can never be negative. |
15+
| test.cpp:288:10:288:18 | ... > ... | Unsigned subtraction can never be negative. |

cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp

Lines changed: 230 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ void test(unsigned x, unsigned y, bool unknown) {
1212
}
1313

1414
if(total <= limit) {
15-
while(limit - total > 0) { // GOOD [FALSE POSITIVE]
15+
while(limit - total > 0) { // GOOD
1616
total += getAnInt();
1717
if(total > limit) break;
1818
}
@@ -29,30 +29,30 @@ void test(unsigned x, unsigned y, bool unknown) {
2929
} else {
3030
y = x;
3131
}
32-
bool b1 = x - y > 0; // GOOD [FALSE POSITIVE]
32+
bool b1 = x - y > 0; // GOOD
3333

3434
x = getAnInt();
3535
y = getAnInt();
3636
if(y > x) {
3737
y = x - 1;
3838
}
39-
bool b2 = x - y > 0; // GOOD [FALSE POSITIVE]
39+
bool b2 = x - y > 0; // GOOD
4040

4141
int N = getAnInt();
4242
y = x;
4343
while(cond()) {
4444
if(unknown) { y--; }
4545
}
4646

47-
if(x - y > 0) { } // GOOD [FALSE POSITIVE]
47+
if(x - y > 0) { } // GOOD
4848

4949
x = y;
5050
while(cond()) {
5151
if(unknown) break;
5252
y--;
5353
}
5454

55-
if(x - y > 0) { } // GOOD [FALSE POSITIVE]
55+
if(x - y > 0) { } // GOOD
5656

5757
y = 0;
5858
for(int i = 0; i < x; ++i) {
@@ -66,12 +66,235 @@ void test(unsigned x, unsigned y, bool unknown) {
6666
if(unknown) { x++; }
6767
}
6868

69-
if(x - y > 0) { } // GOOD [FALSE POSITIVE]
69+
if(x - y > 0) { } // GOOD
7070

7171
int n = getAnInt();
7272
if (n > x - y) { n = x - y; }
7373
if (n > 0) {
7474
y += n; // NOTE: `n` is at most `x - y` at this point.
7575
if (x - y > 0) {} // GOOD [FALSE POSITIVE]
7676
}
77-
}
77+
}
78+
79+
void test2() {
80+
unsigned int a = getAnInt();
81+
unsigned int b = a;
82+
83+
if (a - b > 0) { // GOOD (as a = b)
84+
// ...
85+
}
86+
}
87+
88+
void test3() {
89+
unsigned int a = getAnInt();
90+
unsigned int b = a - 1;
91+
92+
if (a - b > 0) { // GOOD (as a >= b)
93+
// ...
94+
}
95+
}
96+
97+
void test4() {
98+
unsigned int a = getAnInt();
99+
unsigned int b = a + 1;
100+
101+
if (a - b > 0) { // BAD
102+
// ...
103+
}
104+
}
105+
106+
void test5() {
107+
unsigned int b = getAnInt();
108+
unsigned int a = b;
109+
110+
if (a - b > 0) { // GOOD (as a = b)
111+
// ...
112+
}
113+
}
114+
115+
void test6() {
116+
unsigned int b = getAnInt();
117+
unsigned int a = b + 1;
118+
119+
if (a - b > 0) { // GOOD (as a >= b)
120+
// ...
121+
}
122+
}
123+
124+
void test7() {
125+
unsigned int b = getAnInt();
126+
unsigned int a = b - 1;
127+
128+
if (a - b > 0) { // BAD
129+
// ...
130+
}
131+
}
132+
133+
void test8() {
134+
unsigned int a = getAnInt();
135+
unsigned int b = getAnInt();
136+
137+
if (a - b > 0) { // BAD
138+
// ...
139+
}
140+
141+
if (a >= b) { // GOOD
142+
if (a - b > 0) { // GOOD (as a >= b)
143+
// ...
144+
}
145+
} else {
146+
if (a - b > 0) { // BAD
147+
// ...
148+
}
149+
}
150+
151+
if (b >= a) { // GOOD
152+
if (a - b > 0) { // BAD
153+
// ...
154+
}
155+
} else {
156+
if (a - b > 0) { // GOOD (as a > b)
157+
// ...
158+
}
159+
}
160+
161+
while (a >= b) { // GOOD
162+
if (a - b > 0) { // GOOD (as a >= b)
163+
// ...
164+
}
165+
}
166+
167+
if (a < b) return;
168+
169+
if (a - b > 0) { // GOOD (as a >= b)
170+
// ...
171+
}
172+
}
173+
174+
void test9() {
175+
unsigned int a = getAnInt();
176+
unsigned int b = getAnInt();
177+
178+
if (a < b) {
179+
b = 0;
180+
}
181+
182+
if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE]
183+
// ...
184+
}
185+
}
186+
187+
void test10() {
188+
unsigned int a = getAnInt();
189+
unsigned int b = getAnInt();
190+
191+
if (a < b) {
192+
a = b;
193+
}
194+
195+
if (a - b > 0) { // GOOD (as a >= b)
196+
// ...
197+
}
198+
}
199+
200+
void test11() {
201+
unsigned int a = getAnInt();
202+
unsigned int b = getAnInt();
203+
204+
if (a < b) return;
205+
206+
b = getAnInt();
207+
208+
if (a - b > 0) { // BAD
209+
// ...
210+
}
211+
}
212+
213+
void test12() {
214+
unsigned int a = getAnInt();
215+
unsigned int b = getAnInt();
216+
unsigned int c;
217+
218+
if ((b <= c) && (c <= a)) {
219+
if (a - b > 0) { // GOOD (as b <= a)
220+
// ...
221+
}
222+
}
223+
224+
if (b <= c) {
225+
if (c <= a) {
226+
if (a - b > 0) { // GOOD (as b <= a)
227+
// ...
228+
}
229+
}
230+
}
231+
}
232+
233+
int test13() {
234+
unsigned int a = getAnInt();
235+
unsigned int b = getAnInt();
236+
237+
if (b != 0) {
238+
return 0;
239+
} // b = 0
240+
241+
return (a - b > 0); // GOOD (as b = 0)
242+
}
243+
244+
int test14() {
245+
unsigned int a = getAnInt();
246+
unsigned int b = getAnInt();
247+
248+
if (!b) {
249+
return 0;
250+
} // b != 0
251+
252+
return (a - b > 0); // BAD
253+
}
254+
255+
struct Numbers
256+
{
257+
unsigned int a, b;
258+
};
259+
260+
int test15(Numbers *n) {
261+
262+
if (!n) {
263+
return 0;
264+
}
265+
266+
return (n->a - n->b > 0); // BAD
267+
}
268+
269+
int test16() {
270+
unsigned int a = getAnInt();
271+
unsigned int b = getAnInt();
272+
273+
if (!b) {
274+
return 0;
275+
} else {
276+
return (a - b > 0); // BAD
277+
}
278+
}
279+
280+
int test17() {
281+
unsigned int a = getAnInt();
282+
unsigned int b = getAnInt();
283+
284+
if (b == 0) {
285+
return 0;
286+
} // b != 0
287+
288+
return (a - b > 0); // BAD
289+
}
290+
291+
int test18() {
292+
unsigned int a = getAnInt();
293+
unsigned int b = getAnInt();
294+
295+
if (b) {
296+
return 0;
297+
} // b == 0
298+
299+
return (a - b > 0); // GOOD (as b = 0)
300+
}

0 commit comments

Comments
 (0)