Skip to content

Commit d3dc330

Browse files
committed
IntegerOverflow: Further restrictions
* Simplify the handling of div/rem exprs * Exclude rem from unsigned integer * Exclude left shift from signed integer overflow * Remove silly mulexpr "compliant" cases. * Fix div/rem checks to use the right max type * Remove getAGuardingGVN and unify under the hasValidPreCheck() case, but only for unsigned addition. * Add guards check for div and rem
1 parent bf37105 commit d3dc330

File tree

6 files changed

+79
-96
lines changed

6 files changed

+79
-96
lines changed

c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.ql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ where
2626
// Not within a guard condition
2727
not exists(GuardCondition gc | gc.getAChild*() = op) and
2828
// Not guarded by a check, where the check is not an invalid overflow check
29-
not op.getAGuardingGVN() = globalValueNumber(op.getAChild*()) and
29+
not op.hasValidPreCheck() and
3030
// Is not checked after the operation
3131
not op.hasValidPostCheck() and
3232
// Permitted by exception 3
3333
not op instanceof LShiftExpr and
3434
// Permitted by exception 2 - zero case is handled in separate query
35-
not op instanceof DivExpr
35+
not op instanceof DivExpr and
36+
not op instanceof RemExpr
3637
select op,
3738
"Operation " + op.getOperator() + " of type " + op.getType().getUnderlyingType() + " may wrap."

c/cert/src/rules/INT32-C/SignedIntegerOverflow.ql

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,12 @@ where
2626
op.getType().getUnderlyingType().(IntegralType).isSigned()
2727
or
2828
// The divide or rem expression on a signed integer
29-
(op instanceof DivExpr or op instanceof RemExpr) and
30-
op.(BinaryOperation).getLeftOperand().getType().getUnderlyingType().(IntegralType).isSigned()
31-
or
32-
// The assign divide or rem expression on a signed integer
33-
(op instanceof AssignDivExpr or op instanceof AssignRemExpr) and
34-
op.(AssignArithmeticOperation)
35-
.getLValue()
36-
.getType()
37-
.getUnderlyingType()
38-
.(IntegralType)
39-
.isSigned()
29+
op.(DivOrRemOperation).getDividend().getType().getUnderlyingType().(IntegralType).isSigned()
4030
) and
4131
// Not checked before the operation
4232
not op.hasValidPreCheck() and
43-
// Not guarded by a check, where the check is not an invalid overflow check
44-
not op.getAGuardingGVN() = globalValueNumber(op.getAChild*())
33+
// Covered by INT34-C
34+
not op instanceof LShiftExpr
4535
select op,
4636
"Operation " + op.getOperator() + " of type " + op.getType().getUnderlyingType() +
4737
" may overflow or underflow."

c/cert/test/rules/INT32-C/SignedIntegerOverflow.expected

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
| test.c:62:3:62:10 | ... -= ... | Operation -= of type signed int may overflow or underflow. |
1212
| test.c:69:3:69:8 | ... * ... | Operation * of type int may overflow or underflow. |
1313
| test.c:70:3:70:10 | ... *= ... | Operation *= of type signed int may overflow or underflow. |
14-
| test.c:117:3:117:9 | ... / ... | Operation / of type int may overflow or underflow. |
15-
| test.c:118:3:118:10 | ... /= ... | Operation /= of type signed int may overflow or underflow. |
16-
| test.c:140:3:140:9 | ... % ... | Operation % of type int may overflow or underflow. |
17-
| test.c:141:3:141:10 | ... %= ... | Operation %= of type signed int may overflow or underflow. |
18-
| test.c:163:3:163:10 | ... << ... | Operation << of type signed int may overflow or underflow. |
19-
| test.c:164:3:164:11 | ... <<= ... | Operation <<= of type signed int may overflow or underflow. |
20-
| test.c:183:3:183:5 | - ... | Operation - of type signed int may overflow or underflow. |
14+
| test.c:115:3:115:9 | ... / ... | Operation / of type int may overflow or underflow. |
15+
| test.c:116:3:116:10 | ... /= ... | Operation /= of type signed int may overflow or underflow. |
16+
| test.c:123:5:123:11 | ... / ... | Operation / of type int may overflow or underflow. |
17+
| test.c:124:5:124:12 | ... /= ... | Operation /= of type signed int may overflow or underflow. |
18+
| test.c:138:3:138:9 | ... % ... | Operation % of type int may overflow or underflow. |
19+
| test.c:139:3:139:10 | ... %= ... | Operation %= of type signed int may overflow or underflow. |
20+
| test.c:146:5:146:11 | ... % ... | Operation % of type int may overflow or underflow. |
21+
| test.c:147:5:147:12 | ... %= ... | Operation %= of type signed int may overflow or underflow. |
22+
| test.c:161:3:161:5 | - ... | Operation - of type signed int may overflow or underflow. |

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

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ void test_mul_precheck(signed int i1, signed int i2) {
7878
if (tmp > INT_MAX || tmp < INT_MIN) {
7979
// handle error
8080
} else {
81-
i1 *i2; // COMPLIANT - checked
8281
result = (signed int)tmp;
83-
i1 *= i2; // COMPLIANT - checked
8482
}
8583
}
8684

@@ -128,7 +126,7 @@ void test_simple_div_no_zero(signed int i1, signed int i2) {
128126
}
129127

130128
void test_div_precheck(signed int i1, signed int i2) {
131-
if ((i2 == 0) || ((i1 == LONG_MIN) && (i2 == -1))) {
129+
if ((i2 == 0) || ((i1 == INT_MIN) && (i2 == -1))) {
132130
/* Handle error */
133131
} else {
134132
i1 / i2; // COMPLIANT
@@ -151,34 +149,14 @@ void test_simple_rem_no_zero(signed int i1, signed int i2) {
151149
}
152150

153151
void test_rem_precheck(signed int i1, signed int i2) {
154-
if ((i2 == 0) || ((i1 == LONG_MIN) && (i2 == -1))) {
152+
if ((i2 == 0) || ((i1 == INT_MIN) && (i2 == -1))) {
155153
/* Handle error */
156154
} else {
157155
i1 % i2; // COMPLIANT
158156
i1 %= i2; // COMPLIANT
159157
}
160158
}
161159

162-
void test_simple_left_shift(signed int i1, signed int i2) {
163-
i1 << i2; // NON_COMPLIANT
164-
i1 <<= i2; // NON_COMPLIANT
165-
}
166-
167-
/* Returns the number of set bits */
168-
size_t popcount(uintmax_t num);
169-
170-
#define PRECISION(umax_value) popcount(umax_value)
171-
172-
void test_left_shift_precheck(signed int i1, signed int i2) {
173-
if ((i1 < 0) || (i2 < 0) || (i2 >= PRECISION(UINT_MAX)) ||
174-
(i1 > (INT_MAX >> i2))) {
175-
// handle error
176-
} else {
177-
i1 << i2; // COMPLIANT
178-
i1 <<= i2; // COMPLIANT
179-
}
180-
}
181-
182160
void test_simple_negate(signed int i1) {
183161
-i1; // NON_COMPLIANT
184162
}

cpp/autosar/src/rules/A4-7-1/IntegerExpressionLeadToDataLoss.ql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ where
2525
// Not within a guard condition
2626
not exists(GuardCondition gc | gc.getAChild*() = e) and
2727
// Not guarded by a check, where the check is not an invalid overflow check
28-
not e.getAGuardingGVN() = globalValueNumber(e.getAChild*()) and
28+
not e.hasValidPreCheck() and
2929
// Covered by `IntMultToLong.ql` instead
3030
not e instanceof MulExpr and
3131
// Not covered by this query - overflow/underflow in division is rare
32-
not e instanceof DivExpr
32+
not e instanceof DivExpr and
33+
not e instanceof RemExpr
3334
select e, "Binary expression ..." + e.getOperator() + "... may overflow."

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

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,13 @@ class InterestingOverflowingOperation extends Operation {
2121
exprMightOverflowPositively(this)
2222
or
2323
// Division and remainder are not handled by the library
24-
exists(Expr leftOperand, Expr rightOperand |
25-
(this instanceof DivExpr or this instanceof RemExpr) and
26-
leftOperand = this.(BinaryOperation).getLeftOperand() and
27-
rightOperand = this.(BinaryOperation).getRightOperand()
28-
or
29-
(this instanceof AssignDivExpr or this instanceof AssignRemExpr) and
30-
leftOperand = this.(AssignArithmeticOperation).getLValue() and
31-
rightOperand = this.(AssignArithmeticOperation).getRValue()
32-
|
24+
exists(DivOrRemOperation divOrRem | divOrRem = this |
3325
// The right hand side could be -1
34-
upperBound(rightOperand) >= -1.0 and
35-
lowerBound(rightOperand) <= -1.0 and
26+
upperBound(divOrRem.getDivisor()) >= -1.0 and
27+
lowerBound(divOrRem.getDivisor()) <= -1.0 and
3628
// The left hand side could be the smallest possible integer value
37-
lowerBound(leftOperand) <= typeLowerBound(leftOperand.getType().getUnderlyingType())
29+
lowerBound(divOrRem.getDividend()) <=
30+
typeLowerBound(divOrRem.getDividend().getType().getUnderlyingType())
3831
)
3932
) and
4033
// Multiplication is not covered by the standard range analysis library, so implement our own
@@ -46,18 +39,6 @@ class InterestingOverflowingOperation extends Operation {
4639
not this instanceof PointerArithmeticOperation
4740
}
4841

49-
/**
50-
* Get a `GVN` which guards this expression which may overflow.
51-
*/
52-
GVN getAGuardingGVN() {
53-
exists(GuardCondition gc, Expr e |
54-
not gc = getABadOverflowCheck() and
55-
TaintTracking::localTaint(DataFlow::exprNode(e), DataFlow::exprNode(gc.getAChild*())) and
56-
gc.controls(this.getBasicBlock(), _) and
57-
result = globalValueNumber(e)
58-
)
59-
}
60-
6142
/**
6243
* Holds if there is a correct validity check after this expression which may overflow.
6344
*/
@@ -70,6 +51,27 @@ class InterestingOverflowingOperation extends Operation {
7051
i1 = globalValueNumber(op1) and
7152
i2 = globalValueNumber(op2)
7253
|
54+
// For unsigned integer addition, look for this pattern:
55+
// if (x + y > x)
56+
// use(x + y)
57+
// Ensuring it is not a bad overflow check
58+
(this instanceof AddExpr or this instanceof AssignAddExpr) and
59+
this.getType().getUnspecifiedType().(IntegralType).isUnsigned() and
60+
exists(AddExpr ae, RelationalOperation relOp |
61+
globalValueNumber(relOp.getAnOperand()) = i1 and
62+
relOp.getAnOperand() = ae and
63+
globalValueNumber(ae.getAnOperand()) = i1 and
64+
globalValueNumber(ae.getAnOperand()) = i2
65+
|
66+
// At least one operand is not smaller than int
67+
exists(Expr op | op = ae.getAnOperand() |
68+
op.getType().getSize() >= any(IntType i).getSize()
69+
)
70+
or
71+
// The result of the addition is explicitly converted to a smaller type before the comparison
72+
ae.getExplicitlyConverted().getType().getSize() < any(IntType i).getSize()
73+
)
74+
or
7375
// The CERT rule for signed integer overflow has a very specific pattern it recommends
7476
// for checking for overflow. We try to match the pattern here.
7577
// ((i2 > 0 && i1 > (INT_MAX - i2)) || (i2 < 0 && i1 < (INT_MIN - i2)))
@@ -156,6 +158,20 @@ class InterestingOverflowingOperation extends Operation {
156158
)
157159
)
158160
or
161+
// CERT recommends checking for divisor != -1 and dividor != INT_MIN
162+
this instanceof DivOrRemOperation and
163+
exists(EqualityOperation eop |
164+
// GuardCondition doesn't work in this case, so just confirm that this check dominates the overflow
165+
globalValueNumber(eop.getAnOperand()) = i1 and
166+
eop.getAnOperand().getValue().toFloat() =
167+
typeLowerBound(this.(DivOrRemOperation).getDividend().getType().getUnderlyingType())
168+
) and
169+
exists(EqualityOperation eop |
170+
// GuardCondition doesn't work in this case, so just confirm that this check dominates the overflow
171+
globalValueNumber(eop.getAnOperand()) = i2 and
172+
eop.getAnOperand().getValue().toInt() = -1
173+
)
174+
or
159175
// The CERT rule for signed integer overflow has a very specific pattern it recommends
160176
// for checking for multiplication underflow/overflow. We just use a heuristic here,
161177
// which determines if at least 4 checks of the sort `a < INT_MAX / b` are present in the code.
@@ -193,36 +209,31 @@ class InterestingOverflowingOperation extends Operation {
193209
)
194210
)
195211
}
196-
197-
/**
198-
* Identifies a bad overflow check for this overflow expression.
199-
*/
200-
GuardCondition getABadOverflowCheck() {
201-
exists(RelationalOperation relOp |
202-
(this instanceof AddExpr or this instanceof AssignAddExpr) and
203-
result = relOp and
204-
// Looking for this pattern:
205-
// if (x + y > x)
206-
// use(x + y)
207-
//
208-
globalValueNumber(relOp.getAnOperand()) = globalValueNumber(this) and
209-
globalValueNumber(relOp.getAnOperand()) = globalValueNumber(this.getAnOperand())
210-
|
211-
// Signed overflow checks are insufficient
212-
this.getUnspecifiedType().(IntegralType).isSigned()
213-
or
214-
// Unsigned overflow checks can still be bad, if the result is promoted.
215-
forall(Expr op | op = this.getAnOperand() | op.getType().getSize() < any(IntType i).getSize()) and
216-
// Not explicitly converted to a smaller type before the comparison
217-
not this.getExplicitlyConverted().getType().getSize() < any(IntType i).getSize()
218-
)
219-
}
220212
}
221213

222214
private class StrictRelationalOperation extends RelationalOperation {
223215
StrictRelationalOperation() { this.getOperator() = [">", "<"] }
224216
}
225217

218+
class DivOrRemOperation extends Operation {
219+
DivOrRemOperation() {
220+
this instanceof DivExpr or
221+
this instanceof RemExpr or
222+
this instanceof AssignDivExpr or
223+
this instanceof AssignRemExpr
224+
}
225+
226+
Expr getDividend() {
227+
result = this.(BinaryOperation).getLeftOperand() or
228+
result = this.(AssignArithmeticOperation).getLValue()
229+
}
230+
231+
Expr getDivisor() {
232+
result = this.(BinaryOperation).getRightOperand() or
233+
result = this.(AssignArithmeticOperation).getRValue()
234+
}
235+
}
236+
226237
/**
227238
* Module inspired by the IntMultToLong.ql query.
228239
*/

0 commit comments

Comments
 (0)