Skip to content

Commit 3a33a22

Browse files
committed
IntegerOverflow: recognise safe crements
Due to widening in loops, SimpleRangeAnalysis is overly cautious about crement operations in loop updates. This commit makes some small adjustments to identify "safe" crement operations that cannot overflow due to the bounding by the loop counters.
1 parent b455965 commit 3a33a22

File tree

3 files changed

+89
-11
lines changed

3 files changed

+89
-11
lines changed

cpp/autosar/test/rules/A4-7-1/IntegerExpressionLeadToDataLoss.expected

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
| IntMultToLongc.cpp:109:13:109:28 | ... + ... | Binary expression ...+... may overflow. |
99
| test.cpp:2:10:2:14 | ... + ... | Binary expression ...+... may overflow. |
1010
| test.cpp:22:12:22:16 | ... + ... | Binary expression ...+... may overflow. |
11-
| test.cpp:52:7:52:14 | ... + ... | Binary expression ...+... may overflow. |
11+
| test.cpp:50:7:50:14 | ... + ... | Binary expression ...+... may overflow. |
12+
| test.cpp:62:8:62:10 | ... ++ | Binary expression ...++... may overflow. |

cpp/autosar/test/rules/A4-7-1/test.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,31 @@ short test_addition_invalid_overflow_check(short x, short y) {
3535
return 0;
3636
}
3737

38-
void test_addition_loop_bound(unsigned int base, unsigned int size) {
39-
if (size > 0) {
40-
int n = size - 1;
41-
for (int i = 0; i < n; i++) {
42-
base + i; // COMPLIANT - `i` is bounded
38+
void test_addition_loop_bound(unsigned short base, unsigned int n) {
39+
if (n < 1000) {
40+
for (unsigned int i = 0; i < n; i++) { // COMPLIANT
41+
base + i; // COMPLIANT - `i` is bounded
4342
}
4443
}
4544
}
4645

47-
void test_addition_invalid_loop_bound(unsigned int base, unsigned int j,
48-
unsigned int size) {
49-
if (size > 0) {
50-
int n = size - 1;
51-
for (int i = 0; i < n; i++) {
46+
void test_addition_invalid_loop_bound(unsigned short base, unsigned int j,
47+
unsigned int n) {
48+
if (n < 1000) {
49+
for (unsigned int i = 0; i < n; i++) { // COMPLIANT
5250
base + j; // NON_COMPLIANT - guards are not related
5351
}
5452
}
53+
}
54+
55+
void test_loop_bound(unsigned int n) {
56+
for (unsigned int i = 0; i < n; i++) { // COMPLIANT
57+
}
58+
}
59+
60+
void test_loop_bound_bad(unsigned int n) {
61+
for (unsigned short i = 0; i < n;
62+
i++) { // NON_COMPLIANT - crement will overflow before loop bound is
63+
// reached
64+
}
5565
}

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class InterestingOverflowingOperation extends Operation {
3333
// Multiplication is not covered by the standard range analysis library, so implement our own
3434
// mini analysis.
3535
(this instanceof MulExpr implies MulExprAnalysis::overflows(this)) and
36+
// This shouldn't be a "safe" crement operation
37+
not LoopCounterAnalysis::isCrementSafeFromOverflow(this) and
3638
// Not within a macro
3739
not this.isAffectedByMacro() and
3840
// Ignore pointer arithmetic
@@ -328,3 +330,68 @@ private module MulExprAnalysis {
328330
me.(MulAnalyzableExpr).minValue() < exprMinVal(me)
329331
}
330332
}
333+
334+
/**
335+
* An analysis on safe loop counters.
336+
*/
337+
module LoopCounterAnalysis {
338+
newtype LoopBound =
339+
LoopUpperBound() or
340+
LoopLowerBound()
341+
342+
predicate isLoopBounded(
343+
CrementOperation cop, ForStmt fs, Variable loopCounter, Expr initializer, Expr counterBound,
344+
LoopBound boundKind, boolean equals
345+
) {
346+
// Initialization sets the loop counter
347+
(
348+
loopCounter = fs.getInitialization().(DeclStmt).getADeclaration() and
349+
initializer = loopCounter.getInitializer().getExpr()
350+
or
351+
loopCounter.getAnAssignment() = initializer and
352+
initializer = fs.getInitialization().(ExprStmt).getExpr()
353+
) and
354+
// Condition is a relation operation on the loop counter
355+
exists(RelationalOperation relOp |
356+
fs.getCondition() = relOp and
357+
(if relOp.getOperator().charAt(1) = "=" then equals = true else equals = false)
358+
|
359+
relOp.getGreaterOperand() = loopCounter.getAnAccess() and
360+
relOp.getLesserOperand() = counterBound and
361+
cop instanceof DecrementOperation and
362+
boundKind = LoopLowerBound()
363+
or
364+
relOp.getLesserOperand() = loopCounter.getAnAccess() and
365+
relOp.getGreaterOperand() = counterBound and
366+
cop instanceof IncrementOperation and
367+
boundKind = LoopUpperBound()
368+
) and
369+
// Update is a crement operation with the loop counter
370+
fs.getUpdate() = cop and
371+
cop.getOperand() = loopCounter.getAnAccess()
372+
}
373+
374+
/**
375+
* Holds if the crement operation is safe from under/overflow.
376+
*/
377+
predicate isCrementSafeFromOverflow(CrementOperation op) {
378+
exists(
379+
Expr initializer, Expr counterBound, LoopBound boundKind, boolean equals, int equalsOffset
380+
|
381+
isLoopBounded(op, _, _, initializer, counterBound, boundKind, equals) and
382+
(
383+
equals = true and equalsOffset = 1
384+
or
385+
equals = false and equalsOffset = 0
386+
)
387+
|
388+
boundKind = LoopUpperBound() and
389+
// upper bound of the inccrement is smaller than the maximum value representable in the type
390+
upperBound(counterBound) + equalsOffset <= typeUpperBound(op.getType().getUnspecifiedType())
391+
or
392+
// the lower bound of the decrement is larger than the smal
393+
boundKind = LoopLowerBound() and
394+
lowerBound(counterBound) - equalsOffset >= typeLowerBound(op.getType().getUnspecifiedType())
395+
)
396+
}
397+
}

0 commit comments

Comments
 (0)