-
Notifications
You must be signed in to change notification settings - Fork 67
Package: Side effects3 #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
f2ce331
Add SidePackage3 rule description and metadata
rvermeulen ee8db1d
Change the definition of a full expr to match the standard
rvermeulen 25cee4f
Add full declarators case to ordering module
rvermeulen 3e88808
Consider children expression when determining ordering
rvermeulen 19bebdd
Address typos
rvermeulen 86540f5
Remove unimplemented queries for RULE-13-2 from package.
rvermeulen 96317b6
Add query for RULE 13-2
rvermeulen 3e94fd7
Correct comments
rvermeulen f0ee5b1
Attemp to optimize query
rvermeulen 84b1ad8
Add comments to explain steps
rvermeulen 962a640
Provide more useful predicate names
rvermeulen 9deadab
Change scope of RULE 13-2
rvermeulen a3d046f
Remove PRE31-C from side effects 3
rvermeulen bef7b9e
Address incorrect alert specification
rvermeulen dc02657
Add support for MISRA example case
rvermeulen ca80465
Remove PRE31-C from side effects 3
rvermeulen f0d8026
Update FullExpr.expected
rvermeulen 693df26
Merge branch 'main' into rvermeulen/side-effects3
lcartey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
/** | ||
rvermeulen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @id c/misra/unsequenced-side-effects | ||
* @name RULE-13-2: The value of an expression and its persistent side effects depend on its evaluation order | ||
* @description The value of an expression and its persistent side effects are depending on the | ||
* evaluation order resulting in unpredictable behavior. | ||
* @kind problem | ||
* @precision very-high | ||
* @problem.severity error | ||
* @tags external/misra/id/rule-13-2 | ||
* correctness | ||
* external/misra/obligation/required | ||
*/ | ||
|
||
import cpp | ||
import codingstandards.c.misra | ||
import codingstandards.c.Expr | ||
import codingstandards.c.SideEffects | ||
import codingstandards.c.Ordering | ||
|
||
class VariableEffectOrAccess extends Expr { | ||
VariableEffectOrAccess() { | ||
this instanceof VariableEffect or | ||
this instanceof VariableAccess | ||
} | ||
} | ||
|
||
pragma[noinline] | ||
predicate partOfFullExpr(VariableEffectOrAccess e, FullExpr fe) { | ||
( | ||
e.(VariableEffect).getAnAccess() = fe.getAChild+() | ||
or | ||
e.(VariableAccess) = fe.getAChild+() | ||
) | ||
} | ||
|
||
class ConstituentExprOrdering extends Ordering::Configuration { | ||
ConstituentExprOrdering() { this = "ConstituentExprOrdering" } | ||
|
||
override predicate isCandidate(Expr e1, Expr e2) { | ||
exists(FullExpr fe | | ||
partOfFullExpr(e1, fe) and | ||
partOfFullExpr(e2, fe) | ||
) | ||
} | ||
} | ||
|
||
predicate sameFullExpr(FullExpr fe, VariableAccess va1, VariableAccess va2) { | ||
partOfFullExpr(va1, fe) and | ||
partOfFullExpr(va2, fe) and | ||
va1 != va2 and | ||
exists(Variable v1, Variable v2 | | ||
// Use `pragma[only_bind_into]` to prevent CP between variable accesses. | ||
va1.getTarget() = pragma[only_bind_into](v1) and va2.getTarget() = pragma[only_bind_into](v2) | ||
| | ||
v1.isVolatile() and v2.isVolatile() | ||
or | ||
not (v1.isVolatile() and v2.isVolatile()) and | ||
v1 = v2 | ||
) | ||
} | ||
|
||
int getLeafCount(LeftRightOperation bop) { | ||
if | ||
not bop.getLeftOperand() instanceof BinaryOperation and | ||
not bop.getRightOperand() instanceof BinaryOperation | ||
then result = 2 | ||
else | ||
if | ||
bop.getLeftOperand() instanceof BinaryOperation and | ||
not bop.getRightOperand() instanceof BinaryOperation | ||
then result = 1 + getLeafCount(bop.getLeftOperand()) | ||
else | ||
if | ||
not bop.getLeftOperand() instanceof BinaryOperation and | ||
bop.getRightOperand() instanceof BinaryOperation | ||
then result = 1 + getLeafCount(bop.getRightOperand()) | ||
else result = getLeafCount(bop.getLeftOperand()) + getLeafCount(bop.getRightOperand()) | ||
} | ||
|
||
class LeftRightOperation extends Expr { | ||
LeftRightOperation() { | ||
this instanceof BinaryOperation or | ||
this instanceof AssignOperation or | ||
this instanceof AssignExpr | ||
} | ||
|
||
Expr getLeftOperand() { | ||
result = this.(BinaryOperation).getLeftOperand() | ||
or | ||
result = this.(AssignOperation).getLValue() | ||
or | ||
result = this.(AssignExpr).getLValue() | ||
} | ||
|
||
Expr getRightOperand() { | ||
result = this.(BinaryOperation).getRightOperand() | ||
or | ||
result = this.(AssignOperation).getRValue() | ||
or | ||
result = this.(AssignExpr).getRValue() | ||
} | ||
|
||
Expr getAnOperand() { | ||
result = getLeftOperand() or | ||
result = getRightOperand() | ||
} | ||
} | ||
|
||
int getOperandIndexIn(FullExpr fullExpr, Expr operand) { | ||
result = getOperandIndex(fullExpr, operand) | ||
or | ||
fullExpr.(Call).getArgument(result).getAChild*() = operand | ||
} | ||
|
||
int getOperandIndex(LeftRightOperation binop, Expr operand) { | ||
if operand = binop.getAnOperand() | ||
then | ||
operand = binop.getLeftOperand() and | ||
result = 0 | ||
or | ||
operand = binop.getRightOperand() and | ||
result = getLeafCount(binop.getLeftOperand()) + 1 | ||
or | ||
operand = binop.getRightOperand() and | ||
not binop.getLeftOperand() instanceof LeftRightOperation and | ||
result = 1 | ||
else ( | ||
// Child of left operand that is a binary operation. | ||
result = getOperandIndex(binop.getLeftOperand(), operand) | ||
or | ||
// Child of left operand that is not a binary operation. | ||
result = 0 and | ||
not binop.getLeftOperand() instanceof LeftRightOperation and | ||
binop.getLeftOperand().getAChild+() = operand | ||
or | ||
// Child of right operand and both left and right operands are binary operations. | ||
result = | ||
getLeafCount(binop.getLeftOperand()) + getOperandIndex(binop.getRightOperand(), operand) | ||
or | ||
// Child of right operand and left operand is not a binary operation. | ||
result = 1 + getOperandIndex(binop.getRightOperand(), operand) and | ||
not binop.getLeftOperand() instanceof LeftRightOperation | ||
or | ||
// Child of right operand that is not a binary operation and the left operand is a binary operation. | ||
result = getLeafCount(binop.getLeftOperand()) + 1 and | ||
binop.getRightOperand().getAChild+() = operand and | ||
not binop.getRightOperand() instanceof LeftRightOperation | ||
or | ||
// Child of right operand that is not a binary operation and the left operand is not a binary operation. | ||
result = 1 and | ||
not binop.getLeftOperand() instanceof LeftRightOperation and | ||
not binop.getRightOperand() instanceof LeftRightOperation and | ||
binop.getRightOperand().getAChild+() = operand | ||
) | ||
} | ||
|
||
from | ||
ConstituentExprOrdering orderingConfig, FullExpr fullExpr, VariableEffect variableEffect1, | ||
VariableAccess va1, VariableAccess va2, Locatable placeHolder, string label | ||
where | ||
not isExcluded(fullExpr, SideEffects3Package::unsequencedSideEffectsQuery()) and | ||
// The two access are scoped to the same full expression. | ||
sameFullExpr(fullExpr, va1, va2) and | ||
// We are only interested in effects that change an object, | ||
// i.e., exclude patterns suchs as `b->data[b->cursor++]` where `b` is considered modified and read or `foo.bar = 1` where `=` modifies to both `foo` and `bar`. | ||
not variableEffect1.isPartial() and | ||
variableEffect1.getAnAccess() = va1 and | ||
( | ||
exists(VariableEffect variableEffect2 | | ||
not variableEffect2.isPartial() and | ||
variableEffect2.getAnAccess() = va2 and | ||
// If the effect is not local (happens in a different function) we use the call with the access as a proxy. | ||
( | ||
va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and | ||
va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and | ||
orderingConfig.isUnsequenced(variableEffect1, variableEffect2) | ||
or | ||
va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and | ||
not va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and | ||
exists(Call call | | ||
rvermeulen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
call.getAnArgument() = va2 and call.getEnclosingStmt() = va1.getEnclosingStmt() | ||
| | ||
orderingConfig.isUnsequenced(variableEffect1, call) | ||
) | ||
or | ||
not va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and | ||
va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and | ||
exists(Call call | | ||
call.getAnArgument() = va1 and call.getEnclosingStmt() = va2.getEnclosingStmt() | ||
| | ||
orderingConfig.isUnsequenced(call, variableEffect2) | ||
) | ||
) and | ||
// Break the symmetry of the ordering relation by requiring that the first expression is located before the second. | ||
// This builds upon the assumption that the expressions are part of the same full expression as specified in the ordering configuration. | ||
getOperandIndexIn(fullExpr, va1) < getOperandIndexIn(fullExpr, va2) and | ||
placeHolder = variableEffect2 and | ||
label = "side effect" | ||
) | ||
or | ||
placeHolder = va2 and | ||
label = "read" and | ||
not exists(VariableEffect variableEffect2 | variableEffect1 != variableEffect2 | | ||
variableEffect2.getAnAccess() = va2 | ||
) and | ||
( | ||
va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and | ||
orderingConfig.isUnsequenced(variableEffect1, va2) | ||
or | ||
not va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and | ||
exists(Call call | | ||
call.getAnArgument() = va1 and call.getEnclosingStmt() = va2.getEnclosingStmt() | ||
| | ||
orderingConfig.isUnsequenced(call, va2) | ||
) | ||
) and | ||
// The read is not used to compute the effect on the variable. | ||
// E.g., exclude x = x + 1 | ||
not variableEffect1.getAChild+() = va2 | ||
) and | ||
// Both are evaluated | ||
not exists(ConditionalExpr ce | | ||
ce.getThen().getAChild*() = va1 and ce.getElse().getAChild*() = va2 | ||
) | ||
select fullExpr, "The expression contains unsequenced $@ to $@ and $@ to $@.", variableEffect1, | ||
lcartey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"side effect", va1, va1.getTarget().getName(), placeHolder, label, va2, va2.getTarget().getName() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
| test.c:6:12:6:18 | ... + ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:6:12:6:13 | l1 | side effect | test.c:6:12:6:13 | l1 | l1 | test.c:6:17:6:18 | l1 | side effect | test.c:6:17:6:18 | l1 | l1 | | ||
| test.c:7:12:7:18 | ... + ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:7:12:7:13 | l1 | side effect | test.c:7:12:7:13 | l1 | l1 | test.c:7:17:7:18 | l2 | side effect | test.c:7:17:7:18 | l2 | l2 | | ||
| test.c:17:3:17:21 | ... = ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:17:8:17:9 | l1 | side effect | test.c:17:8:17:9 | l1 | l1 | test.c:17:13:17:14 | l1 | side effect | test.c:17:13:17:14 | l1 | l1 | | ||
| test.c:19:3:19:5 | call to foo | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:19:7:19:8 | l1 | side effect | test.c:19:7:19:8 | l1 | l1 | test.c:19:11:19:12 | l2 | side effect | test.c:19:11:19:12 | l2 | l2 | | ||
| test.c:25:3:25:5 | call to foo | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:25:7:25:10 | ... ++ | side effect | test.c:25:7:25:8 | l8 | l8 | test.c:25:13:25:14 | l8 | read | test.c:25:13:25:14 | l8 | l8 | | ||
| test.c:35:5:35:13 | ... = ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:35:10:35:12 | ... ++ | side effect | test.c:35:10:35:10 | i | i | test.c:35:10:35:12 | ... ++ | side effect | test.c:35:10:35:10 | i | i | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
rules/RULE-13-2/UnsequencedSideEffects.ql |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.