Skip to content

Commit 03cb601

Browse files
committed
IntegerOverflow: Add query for INT30-C
Adds a query for finding unsigned integer wraparound, based on the `InterestingBinaryOverflowingExpr` class.
1 parent 0ffa4b1 commit 03cb601

File tree

6 files changed

+134
-0
lines changed

6 files changed

+134
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# INT30-C: Ensure that unsigned integer operations do not wrap
2+
3+
This query implements the CERT-C rule INT30-C:
4+
5+
> Ensure that unsigned integer operations do not wrap
6+
## CERT
7+
8+
** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` **
9+
10+
## Implementation notes
11+
12+
None
13+
14+
## References
15+
16+
* CERT-C: [INT30-C: Ensure that unsigned integer operations do not wrap](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @id c/cert/unsigned-integer-operations-wrap-around
3+
* @name INT30-C: Ensure that unsigned integer operations do not wrap
4+
* @description Unsigned integer expressions do not strictly overflow, but instead wrap around in a
5+
* modular way. If the size of the type is not sufficient, this can happen
6+
* unexpectedly.
7+
* @kind problem
8+
* @precision high
9+
* @problem.severity error
10+
* @tags external/cert/id/int30-c
11+
* correctness
12+
* security
13+
* external/cert/obligation/rule
14+
*/
15+
16+
import cpp
17+
import codingstandards.c.cert
18+
import codingstandards.cpp.Overflow
19+
import semmle.code.cpp.controlflow.Guards
20+
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
21+
22+
/* TODO: review the table to restrict to only those operations that actually overflow */
23+
from InterestingBinaryOverflowingExpr bop
24+
where
25+
not isExcluded(bop, IntegerOverflowPackage::unsignedIntegerOperationsWrapAroundQuery()) and
26+
bop.getType().getUnderlyingType().(IntegralType).isUnsigned() and
27+
// Not within a guard condition
28+
not exists(GuardCondition gc | gc.getAChild*() = bop) and
29+
// Not guarded by a check, where the check is not an invalid overflow check
30+
not bop.getAGuardingGVN() = globalValueNumber(bop.getAChild*()) and
31+
// Is not checked after the operation
32+
not bop.hasPostCheck()
33+
select bop,
34+
"Binary expression ..." + bop.getOperator() + "... of type " + bop.getType().getUnderlyingType() +
35+
" may wrap."
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.c:4:3:4:9 | ... + ... | Binary expression ...+... of type unsigned int may wrap. |
2+
| test.c:48:3:48:9 | ... - ... | Binary expression ...-... of type unsigned int may wrap. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/INT30-C/UnsignedIntegerOperationsWrapAround.ql

c/cert/test/rules/INT30-C/test.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include <limits.h>
2+
3+
void test_add_simple(unsigned int i1, unsigned int i2) {
4+
i1 + i2; // NON_COMPLIANT - not bounds checked
5+
}
6+
7+
void test_add_precheck(unsigned int i1, unsigned int i2) {
8+
if (UINT_MAX - i1 < i2) {
9+
// handle error
10+
} else {
11+
i1 + i2; // COMPLIANT - bounds checked
12+
}
13+
}
14+
15+
void test_add_precheck_2(unsigned int i1, unsigned int i2) {
16+
if (i1 + i2 < i1) {
17+
// handle error
18+
} else {
19+
i1 + i2; // COMPLIANT - bounds checked
20+
}
21+
}
22+
23+
void test_add_postcheck(unsigned int i1, unsigned int i2) {
24+
unsigned int i3 = i1 + i2; // COMPLIANT - checked for overflow afterwards
25+
if (i3 < i1) {
26+
// handle error
27+
}
28+
}
29+
30+
void test_ex2(unsigned int i1, unsigned int i2) {
31+
unsigned int ci1 = 2;
32+
unsigned int ci2 = 3;
33+
ci1 + ci2; // COMPLIANT, compile time constants
34+
i1 + 0; // COMPLIANT
35+
i1 - 0; // COMPLIANT
36+
UINT_MAX - i1; // COMPLIANT - cannot be smaller than 0
37+
i1 * 1; // COMPLIANT
38+
if (0 <= i1 && i1 < 32) {
39+
UINT_MAX >> i1; // COMPLIANT
40+
}
41+
}
42+
43+
void test_ex3(unsigned int i1, unsigned int i2) {
44+
i1 << i2; // COMPLIANT - by EX3
45+
}
46+
47+
void test_sub_simple(unsigned int i1, unsigned int i2) {
48+
i1 - i2; // NON_COMPLIANT - not bounds checked
49+
}
50+
51+
void test_sub_precheck(unsigned int i1, unsigned int i2) {
52+
if (i1 < i2) {
53+
// handle error
54+
} else {
55+
i1 - i2; // COMPLIANT - bounds checked
56+
}
57+
}
58+
59+
void test_sub_postcheck(unsigned int i1, unsigned int i2) {
60+
unsigned int i3 = i1 - i2; // COMPLIANT - checked for wrap afterwards
61+
if (i3 > i1) {
62+
// handle error
63+
}
64+
}

cpp/common/src/codingstandards/cpp/Overflow.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ class InterestingBinaryOverflowingExpr extends BinaryArithmeticOperation {
4242
)
4343
}
4444

45+
predicate hasPostCheck() {
46+
exists(RelationalOperation ro |
47+
DataFlow::localExprFlow(this, ro.getLesserOperand()) and
48+
globalValueNumber(ro.getGreaterOperand()) = globalValueNumber(this.getAnOperand()) and
49+
this instanceof AddExpr and
50+
ro instanceof GuardCondition
51+
)
52+
or
53+
exists(RelationalOperation ro |
54+
DataFlow::localExprFlow(this, ro.getGreaterOperand()) and
55+
globalValueNumber(ro.getLesserOperand()) = globalValueNumber(this.getAnOperand()) and
56+
this instanceof SubExpr and
57+
ro instanceof GuardCondition
58+
)
59+
}
60+
4561
/**
4662
* Identifies a bad overflow check for this overflow expression.
4763
*/

0 commit comments

Comments
 (0)