Skip to content

Commit 999e870

Browse files
committed
Tidy up, refine a bit more, add a series of test cases
1 parent 2f6fc3d commit 999e870

File tree

2 files changed

+120
-263
lines changed

2 files changed

+120
-263
lines changed

cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql

Lines changed: 82 additions & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
*/
1717

1818
import cpp
19+
import semmle.code.cpp.dataflow.internal.AddressFlow
1920
import codingstandards.cpp.misra
2021
import codingstandards.cpp.Call
22+
import codingstandards.cpp.Loops
2123
import codingstandards.cpp.misra.BuiltInTypeRules::MisraCpp23BuiltInTypes
2224

2325
/**
@@ -36,7 +38,7 @@ class LegacyForLoopCondition extends RelationalOperation {
3638
LegacyForLoopCondition() {
3739
loopCounter = this.getAnOperand() and
3840
loopBound = this.getAnOperand() and
39-
loopCounter.getTarget() = forLoop.getInitialization().(DeclStmt).getADeclaration() and
41+
loopCounter.getTarget() = getAnIterationVariable(forLoop) and
4042
loopBound != loopCounter
4143
}
4244

@@ -52,18 +54,22 @@ class LegacyForLoopCondition extends RelationalOperation {
5254
}
5355

5456
/**
55-
* Holds if the given expression is impure and contains an access to the variable, and
56-
* thus may mutate the variable.
57-
*
58-
* Note that this relation over-approximates and might include impure expressions that
59-
* in fact do not mutate the variable.
57+
* Holds if the given expression may mutate the variable.
6058
*/
61-
predicate exprWithVarAccessMaybeImpure(Expr expr, Variable variable) {
62-
exists(VariableAccess varAccess |
63-
expr.mayBeImpure() and
64-
expr.getAChild*() = varAccess and // TODO: the `l` in the `i += l` is not mutated!
65-
variable = varAccess.getTarget()
66-
)
59+
predicate variableModifiedInExpression(Expr expr, VariableAccess va) {
60+
/*
61+
* 1. Direct modification (assignment, increment, etc.) or a function call.
62+
*/
63+
64+
expr.getAChild+() = va and
65+
va.isModified()
66+
or
67+
/*
68+
* 2. Address taken for non-const access that can potentially lead to modification.
69+
* This overlaps with the former example on cases where `expr` is a function call.
70+
*/
71+
72+
valueToUpdate(va, _, expr)
6773
}
6874

6975
/**
@@ -79,212 +85,26 @@ Expr getLoopStepOfForStmt(ForStmt forLoop) {
7985
result = forLoop.getUpdate().(AssignSubExpr).getRValue()
8086
}
8187

82-
/**
83-
* Holds if the given function has as parameter a pointer to a constant
84-
* value, at a given index.
85-
*/
86-
private predicate functionHasConstPointerParameter(Function function, int index) {
87-
function.getParameter(index).getType().(PointerType).getBaseType().isConst()
88-
}
89-
90-
/**
91-
* Holds if the variable behind a given variable access is taken its address in
92-
* a non-const variable declaration, in the body of the for-loop.
93-
*
94-
* e.g.1. The loop counter variable `i` in the body is taken its address in the
95-
* declaration of a pointer variable `m`.
96-
* ``` C++
97-
* for (int i = 0; i < k; i += l) {
98-
* int *m = &i;
99-
* }
100-
* ```
101-
* e.g.2. The loop bound variable `k` in the body is taken its address in the
102-
* declaration of a pointer variable `m`.
103-
* ``` C++
104-
* for (int i = j; i < k; i += l) {
105-
* int *m = &k;
106-
* }
107-
* ```
108-
*/
109-
predicate variableAddressTakenInNonConstDeclaration(
110-
ForStmt forLoop, VariableAccess baseVariableAccess
111-
) {
112-
exists(AddressOfExpr addressOfExpr, DeclStmt decl |
113-
decl.getParentStmt+() = forLoop and
114-
decl.getADeclarationEntry().(VariableDeclarationEntry).getVariable().getInitializer().getExpr() =
115-
addressOfExpr and
116-
addressOfExpr.getOperand() = baseVariableAccess and
117-
not decl.getADeclarationEntry()
118-
.(VariableDeclarationEntry)
119-
.getVariable()
120-
.getType()
121-
.(PointerType)
122-
.getBaseType()
123-
.isConst()
124-
)
125-
}
126-
127-
/**
128-
* Holds if the variable behind a given variable access is taken its address
129-
* as an argument of a call in either the body of the for-loop or in its update
130-
* expression.
131-
*
132-
* e.g.1. The address of the loop counter variable `i` is passed as argument
133-
* to the call to `g`.
134-
* ``` C++
135-
* void g1(int *x);
136-
*
137-
* for (int i = 0; i < k; i += l) {
138-
* g1(&i);
139-
* }
140-
* ```
141-
* e.g.2. The address of the loop counter variable `k` is passed as argument
142-
* to the call to `g`.
143-
* ``` C++
144-
* void g1(int *x);
145-
*
146-
* for (int i = j; i < k; i += l) {
147-
* g1(&k);
148-
* }
149-
* ```
150-
*/
151-
private predicate variableAddressTakenAsConstArgument(
152-
ForStmt forLoop, VariableAccess baseVariableAccess, Call call
153-
) {
154-
exists(AddressOfExpr addressOfExpr, int index |
155-
call.getParent+() = forLoop.getAChild+() and // TODO: Bad
156-
call.getArgument(index).getAChild*() = addressOfExpr and
157-
exists(PointerType parameterType |
158-
parameterType = call.getTarget().getParameter(index).getType() and
159-
not parameterType.getBaseType().isConst()
160-
) and
161-
addressOfExpr.getOperand() = baseVariableAccess.getTarget().getAnAccess() and
162-
baseVariableAccess.getParent+() = forLoop
163-
)
164-
}
165-
166-
/**
167-
* Holds if the variable behind a given variable access is taken its address
168-
* as an argument of a complex expression in either the body of the for-loop or
169-
* in its update expression.
170-
*
171-
* e.g.1. The loop counter variable `i` in the body and the loop bound variable `k`
172-
* is taken its address in a call.
173-
* ``` C++
174-
* void g1(int *x);
175-
*
176-
* for (int i = j; i < k; i += l) {
177-
* g1(&i);
178-
* }
179-
* ```
180-
* e.g.2. The loop counter variable `i` in the body and the loop bound variable `k`
181-
* is taken its address in a compound expression.
182-
* ``` C++
183-
* for (int i = 0; i < k; i += l) {
184-
* *(cond ? &i : &k) += 1;
185-
* }
186-
* ```
187-
*/
188-
/* TODO: Do we need to use Expr.getUnderlyingType() to ensure that the expression is non-const? */
189-
predicate variableAddressTakenInExpression(ForStmt forLoop, VariableAccess baseVariableAccess) {
190-
exists(AddressOfExpr addressOfExpr |
191-
baseVariableAccess.getParent+() = forLoop.getAChild+() and // TODO: Bad
192-
addressOfExpr.getParent+() = forLoop.getAChild+() and
193-
addressOfExpr.getOperand() = baseVariableAccess.getTarget().getAnAccess()
194-
)
195-
}
196-
197-
/**
198-
* Holds if the variable behind a given variable access is taken its reference
199-
* in a non-const variable declaration, in the body of the for-loop.
200-
*
201-
* e.g.1. The loop counter variable `i` in the body is taken its reference in
202-
* the declaration of a variable `m`.
203-
* ``` C++
204-
* for (int i = j; i < k; i += l) {
205-
* int &m = i;
206-
* }
207-
* ```
208-
* e.g.2. The loop bound variable `k` in the body is taken its reference in the
209-
* declaration of a variable `m`.
210-
* ``` C++
211-
* for (int i = j; i < k; i += l) {
212-
* int &m = k;
213-
* }
214-
* ```
215-
*/
216-
predicate variableReferenceTakenInNonConstDeclaration(
217-
ForStmt forLoop, VariableAccess baseVariableAccess
218-
) {
219-
exists(DeclStmt decl, Variable definedVariable, ReferenceType definedVariableType |
220-
decl.getParentStmt+() = forLoop and
221-
not decl = forLoop.getInitialization() and // Exclude the for-loop counter initialization.
222-
definedVariable = decl.getADeclarationEntry().(VariableDeclarationEntry).getVariable() and
223-
definedVariable.getInitializer().getExpr() = baseVariableAccess and
224-
definedVariableType = definedVariable.getType() and
225-
not definedVariableType.getBaseType().isConst()
226-
)
227-
}
228-
229-
/**
230-
* Holds if the variable behind a given variable access is taken its reference
231-
* as an argument of a call in either the body of the for-loop or in its update
232-
* expression.
233-
*
234-
* e.g.1. The loop counter variable `i` in the body is passed by reference to the
235-
* call to `f1`.
236-
* ``` C++
237-
* void f1(int &x);
238-
*
239-
* for (int i = j; i < k; i += l) {
240-
* f1(i);
241-
* }
242-
* ```
243-
* e.g.2. The loop bound variable `k` in the body is passed by reference to the
244-
* call to `f1`.
245-
* ``` C++
246-
* void f1(int &x);
247-
*
248-
* for (int i = j; i < k; i += l) {
249-
* f1(k);
250-
* }
251-
* ```
252-
*/
253-
private predicate variableReferenceTakenAsNonConstArgument(
254-
ForStmt forLoop, VariableAccess baseVariableAccess, Call call
255-
) {
256-
exists(int index |
257-
call.getParent+() = forLoop.getAChild+() and
258-
call.getArgument(index).getAChild*() = baseVariableAccess.getTarget().getAnAccess() and
259-
/*
260-
* The given function has as parameter a reference of a constant
261-
* value, at a given index.
262-
*/
263-
264-
exists(ReferenceType parameterType |
265-
parameterType = call.getTarget().getParameter(index).getType() and
266-
not parameterType.getBaseType().isConst()
267-
) and
268-
baseVariableAccess.getParent+() = forLoop
269-
)
270-
}
271-
272-
predicate loopVariableAssignedToPointerOrReferenceType(
88+
predicate loopVariableAssignedToNonConstPointerOrReferenceType(
27389
ForStmt forLoop, VariableAccess loopVariableAccessInCondition
27490
) {
27591
exists(Expr assignmentRhs, DerivedType targetType |
276-
assignmentRhs.getEnclosingStmt().getParent*() = forLoop.getStmt() and
277-
(
278-
assignmentRhs.(AddressOfExpr).getOperand() =
279-
loopVariableAccessInCondition.getTarget().getAnAccess() or
280-
assignmentRhs = loopVariableAccessInCondition.getTarget().getAnAccess()
281-
) and
28292
isAssignment(assignmentRhs, targetType, _) and
93+
not targetType.getBaseType().isConst() and
28394
(
28495
targetType instanceof PointerType or
28596
targetType instanceof ReferenceType
286-
) and
287-
not targetType.getBaseType().isConst()
97+
)
98+
|
99+
assignmentRhs.getEnclosingStmt().getParent*() = forLoop.getStmt() and
100+
(
101+
/* 1. The address is taken: A loop variable access */
102+
assignmentRhs.(AddressOfExpr).getOperand() =
103+
loopVariableAccessInCondition.getTarget().getAnAccess()
104+
or
105+
/* 2. The address is taken: A loop variable access */
106+
assignmentRhs = loopVariableAccessInCondition.getTarget().getAnAccess()
107+
)
288108
)
289109
}
290110

@@ -301,12 +121,12 @@ predicate loopVariableAssignedToPointerOrReferenceType(
301121
* Also, this predicate requires that the call is the body of the given for-loop.
302122
*/
303123

304-
predicate loopVariablePassedAsArgumentToReferenceParameter(
305-
ForStmt forLoop, Expr loopVariableAccessInCondition
124+
predicate loopVariablePassedAsArgumentToNonConstReferenceParameter(
125+
ForStmt forLoop, VariableAccess loopVariableAccessInCondition
306126
) {
307127
exists(ReferenceType targetType |
308128
exists(Call call, int i |
309-
call.getArgument(i) = loopVariableAccessInCondition and
129+
call.getArgument(i) = loopVariableAccessInCondition.getTarget().getAnAccess() and
310130
call.getEnclosingStmt().getParent*() = forLoop.getStmt() and
311131
not targetType.getBaseType().isConst()
312132
|
@@ -343,11 +163,8 @@ where
343163
not forLoop.getCondition() instanceof LegacyForLoopCondition
344164
or
345165
/* 3. The loop counter is mutated somewhere other than its update expression. */
346-
exists(Expr mutatingExpr, Variable loopCounter |
347-
mutatingExpr = forLoop.getStmt().getChildStmt().getAChild() and
348-
loopCounter = forLoop.getAnIterationVariable()
349-
|
350-
exprWithVarAccessMaybeImpure(mutatingExpr, loopCounter)
166+
exists(Variable loopCounter |
167+
isIrregularLoopCounterModification(forLoop, loopCounter, loopCounter.getAnAccess())
351168
)
352169
or
353170
/* 4. The type size of the loop counter is not greater or equal to that of the loop counter. */
@@ -360,27 +177,45 @@ where
360177
)
361178
)
362179
or
363-
/* 5. The loop bound and the loop step is a variable that is mutated in the for loop. */
364-
exists(Expr mutatingExpr |
365-
(
366-
/* 1. The mutating expression may be in the loop body. */
180+
/*
181+
* 5. The loop bound and the loop step are non-const expressions, or are variables that are
182+
* mutated in the for loop.
183+
*/
184+
185+
/* 5-1. The mutating expression mutates the loop bound. */
186+
exists(Expr loopBound |
187+
loopBound = forLoop.getCondition().(LegacyForLoopCondition).getLoopBound()
188+
|
189+
exists(Expr mutatingExpr |
190+
/* The mutating expression may be in the loop body. */
367191
mutatingExpr = forLoop.getStmt().getChildStmt().getAChild*()
368192
or
369-
/* 2. The mutating expression may be in the loop updating expression. */
193+
/* The mutating expression may be in the loop updating expression. */
370194
mutatingExpr = forLoop.getUpdate().getAChild*()
371-
)
372-
|
373-
/* 5-1. The mutating expression mutates the loop bound. */
374-
exists(LegacyForLoopCondition forLoopCondition, Variable loopBoundVariable |
375-
forLoopCondition = forLoop.getCondition() and
376-
loopBoundVariable = forLoopCondition.getLoopBound().(VariableAccess).getTarget()
377195
|
378-
exprWithVarAccessMaybeImpure(mutatingExpr, loopBoundVariable)
196+
/* 5-1-1. The loop bound is a variable that is mutated in the for loop. */
197+
variableModifiedInExpression(mutatingExpr,
198+
loopBound.(VariableAccess).getTarget().getAnAccess())
199+
or
200+
/* 5-1-2. The loop bound is not a variable access and is not a constant expression. */
201+
not loopBound instanceof VariableAccess and not loopBound.isConstant()
379202
)
380-
or
381-
/* 5-2. The mutating expression mutates the loop step. */
382-
exists(VariableAccess loopStep | loopStep = getLoopStepOfForStmt(forLoop) |
383-
exprWithVarAccessMaybeImpure(mutatingExpr, loopStep.getTarget())
203+
)
204+
or
205+
/* 5-2. The mutating expression mutates the loop step. */
206+
exists(Expr loopStep | loopStep = getLoopStepOfForStmt(forLoop) |
207+
exists(Expr mutatingExpr |
208+
/* The mutating expression may be in the loop body. */
209+
mutatingExpr = forLoop.getStmt().getChildStmt().getAChild*()
210+
or
211+
/* The mutating expression may be in the loop updating expression. */
212+
mutatingExpr = forLoop.getUpdate().getAChild*()
213+
|
214+
/* 5-1-2. The loop step is a variable that is mutated in the for loop. */
215+
variableModifiedInExpression(mutatingExpr, loopStep.(VariableAccess).getTarget().getAnAccess())
216+
or
217+
/* 5-1-2. The loop bound is not a variable access and is not a constant expression. */
218+
not loopStep instanceof VariableAccess and not loopStep.isConstant()
384219
)
385220
)
386221
or
@@ -390,12 +225,17 @@ where
390225
*/
391226

392227
exists(VariableAccess loopVariableAccessInCondition |
393-
loopVariableAccessInCondition = forLoop.getCondition().(LegacyForLoopCondition).getLoopCounter() or
394-
loopVariableAccessInCondition = forLoop.getCondition().(LegacyForLoopCondition).getLoopBound() or
395-
loopVariableAccessInCondition = getLoopStepOfForStmt(forLoop)
396-
|
397-
loopVariableAssignedToPointerOrReferenceType(forLoop, loopVariableAccessInCondition)
398-
or
399-
loopVariablePassedAsArgumentToReferenceParameter(forLoop, loopVariableAccessInCondition)
228+
(
229+
loopVariableAccessInCondition =
230+
forLoop.getCondition().(LegacyForLoopCondition).getLoopCounter() or
231+
loopVariableAccessInCondition = forLoop.getCondition().(LegacyForLoopCondition).getLoopBound() or
232+
loopVariableAccessInCondition = getLoopStepOfForStmt(forLoop)
233+
) and
234+
(
235+
loopVariableAssignedToNonConstPointerOrReferenceType(forLoop, loopVariableAccessInCondition)
236+
or
237+
loopVariablePassedAsArgumentToNonConstReferenceParameter(forLoop,
238+
loopVariableAccessInCondition)
239+
)
400240
)
401241
select forLoop, "TODO"

0 commit comments

Comments
 (0)