Skip to content

Commit 62db19b

Browse files
authored
Merge pull request #492 from geoffw0/offsetuse
Approved by dave-bartolomeo
2 parents 4e72a08 + 646bb01 commit 62db19b

File tree

5 files changed

+84
-6
lines changed

5 files changed

+84
-6
lines changed

change-notes/1.19/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
| **Query** | **Expected impact** | **Change** |
1717
|----------------------------|------------------------|------------------------------------------------------------------|
18+
| Array offset used before range check | More results and fewer false positive results | The query now recognizes array accesses in different positions within the expression. False positives where the range is checked before and after the array access have been fixed. |
1819
| Empty branch of conditional | Fewer false positive results | The query now recognizes commented blocks more reliably. |
1920
| Expression has no effect | Fewer false positive results | Expressions in template instantiations are now excluded from this query. |
2021
| Global could be static | Fewer false positive results | Variables with declarations in header files are now excluded from this query. |

cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* @kind problem
66
* @id cpp/offset-use-before-range-check
77
* @problem.severity warning
8+
* @precision medium
89
* @tags reliability
910
* security
1011
* external/cwe/cwe-120
@@ -13,10 +14,29 @@
1314

1415
import cpp
1516

16-
from Variable v, LogicalAndExpr andexpr, ArrayExpr access, LTExpr rangecheck
17-
where access.getArrayOffset() = v.getAnAccess()
18-
and andexpr.getLeftOperand().getAChild() = access
19-
and andexpr.getRightOperand() = rangecheck
20-
and rangecheck.getLeftOperand() = v.getAnAccess()
21-
and not access.isInMacroExpansion()
17+
predicate beforeArrayAccess(Variable v, ArrayExpr access, Expr before) {
18+
exists(LogicalAndExpr andexpr |
19+
access.getArrayOffset() = v.getAnAccess() and
20+
andexpr.getRightOperand().getAChild*() = access and
21+
andexpr.getLeftOperand() = before
22+
)
23+
}
24+
25+
predicate afterArrayAccess(Variable v, ArrayExpr access, Expr after) {
26+
exists(LogicalAndExpr andexpr |
27+
access.getArrayOffset() = v.getAnAccess() and
28+
andexpr.getLeftOperand().getAChild*() = access and
29+
andexpr.getRightOperand() = after
30+
)
31+
}
32+
33+
from Variable v, ArrayExpr access, LTExpr rangecheck
34+
where
35+
afterArrayAccess(v, access, rangecheck) and
36+
rangecheck.getLeftOperand() = v.getAnAccess() and
37+
not access.isInMacroExpansion() and
38+
not exists(LTExpr altcheck |
39+
beforeArrayAccess(v, access, altcheck) and
40+
altcheck.getLeftOperand() = v.getAnAccess()
41+
)
2242
select access, "This use of offset '" + v.getName() + "' should follow the $@.", rangecheck, "range check"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
| test.cpp:11:10:11:18 | access to array | This use of offset 'i' should follow the $@. | test.cpp:11:32:11:45 | ... < ... | range check |
2+
| test.cpp:15:7:15:15 | access to array | This use of offset 'i' should follow the $@. | test.cpp:15:29:15:42 | ... < ... | range check |
3+
| test.cpp:27:7:27:15 | access to array | This use of offset 'i' should follow the $@. | test.cpp:27:39:27:52 | ... < ... | range check |
4+
| test.cpp:39:8:39:16 | access to array | This use of offset 'i' should follow the $@. | test.cpp:39:30:39:47 | ... < ... | range check |
5+
| test.cpp:44:7:44:15 | access to array | This use of offset 'i' should follow the $@. | test.cpp:44:22:44:35 | ... < ... | range check |
6+
| test.cpp:47:7:47:15 | access to array | This use of offset 'i' should follow the $@. | test.cpp:47:33:47:46 | ... < ... | range check |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
void test(char *buffer, int bufferSize)
3+
{
4+
int i;
5+
6+
// skip whitespace
7+
i = 0;
8+
while ((i < bufferSize) && (buffer[i] == ' ')) { i++; } // GOOD
9+
10+
i = 0;
11+
while ((buffer[i] == ' ') && (i < bufferSize)) { i++; } // BAD
12+
13+
// check for 'x'
14+
if ((i < bufferSize) && (buffer[i] == 'x')) {} // GOOD
15+
if ((buffer[i] == 'x') && (i < bufferSize)) {} // BAD
16+
17+
if ((bufferSize > i) && (buffer[i] == 'x')) {} // GOOD
18+
if ((buffer[i] == 'x') && (bufferSize > i)) {} // BAD [NOT DETECTED]
19+
20+
if ((i <= bufferSize - 1) && (buffer[i] == 'x')) {} // GOOD
21+
if ((buffer[i] == 'x') && (i <= bufferSize - 1)) {} // BAD [NOT DETECTED]
22+
23+
if ((bufferSize >= i + 1) && (buffer[i] == 'x')) {} // GOOD
24+
if ((buffer[i] == 'x') && (bufferSize >= i + 1)) {} // BAD [NOT DETECTED]
25+
26+
if ((i < bufferSize) && (true) && (buffer[i] == 'x')) {} // GOOD
27+
if ((buffer[i] == 'x') && (true) && (i < bufferSize)) {} // BAD
28+
29+
if ((i < bufferSize - 1) && (buffer[i + 1] == 'x')) {} // GOOD
30+
if ((buffer[i + 1] == 'x') && (i < bufferSize - 1)) {} // BAD [NOT DETECTED]
31+
32+
if ((i < bufferSize) && (buffer[i] == 'x') && (i < bufferSize - 1)) {} // GOOD
33+
if ((i < bufferSize) && ((buffer[i] == 'x') && (i < bufferSize - 1))) {} // GOOD
34+
if ((i < bufferSize + 1) && (buffer[i] == 'x') && (i < bufferSize)) {} // BAD [NOT DETECTED]
35+
if ((i < bufferSize + 1) && ((buffer[i] == 'x') && (i < bufferSize))) {} // BAD [NOT DETECTED]
36+
37+
// look for 'ab'
38+
for (i = 0; i < bufferSize; i++) {
39+
if ((buffer[i] == 'a') && (i < bufferSize - 1) && (buffer[i + 1] == 'b')) // GOOD [FALSE POSITIVE]
40+
break;
41+
}
42+
43+
if ((i < bufferSize) && (buffer[i])) {} // GOOD
44+
if ((buffer[i]) && (i < bufferSize)) {} // BAD
45+
46+
if ((i < bufferSize) && (buffer[i] + 1 == 'x')) {} // GOOD
47+
if ((buffer[i] + 1 == 'x') && (i < bufferSize)) {} // BAD
48+
49+
if ((buffer != 0) && (i < bufferSize)) {} // GOOD
50+
}

0 commit comments

Comments
 (0)