Skip to content

Commit 6dd30df

Browse files
committed
IntegerOverflow: Support MulExpr
Add support for `MulExpr`s to the overflow library.
1 parent 37df9a6 commit 6dd30df

File tree

4 files changed

+116
-4
lines changed

4 files changed

+116
-4
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
| test.c:28:19:28:25 | ... + ... | Binary expression ...+... of type int may overflow or underflow. |
44
| test.c:36:3:36:9 | ... - ... | Binary expression ...-... of type int may overflow or underflow. |
55
| test.c:49:19:49:25 | ... - ... | Binary expression ...-... of type int may overflow or underflow. |
6+
| test.c:56:3:56:8 | ... * ... | Binary expression ...*... of type int may overflow or underflow. |

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void test_add_precheck(signed int i1, signed int i2) {
1717
}
1818

1919
void test_add_precheck_2(signed int i1, signed int i2) {
20-
if (i1 + i2 < i1) { // Bad overflow check - undefined behavior
20+
if (i1 + i2 < i1) { // NON_COMPLIANT - bad overflow check - undefined behavior
2121
// handle error
2222
} else {
2323
i1 + i2; // NON_COMPLIANT

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@ 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*())
28+
not e.getAGuardingGVN() = globalValueNumber(e.getAChild*()) and
29+
// Covered by `IntMultToLong.ql` instead
30+
not e instanceof MulExpr
2931
select e, "Binary expression ..." + e.getOperator() + "... may overflow."

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

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ class InterestingBinaryOverflowingExpr extends BinaryArithmeticOperation {
2121
or
2222
exprMightOverflowPositively(this)
2323
) and
24+
// Multiplication is not covered by the standard range analysis library, so implement our own
25+
// mini analysis.
26+
(this instanceof MulExpr implies MulExprAnalysis::overflows(this)) and
27+
// Not within a macro
2428
not this.isAffectedByMacro() and
2529
// Ignore pointer arithmetic
2630
not this instanceof PointerArithmeticOperation and
27-
// Covered by `IntMultToLong.ql` instead
28-
not this instanceof MulExpr and
2931
// Not covered by this query - overflow/underflow in division is rare
3032
not this instanceof DivExpr
3133
}
@@ -138,6 +140,18 @@ class InterestingBinaryOverflowingExpr extends BinaryArithmeticOperation {
138140
)
139141
)
140142
)
143+
or
144+
// The CERT rule for signed integer overflow has a very specific pattern it recommends
145+
// for checking for multiplication underflow/overflow. We just use a heuristic here,
146+
// which determines if at least 4 checks of the sort `a < INT_MAX / b` are present in the code.
147+
this instanceof MulExpr and
148+
count(StrictRelationalOperation rel |
149+
globalValueNumber(rel.getAnOperand()) = i1 and
150+
globalValueNumber(rel.getAnOperand().(DivExpr).getRightOperand()) = i2
151+
or
152+
globalValueNumber(rel.getAnOperand()) = i2 and
153+
globalValueNumber(rel.getAnOperand().(DivExpr).getRightOperand()) = i1
154+
) >= 4
141155
)
142156
}
143157

@@ -193,3 +207,98 @@ class InterestingBinaryOverflowingExpr extends BinaryArithmeticOperation {
193207
private class StrictRelationalOperation extends RelationalOperation {
194208
StrictRelationalOperation() { this.getOperator() = [">", "<"] }
195209
}
210+
211+
/**
212+
* Module inspired by the IntMultToLong.ql query.
213+
*/
214+
private module MulExprAnalysis {
215+
/**
216+
* As SimpleRangeAnalysis does not support reasoning about multiplication
217+
* we create a tiny abstract interpreter for handling multiplication, which
218+
* we invoke only after weeding out of all of trivial cases that we do
219+
* not care about. By default, the maximum and minimum values are computed
220+
* using SimpleRangeAnalysis.
221+
*/
222+
class AnalyzableExpr extends Expr {
223+
AnalyzableExpr() {
224+
// A integer multiplication, or an expression within an integral expression
225+
this.(MulExpr).getType().getUnspecifiedType() instanceof IntegralType or
226+
this.getParent() instanceof AnalyzableExpr or
227+
this.(Conversion).getExpr() instanceof AnalyzableExpr
228+
}
229+
230+
float maxValue() { result = upperBound(this.getFullyConverted()) }
231+
232+
float minValue() { result = lowerBound(this.getFullyConverted()) }
233+
}
234+
235+
class ParenAnalyzableExpr extends AnalyzableExpr, ParenthesisExpr {
236+
override float maxValue() { result = this.getExpr().(AnalyzableExpr).maxValue() }
237+
238+
override float minValue() { result = this.getExpr().(AnalyzableExpr).minValue() }
239+
}
240+
241+
class MulAnalyzableExpr extends AnalyzableExpr, MulExpr {
242+
override float maxValue() {
243+
exists(float x1, float y1, float x2, float y2 |
244+
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
245+
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
246+
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
247+
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
248+
result = (x1 * y1).maximum(x1 * y2).maximum(x2 * y1).maximum(x2 * y2)
249+
)
250+
}
251+
252+
override float minValue() {
253+
exists(float x1, float x2, float y1, float y2 |
254+
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
255+
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
256+
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
257+
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
258+
result = (x1 * y1).minimum(x1 * y2).minimum(x2 * y1).minimum(x2 * y2)
259+
)
260+
}
261+
}
262+
263+
/**
264+
* Analyze add expressions directly. This handles the case where an add expression is contributed to
265+
* by a multiplication.
266+
*/
267+
class AddAnalyzableExpr extends AnalyzableExpr, AddExpr {
268+
override float maxValue() {
269+
result =
270+
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() +
271+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
272+
}
273+
274+
override float minValue() {
275+
result =
276+
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() +
277+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
278+
}
279+
}
280+
281+
/**
282+
* Analyze sub expressions directly. This handles the case where a sub expression is contributed to
283+
* by a multiplication.
284+
*/
285+
class SubAnalyzableExpr extends AnalyzableExpr, SubExpr {
286+
override float maxValue() {
287+
result =
288+
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() -
289+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
290+
}
291+
292+
override float minValue() {
293+
result =
294+
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() -
295+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
296+
}
297+
}
298+
299+
predicate overflows(MulExpr me) {
300+
me.(MulAnalyzableExpr).maxValue() > exprMaxVal(me)
301+
or
302+
me.(MulAnalyzableExpr).minValue() < exprMinVal(me)
303+
}
304+
}

0 commit comments

Comments
 (0)