From 12e6bb387a2f442836e096d79417f8595f790094 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Thu, 19 Feb 2026 15:58:25 -0800 Subject: [PATCH 01/10] Refactor Ordering from abstract classes to parametric module. Allows for code sharing between C and C++ even if the sequencing is different. Initial implementation of split between c++14 and c++17 sequencing. --- c/common/src/codingstandards/c/Ordering.qll | 159 ++++++------- .../VariableAccessOrdering.qll | 7 +- .../rules/RULE-13-2/UnsequencedAtomicReads.ql | 14 +- .../rules/RULE-13-2/UnsequencedSideEffects.ql | 28 ++- ...ressionShouldNotRelyOnOrderOfEvaluation.ql | 7 +- .../src/codingstandards/cpp/Ordering.qll | 222 ++++++++++-------- .../VariableAccessOrdering.qll | 11 +- 7 files changed, 241 insertions(+), 207 deletions(-) diff --git a/c/common/src/codingstandards/c/Ordering.qll b/c/common/src/codingstandards/c/Ordering.qll index 575dc6f3fd..0f3bcfbe12 100644 --- a/c/common/src/codingstandards/c/Ordering.qll +++ b/c/common/src/codingstandards/c/Ordering.qll @@ -1,97 +1,84 @@ import cpp -import codingstandards.cpp.SideEffect import codingstandards.c.Expr import codingstandards.cpp.Variable module Ordering { - abstract class Configuration extends string { - bindingset[this] - Configuration() { any() } + private import codingstandards.cpp.Ordering as CppCommonOrdering + import CppCommonOrdering::OrderingBase - abstract predicate isCandidate(Expr e1, Expr e2); + module CConfigBase { + class EvaluationNode = Expr; - /** - * Holds if `e1` is sequenced before `e2` as defined by Annex C in ISO/IEC 9899:2011 - * This limits to expression and we do not consider the sequence points that are not amenable to modelling: - * - before a library function returns (see 7.1.4 point 3). - * - after the actions associated with each formatted I/O function conversion specifier (see 7.21.6 point 1 & 7.29.2 point 1). - * - between the expr before and after a call to a comparison function, - * between any call to a comparison function, and any movement of the objects passed - * as arguments to that call (see 7.22.5 point 5). - */ - predicate isSequencedBefore(Expr e1, Expr e2) { - isCandidate(e1, e2) and - not e1 = e2 and + pragma[inline] + Expr toExpr(Expr e) { result = e } + + pragma[inline] + predicate sequencingEdge(Expr e1, Expr e2) { c11Ordering(e1, e2) } + } + + pragma[inline] + predicate c11Ordering(Expr e1, Expr e2) { + // 6.5.2.2 point 10 - The evaluation of the function designator and the actual arguments are sequenced + // before the actual call. + exists(Call call | ( - // 6.5.2.2 point 10 - The evaluation of the function designator and the actual arguments are sequenced - // before the actual call. - exists(Call call | - ( - call.getAnArgument().getAChild*() = e1 - or - // Postfix expression designating the called function - // We current only handle call through function pointers because the postfix expression - // of regular function calls is not available. That is, identifying `f` in `f(...)` - call.(ExprCall).getExpr().getAChild*() = e1 - ) and - call.getTarget() = e2.getEnclosingFunction() - ) - or - // 6.5.13 point 4 & 6.5.14 point 4 - The operators guarantee left-to-right evaluation and there is - // a sequence point between the first and second operand if the latter is evaluated. - exists(BinaryLogicalOperation blop | - blop instanceof LogicalAndExpr or blop instanceof LogicalOrExpr - | - blop.getLeftOperand().getAChild*() = e1 and blop.getRightOperand().getAChild*() = e2 - ) - or - // 6.5.17 point 2 - There is a sequence point between the left operand and the right operand. - exists(CommaExpr ce, Expr lhs, Expr rhs | - lhs = ce.getLeftOperand() and - rhs = ce.getRightOperand() - | - lhs.getAChild*() = e1 and rhs.getAChild*() = e2 - ) + call.getAnArgument().getAChild*() = e1 or - // 6.5.15 point 4 - There is a sequence point between the first operand and the evaluation of the second or third. - exists(ConditionalExpr cond | - cond.getCondition().getAChild*() = e1 and - (cond.getThen().getAChild*() = e2 or cond.getElse().getAChild*() = e2) - ) - or - // Between the evaluation of a full expression and the next to be evaluated full expression. - // Note we don't strictly check if `e2` is the next to be evaluated full expression and rely on the - // `isCandidate` configuration to minimze the scope or related full expressions. - e1 instanceof FullExpr and e2 instanceof FullExpr - or - // The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. - // See 6.5.16 - e2.(Assignment).getAnOperand().getAChild*() = e1 - or - // There is a sequence point after a full declarator as described in 6.7.6 point 3. - exists(DeclStmt declStmt, int i, int j | i < j | - declStmt - .getDeclarationEntry(i) - .(VariableDeclarationEntry) - .getVariable() - .getInitializer() - .getExpr() - .getAChild*() = e1 and - declStmt - .getDeclarationEntry(j) - .(VariableDeclarationEntry) - .getVariable() - .getInitializer() - .getExpr() - .getAChild*() = e2 - ) - ) - } - - predicate isUnsequenced(Expr e1, Expr e2) { - isCandidate(e1, e2) and - not isSequencedBefore(e1, e2) and - not isSequencedBefore(e2, e1) - } + // Postfix expression designating the called function + // We current only handle call through function pointers because the postfix expression + // of regular function calls is not available. That is, identifying `f` in `f(...)` + call.(ExprCall).getExpr().getAChild*() = e1 + ) and + call.getTarget() = e2.getEnclosingFunction() + ) + or + // 6.5.13 point 4 & 6.5.14 point 4 - The operators guarantee left-to-right evaluation and there is + // a sequence point between the first and second operand if the latter is evaluated. + exists(BinaryLogicalOperation blop | + blop instanceof LogicalAndExpr or blop instanceof LogicalOrExpr + | + blop.getLeftOperand().getAChild*() = e1 and blop.getRightOperand().getAChild*() = e2 + ) + or + // 6.5.17 point 2 - There is a sequence point between the left operand and the right operand. + exists(CommaExpr ce, Expr lhs, Expr rhs | + lhs = ce.getLeftOperand() and + rhs = ce.getRightOperand() + | + lhs.getAChild*() = e1 and rhs.getAChild*() = e2 + ) + or + // 6.5.15 point 4 - There is a sequence point between the first operand and the evaluation of the second or third. + exists(ConditionalExpr cond | + cond.getCondition().getAChild*() = e1 and + (cond.getThen().getAChild*() = e2 or cond.getElse().getAChild*() = e2) + ) + or + // Between the evaluation of a full expression and the next to be evaluated full expression. + // Note we don't strictly check if `e2` is the next to be evaluated full expression and rely on the + // `isCandidate` configuration to minimze the scope or related full expressions. + e1 instanceof FullExpr and e2 instanceof FullExpr + or + // The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. + // See 6.5.16 + e2.(Assignment).getAnOperand().getAChild*() = e1 + or + // There is a sequence point after a full declarator as described in 6.7.6 point 3. + exists(DeclStmt declStmt, int i, int j | i < j | + declStmt + .getDeclarationEntry(i) + .(VariableDeclarationEntry) + .getVariable() + .getInitializer() + .getExpr() + .getAChild*() = e1 and + declStmt + .getDeclarationEntry(j) + .(VariableDeclarationEntry) + .getVariable() + .getInitializer() + .getExpr() + .getAChild*() = e2 + ) } } diff --git a/c/common/src/codingstandards/c/orderofevaluation/VariableAccessOrdering.qll b/c/common/src/codingstandards/c/orderofevaluation/VariableAccessOrdering.qll index 4c041e8e4c..1538f16c4f 100644 --- a/c/common/src/codingstandards/c/orderofevaluation/VariableAccessOrdering.qll +++ b/c/common/src/codingstandards/c/orderofevaluation/VariableAccessOrdering.qll @@ -1,10 +1,11 @@ import cpp import codingstandards.c.Ordering +import codingstandards.c.SideEffects -class VariableAccessInFullExpressionOrdering extends Ordering::Configuration { - VariableAccessInFullExpressionOrdering() { this = "VariableAccessInFullExpressionOrdering" } +module VariableAccessInFullExpressionOrdering implements Ordering::ConfigSig { + import Ordering::CConfigBase - override predicate isCandidate(Expr e1, Expr e2) { isCandidate(_, e1, e2) } + predicate isCandidate(Expr e1, Expr e2) { isCandidate(_, e1, e2) } } pragma[noinline] diff --git a/c/misra/src/rules/RULE-13-2/UnsequencedAtomicReads.ql b/c/misra/src/rules/RULE-13-2/UnsequencedAtomicReads.ql index 86756668a8..012b4877c5 100644 --- a/c/misra/src/rules/RULE-13-2/UnsequencedAtomicReads.ql +++ b/c/misra/src/rules/RULE-13-2/UnsequencedAtomicReads.ql @@ -19,10 +19,10 @@ import codingstandards.c.Ordering import codingstandards.c.orderofevaluation.VariableAccessOrdering import codingstandards.cpp.StdFunctionOrMacro -class AtomicAccessInFullExpressionOrdering extends Ordering::Configuration { - AtomicAccessInFullExpressionOrdering() { this = "AtomicAccessInFullExpressionOrdering" } +module AtomicAccessInFullExpressionOrdering implements Ordering::ConfigSig { + import Ordering::CConfigBase - override predicate isCandidate(Expr e1, Expr e2) { + predicate isCandidate(Expr e1, Expr e2) { exists(AtomicVariableAccess a, AtomicVariableAccess b, FullExpr e | a = e1 and b = e2 | a.getTarget() = b.getTarget() and a.getARead().(ConstituentExpr).getFullExpr() = e and @@ -91,9 +91,11 @@ class AtomicVariableAccess extends VariableAccess { } } +import Ordering::Make as AtomicOrdering + from - AtomicAccessInFullExpressionOrdering config, FullExpr e, Variable v, AtomicVariableAccess va1, - AtomicVariableAccess va2, Expr va1Read, Expr va2Read + FullExpr e, Variable v, AtomicVariableAccess va1, AtomicVariableAccess va2, Expr va1Read, + Expr va2Read where not isExcluded(e, SideEffects3Package::unsequencedAtomicReadsQuery()) and va1Read = va1.getARead() and @@ -103,7 +105,7 @@ where // for instance in gcc where atomic functions expand to StmtExprs, which have clear sequences. // In this case, the result of `getARead()` for a pair of atomic function calls may be // unsequenced even though the `VariableAccess`es within those calls are not. - config.isUnsequenced(va1Read, va2Read) and + AtomicOrdering::isUnsequenced(va1Read, va2Read) and v = va1.getTarget() and v = va2.getTarget() and // Exclude cases where the variable is assigned a value tainted by the other variable access. diff --git a/c/misra/src/rules/RULE-13-2/UnsequencedSideEffects.ql b/c/misra/src/rules/RULE-13-2/UnsequencedSideEffects.ql index 90b0315e88..900785151c 100644 --- a/c/misra/src/rules/RULE-13-2/UnsequencedSideEffects.ql +++ b/c/misra/src/rules/RULE-13-2/UnsequencedSideEffects.ql @@ -33,10 +33,10 @@ predicate partOfFullExpr(VariableEffectOrAccess e, FullExpr fe) { ) } -class ConstituentExprOrdering extends Ordering::Configuration { - ConstituentExprOrdering() { this = "ConstituentExprOrdering" } +module ConstituentExprOrderingConfig implements Ordering::ConfigSig { + import Ordering::CConfigBase - override predicate isCandidate(Expr e1, Expr e2) { + predicate isCandidate(Expr e1, Expr e2) { exists(FullExpr fe | partOfFullExpr(e1, fe) and partOfFullExpr(e2, fe) @@ -172,9 +172,11 @@ predicate inConditionalElse(ConditionalExpr ce, Expr e) { ) } +import Ordering::Make as ConstituentExprOrdering + predicate isUnsequencedEffect( - ConstituentExprOrdering orderingConfig, FullExpr fullExpr, VariableEffect variableEffect1, - VariableAccess va1, VariableAccess va2, Locatable placeHolder, string label + FullExpr fullExpr, VariableEffect variableEffect1, VariableAccess va1, VariableAccess va2, + Locatable placeHolder, string label ) { // The two access are scoped to the same full expression. sameFullExpr(fullExpr, va1, va2) and @@ -190,14 +192,14 @@ predicate isUnsequencedEffect( ( va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and - orderingConfig.isUnsequenced(variableEffect1, variableEffect2) + ConstituentExprOrdering::isUnsequenced(variableEffect1, variableEffect2) or va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and not va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and exists(Call call | call.getAnArgument() = va2 and call.getEnclosingStmt() = va1.getEnclosingStmt() | - orderingConfig.isUnsequenced(variableEffect1, call) + ConstituentExprOrdering::isUnsequenced(variableEffect1, call) ) or not va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and @@ -205,7 +207,7 @@ predicate isUnsequencedEffect( exists(Call call | call.getAnArgument() = va1 and call.getEnclosingStmt() = va2.getEnclosingStmt() | - orderingConfig.isUnsequenced(call, variableEffect2) + ConstituentExprOrdering::isUnsequenced(call, variableEffect2) ) ) and // Break the symmetry of the ordering relation by requiring that the first expression is located before the second. @@ -222,13 +224,13 @@ predicate isUnsequencedEffect( ) and ( va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and - orderingConfig.isUnsequenced(variableEffect1, va2) + ConstituentExprOrdering::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) + ConstituentExprOrdering::isUnsequenced(call, va2) ) ) and // The read is not used to compute the effect on the variable. @@ -240,10 +242,10 @@ predicate isUnsequencedEffect( } from - ConstituentExprOrdering orderingConfig, FullExpr fullExpr, VariableEffect variableEffect1, - VariableAccess va1, VariableAccess va2, Locatable placeHolder, string label + FullExpr fullExpr, VariableEffect variableEffect1, VariableAccess va1, VariableAccess va2, + Locatable placeHolder, string label where not isExcluded(fullExpr, SideEffects3Package::unsequencedSideEffectsQuery()) and - isUnsequencedEffect(orderingConfig, fullExpr, variableEffect1, va1, va2, placeHolder, label) + isUnsequencedEffect(fullExpr, variableEffect1, va1, va2, placeHolder, label) select fullExpr, "The expression contains unsequenced $@ to $@ and $@ to $@.", variableEffect1, "side effect", va1, va1.getTarget().getName(), placeHolder, label, va2, va2.getTarget().getName() diff --git a/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql b/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql index 656613003e..997f5bc260 100644 --- a/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql +++ b/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql @@ -19,15 +19,14 @@ import codingstandards.cpp.SideEffect import codingstandards.cpp.sideeffect.DefaultEffects import codingstandards.cpp.Ordering import codingstandards.cpp.orderofevaluation.VariableAccessOrdering +import Ordering::Make as FullExprOrdering -from - VariableAccessInFullExpressionOrdering config, FullExpr e, VariableEffect ve, VariableAccess va1, - VariableAccess va2, Variable v +from FullExpr e, VariableEffect ve, VariableAccess va1, VariableAccess va2, Variable v where not isExcluded(e, OrderOfEvaluationPackage::expressionShouldNotRelyOnOrderOfEvaluationQuery()) and e = va1.(ConstituentExpr).getFullExpr() and va1 = ve.getAnAccess() and - config.isUnsequenced(va1, va2) and + FullExprOrdering::isUnsequenced(va1, va2) and v = va1.getTarget() select e, "The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@.", diff --git a/cpp/common/src/codingstandards/cpp/Ordering.qll b/cpp/common/src/codingstandards/cpp/Ordering.qll index 8f33e15b59..7cd7bbc357 100644 --- a/cpp/common/src/codingstandards/cpp/Ordering.qll +++ b/cpp/common/src/codingstandards/cpp/Ordering.qll @@ -2,118 +2,156 @@ import cpp import codingstandards.cpp.Expr import codingstandards.cpp.SideEffect -module Ordering { - abstract class Configuration extends string { - bindingset[this] - Configuration() { any() } +module OrderingBase { + signature module ConfigSig { + class EvaluationNode; + + Expr toExpr(EvaluationNode n); - abstract predicate isCandidate(Expr e1, Expr e2); + predicate isCandidate(Expr e1, Expr e2); + + predicate sequencingEdge(EvaluationNode n1, EvaluationNode n2); + } + module Make { predicate isSequencedBefore(Expr e1, Expr e2) { - exists(ExprEvaluationNode n1, ExprEvaluationNode n2 | n1.toExpr() = e1 and n2.toExpr() = e2 | - this.edge(n1, n2) + exists(Config::EvaluationNode n1, Config::EvaluationNode n2 | + Config::toExpr(n1) = e1 and Config::toExpr(n2) = e2 + | + edge(n1, n2) ) } predicate isUnsequenced(Expr e1, Expr e2) { - isCandidate(e1, e2) and + Config::isCandidate(e1, e2) and not isSequencedBefore(e1, e2) and not isSequencedBefore(e2, e1) } - private predicate edge(ExprEvaluationNode n1, ExprEvaluationNode n2) { - isCandidate(n1.toExpr(), n2.toExpr()) and + private predicate edge(Config::EvaluationNode n1, Config::EvaluationNode n2) { + Config::isCandidate(Config::toExpr(n1), Config::toExpr(n2)) and not n1 = n2 and + Config::sequencingEdge(n1, n2) + } + } +} + +module Ordering { + import OrderingBase + + module CppConfigBase { + class EvaluationNode = ExprEvaluationNode; + + pragma[inline] + Expr toExpr(EvaluationNode n) { result = n.toExpr() } + } + + pragma[inline] + predicate cpp14Edge(ExprEvaluationNode n1, ExprEvaluationNode n2) { + // [intro.execution] - Each value computation and side effect of a full expression is sequenced + // before each value computation and side effect of the next full expression. + exists(FullExpr e1, FullExpr e2 | e1 = n1.toExpr() and e2 = n2.toExpr()) + or + // [intro.execution] - The value computations of the operands to any operator are sequenced before + // the value computation of the result of the operator. + exists(Operation op | + op.getAnOperand() = n1.toExpr() and + op = n2.toExpr() and + n1.isValueComputation() and + n2.isValueComputation() + ) + or + // [intro.execution] - Every value computation and side effect associated with any argument + // expression, or with the postfix expression designating the called function, of a function call + // is sequenced before every expression or statement in the body of the called function. + exists(Call call | ( - // [intro.execution] - Each value computation and side effect of a full expression is sequenced - // before each value computation and side effect of the next full expression. - exists(FullExpr e1, FullExpr e2 | e1.getASuccessor() = e2 | - e1 = n1.toExpr() and e2 = n2.toExpr() - ) + call.getAnArgument() = n1.toExpr() or - // [intro.execution] - The value computations of the operands to any operator are sequenced before - // the value computation of the result of the operator. - exists(Operation op | - op.getAnOperand() = n1.toExpr() and - op = n2.toExpr() and - n1.isValueComputation() and - n2.isValueComputation() - ) - or - // [intro.execution] - Every value computation and side effect associated with any argument - // expression, or with the postfix expression designating the called function, of a function call - // is sequenced before every expression or statement in the body of the called function. - exists(Call call | - ( - call.getAnArgument() = n1.toExpr() - or - // Postfix expression designating the called function - // We current only handle call through function pointers because the postfix expression - // of regular function calls is not available. That is, identifying `f` in `f(...)` - call.(ExprCall).getExpr() = n1.toExpr() - ) and - call.getTarget() = n2.toExpr().getEnclosingFunction() - ) - or - // [expr.post.incr] - The value computation of the ++ expression is sequenced before the modification of the operand object. - exists(PostfixCrementOperation op | - op = n1.toExpr() and op = n2.toExpr() and n1.isValueComputation() and n2.isSideEffect() - ) - or - // The side effect of the builtin pre-increment and pre-decrement operators is sequenced before its value computation. - exists(PrefixCrementOperation op | - op = n1.toExpr() and op = n2.toExpr() and n1.isSideEffect() and n2.isValueComputation() - ) - or - // [expr.log.and] - If the second expression is evaluated, every value computation and side effect associated with - // the first expression is sequenced before every value computation and side effect associated with - // the second exression. - exists(LogicalAndExpr land | - land.getLeftOperand() = n1.toExpr() and land.getRightOperand() = n2.toExpr() - ) - or - // [expr.log.or] - If the second expression is evaluated, every value computation and side effect associated with - // the first expression is sequenced before every value computation and side effect associated with - // the second exression - exists(LogicalOrExpr lor | - lor.getLeftOperand() = n1.toExpr() and lor.getRightOperand() = n2.toExpr() - ) - or - // [expr.cond] - Every value computation and side effect associated with the first expression is sequenced before - // every value computation and side effect associated with the second or third expression. - exists(ConditionalExpr cond | - cond.getCondition() = n1.toExpr() and - (cond.getThen() = n2.toExpr() or cond.getElse() = n2.toExpr()) - ) - or - // [expr.comma] - Every value computation and side effect assocatiated with the left expression - // is sequenced before every value computation and side effect associated with the right expression. - exists(CommaExpr ce, ConstituentExpr lhs, ConstituentExpr rhs | - lhs = ce.getLeftOperand() and - rhs = ce.getRightOperand() - | - lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() - ) - or - // [dcl.init.list] - Every value computation and side effect associated with any initializer-clause - // is sequenced before every value computation and side effect associaed with any initializer-clause - // that follows it in the comma-separated list of the initializer-list - exists(AggregateLiteral l, ConstituentExpr lhs, ConstituentExpr rhs, int i, int j | - lhs = l.getChild(i) and - rhs = l.getChild(j) and - i < j - | - lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() - ) - ) - } + // Postfix expression designating the called function + // We current only handle call through function pointers because the postfix expression + // of regular function calls is not available. That is, identifying `f` in `f(...)` + call.(ExprCall).getExpr() = n1.toExpr() + ) and + call.getTarget() = n2.toExpr().getEnclosingFunction() + ) + or + // [expr.post.incr] - The value computation of the ++ expression is sequenced before the modification of the operand object. + exists(PostfixCrementOperation op | + op = n1.toExpr() and op = n2.toExpr() and n1.isValueComputation() and n2.isSideEffect() + ) + or + // The side effect of the builtin pre-increment and pre-decrement operators is sequenced before its value computation. + exists(PrefixCrementOperation op | + op = n1.toExpr() and op = n2.toExpr() and n1.isSideEffect() and n2.isValueComputation() + ) + or + // [expr.log.and] - If the second expression is evaluated, every value computation and side effect associated with + // the first expression is sequenced before every value computation and side effect associated with + // the second exression. + exists(LogicalAndExpr land | + land.getLeftOperand() = n1.toExpr() and land.getRightOperand() = n2.toExpr() + ) + or + // [expr.log.or] - If the second expression is evaluated, every value computation and side effect associated with + // the first expression is sequenced before every value computation and side effect associated with + // the second exression + exists(LogicalOrExpr lor | + lor.getLeftOperand() = n1.toExpr() and lor.getRightOperand() = n2.toExpr() + ) + or + // [expr.cond] - Every value computation and side effect associated with the first expression is sequenced before + // every value computation and side effect associated with the second or third expression. + exists(ConditionalExpr cond | + cond.getCondition() = n1.toExpr() and + (cond.getThen() = n2.toExpr() or cond.getElse() = n2.toExpr()) + ) + or + // [expr.comma] - Every value computation and side effect assocatiated with the left expression + // is sequenced before every value computation and side effect associated with the right expression. + exists(CommaExpr ce, ConstituentExpr lhs, ConstituentExpr rhs | + lhs = ce.getLeftOperand() and + rhs = ce.getRightOperand() + | + lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() + ) + or + // [dcl.init.list] - Every value computation and side effect associated with any initializer-clause + // is sequenced before every value computation and side effect associaed with any initializer-clause + // that follows it in the comma-separated list of the initializer-list + exists(AggregateLiteral l, ConstituentExpr lhs, ConstituentExpr rhs, int i, int j | + lhs = l.getChild(i) and + rhs = l.getChild(j) and + i < j + | + lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() + ) + } + + pragma[inline] + predicate cpp17Edge(ExprEvaluationNode n1, ExprEvaluationNode n2) { + cpp14Edge(n1, n2) + or + // [expr.sub] - (in) the expression E1[E2] ... E1 is sequenced before E2 + exists(ArrayExpr ce, ConstituentExpr lhs, ConstituentExpr rhs | + lhs = ce.getArrayBase() and + rhs = ce.getArrayOffset() + | + lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() + ) + or + // [expr.sub] - (in) the expression E1[E2] ... E1 is sequenced before E2 + exists(NewExpr newExpr, ConstituentExpr lhs, ConstituentExpr rhs | + n1.toExpr() = newExpr.getAllocatorCall() and + n2.toExpr() = newExpr.getInitializer().getAChild() + ) } private newtype TExprEvaluationNode = TValueComputationNode(Expr e) or TSideEffectNode(Expr e, SideEffect s) { s = getAnEffect(e) } - private class ExprEvaluationNode extends TExprEvaluationNode { + class ExprEvaluationNode extends TExprEvaluationNode { Expr toExpr() { this = TValueComputationNode(result) or diff --git a/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll b/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll index 5c33e02ffd..002501b8f7 100644 --- a/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll +++ b/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll @@ -1,10 +1,15 @@ import cpp import codingstandards.cpp.Ordering -class VariableAccessInFullExpressionOrdering extends Ordering::Configuration { - VariableAccessInFullExpressionOrdering() { this = "VariableAccessInFullExpressionOrdering" } +module VariableAccessInFullExpressionOrdering implements Ordering::ConfigSig { + import Ordering::CppConfigBase - override predicate isCandidate(Expr e1, Expr e2) { isCandidate(_, e1, e2) } + predicate isCandidate(Expr e1, Expr e2) { isCandidate(_, e1, e2) } + + pragma[inline] + predicate sequencingEdge(Ordering::ExprEvaluationNode n1, Ordering::ExprEvaluationNode n2) { + Ordering::cpp14Edge(n1, n2) + } } pragma[noinline] From c607e3a348e171d0b68b3879121d38e7f0406a66 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Thu, 19 Feb 2026 16:04:54 -0800 Subject: [PATCH 02/10] Update rules.csv, initial package description --- rule_packages/cpp/SideEffects4.json | 27 +++++++++++++++++++++++++++ rules.csv | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 rule_packages/cpp/SideEffects4.json diff --git a/rule_packages/cpp/SideEffects4.json b/rule_packages/cpp/SideEffects4.json new file mode 100644 index 0000000000..865a0dcdfa --- /dev/null +++ b/rule_packages/cpp/SideEffects4.json @@ -0,0 +1,27 @@ +{ + "MISRA-C++-2023": { + "RULE-4-6-1": { + "properties": { + "enforcement": "undecidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Code behavior should not have unsequenced or indeterminately sequenced operations on the same memory location.", + "kind": "problem", + "name": "Operations on a memory location shall be sequenced appropriately", + "precision": "very-high", + "severity": "error", + "short_name": "MemoryUsageNotSequenced", + "tags": [ + "scope/system", + "portability", + "correctness", + "maintainability" + ] + } + ], + "title": "Operations on a memory location shall be sequenced appropriately" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index dcc5ee5c5f..2c48804f3b 100644 --- a/rules.csv +++ b/rules.csv @@ -836,7 +836,7 @@ cpp,MISRA-C++-2023,DIR-0-3-2,Yes,Required,,,A function call shall not violate th cpp,MISRA-C++-2023,RULE-4-1-1,Yes,Required,Undecidable,System,A program shall conform to ISO/IEC 14882:2017 (C++17),,Toolchain2,Hard, cpp,MISRA-C++-2023,RULE-4-1-2,Yes,Advisory,Decidable,Single Translation Unit,Deprecated features should not be used,,Toolchain2,Very Hard, cpp,MISRA-C++-2023,RULE-4-1-3,Yes,Required,Undecidable,System,There shall be no occurrence of undefined or critical unspecified behaviour,,Undefined,Very Hard, -cpp,MISRA-C++-2023,RULE-4-6-1,Yes,Required,Undecidable,System,Operations on a memory location shall be sequenced appropriately,RULE-13-2,SideEffects3,Easy, +cpp,MISRA-C++-2023,RULE-4-6-1,Yes,Required,Undecidable,System,Operations on a memory location shall be sequenced appropriately,RULE-13-2,SideEffects4,Easy, cpp,MISRA-C++-2023,RULE-5-0-1,Yes,Advisory,Decidable,Single Translation Unit,Trigraph-like sequences should not be used,A2-5-1,Trigraph,Very Hard, cpp,MISRA-C++-2023,RULE-5-7-1,Yes,Required,Decidable,Single Translation Unit,The character sequence /* shall not be used within a C-style comment,M2-7-1,ImportMisra23,Import, cpp,MISRA-C++-2023,DIR-5-7-2,Yes,Advisory,,,Sections of code should not be “commented out”,A2-7-2,ImportMisra23,Import, From 01b605b472b31b79cbcac39d39803d211daa6980 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 20 Feb 2026 16:56:31 -0800 Subject: [PATCH 03/10] Implement Rule 4-6-1, memory operations should be properly sequenced. Split configs for C++14 and C++17 sequencing. Fix bug in reciprocity. --- ...ressionShouldNotRelyOnOrderOfEvaluation.ql | 28 ++++++----- .../src/codingstandards/cpp/Ordering.qll | 49 +++++++++++++++++-- .../cpp/exclusions/cpp/RuleMetadata.qll | 3 ++ .../cpp/exclusions/cpp/SideEffects4.qll | 26 ++++++++++ .../VariableAccessOrdering.qll | 16 +++++- .../ExpressionWithUnsequencedSideEffects.qll | 38 ++++++++++++++ ...ressionWithUnsequencedSideEffects.expected | 12 +++++ .../ExpressionWithUnsequencedSideEffects.ql | 14 ++++++ ...onWithUnsequencedSideEffectsCpp17.expected | 6 +++ ...pressionWithUnsequencedSideEffectsCpp17.ql | 14 ++++++ .../test.cpp | 24 +++++++-- .../RULE-4-6-1/MemoryUsageNotSequenced.ql | 33 +++++++++++++ .../MemoryUsageNotSequenced.testref | 1 + rule_packages/cpp/OrderOfEvaluation.json | 1 + rule_packages/cpp/SideEffects4.json | 1 + 15 files changed, 243 insertions(+), 23 deletions(-) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects4.qll create mode 100644 cpp/common/src/codingstandards/cpp/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.qll create mode 100644 cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.expected create mode 100644 cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.ql create mode 100644 cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.expected create mode 100644 cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.ql rename cpp/{autosar/test/rules/A5-0-1 => common/test/rules/expressionwithunsequencedsideeffects}/test.cpp (58%) create mode 100644 cpp/misra/src/rules/RULE-4-6-1/MemoryUsageNotSequenced.ql create mode 100644 cpp/misra/test/rules/RULE-4-6-1/MemoryUsageNotSequenced.testref diff --git a/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql b/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql index 997f5bc260..c98d5a8b55 100644 --- a/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql +++ b/cpp/autosar/src/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql @@ -15,19 +15,21 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.SideEffect -import codingstandards.cpp.sideeffect.DefaultEffects +import codingstandards.cpp.rules.expressionwithunsequencedsideeffects.ExpressionWithUnsequencedSideEffects import codingstandards.cpp.Ordering import codingstandards.cpp.orderofevaluation.VariableAccessOrdering -import Ordering::Make as FullExprOrdering +import Ordering::Make as FullExprOrdering -from FullExpr e, VariableEffect ve, VariableAccess va1, VariableAccess va2, Variable v -where - not isExcluded(e, OrderOfEvaluationPackage::expressionShouldNotRelyOnOrderOfEvaluationQuery()) and - e = va1.(ConstituentExpr).getFullExpr() and - va1 = ve.getAnAccess() and - FullExprOrdering::isUnsequenced(va1, va2) and - v = va1.getTarget() -select e, - "The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@.", - va1, "sub-expression", ve, "expression", va2, "sub-expression", v, v.getName() +module ExpressionShouldNotRelyOnOrderOfEvaluationConfig implements + ExpressionWithUnsequencedSideEffectsConfigSig +{ + Query getQuery() { + result = OrderOfEvaluationPackage::expressionShouldNotRelyOnOrderOfEvaluationQuery() + } + + predicate isUnsequenced(VariableAccess va1, VariableAccess va2) { + FullExprOrdering::isUnsequenced(va1, va2) + } +} + +import ExpressionWithUnsequencedSideEffects diff --git a/cpp/common/src/codingstandards/cpp/Ordering.qll b/cpp/common/src/codingstandards/cpp/Ordering.qll index 7cd7bbc357..94b0b51062 100644 --- a/cpp/common/src/codingstandards/cpp/Ordering.qll +++ b/cpp/common/src/codingstandards/cpp/Ordering.qll @@ -132,6 +132,15 @@ module Ordering { predicate cpp17Edge(ExprEvaluationNode n1, ExprEvaluationNode n2) { cpp14Edge(n1, n2) or + // [expr.call] The "postfix expression" (the part before the `(...)`) is sequenced before each + // expression in the expression list. + exists(Call call, Expr qual, Expr arg | + call.getQualifier() = qual and + call.getAnArgument() = arg + | + qual = n1.toExpr().getParent*() and arg = n2.toExpr().getParent*() + ) + or // [expr.sub] - (in) the expression E1[E2] ... E1 is sequenced before E2 exists(ArrayExpr ce, ConstituentExpr lhs, ConstituentExpr rhs | lhs = ce.getArrayBase() and @@ -140,10 +149,39 @@ module Ordering { lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() ) or - // [expr.sub] - (in) the expression E1[E2] ... E1 is sequenced before E2 - exists(NewExpr newExpr, ConstituentExpr lhs, ConstituentExpr rhs | - n1.toExpr() = newExpr.getAllocatorCall() and - n2.toExpr() = newExpr.getInitializer().getAChild() + // [expr.new] -- invocation of the allocation function is sequenced before the expressions in + // the new-initializer. + exists(NewExpr newExpr, ConstituentExpr alloc, ConstituentExpr arg | + alloc = newExpr.getAllocatorCall() and + arg = newExpr.getInitializer().getAChild() + | + alloc = n1.toExpr().getParent*() and + arg = n2.toExpr().getParent*() + ) + or + // [expr.mptr.oper] - In E.*E2, E1 is sequenced before E2. This is not the case for E->*E2. + exists(PointerToMemberExpr ptrToMember, ConstituentExpr object, ConstituentExpr ptr | + // TODO: distinguish between `.*` and `->*` operators. + object = ptrToMember.getObjectExpr() and + ptr = ptrToMember.getPointerExpr() + | + object = n1.toExpr().getParent*() and ptr = n2.toExpr().getParent*() + ) + or + // [expr.shift] In E1 << E2 and E1 >> E2, E1 is sequenced before E2. + exists(BitShiftExpr shift, ConstituentExpr lhs, ConstituentExpr rhs | + lhs = shift.getLeftOperand() and + rhs = shift.getRightOperand() + | + lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() + ) + or + // [expr.ass] The right operand is sequenced before the left operand for all assignment operators. + exists(Assignment assign, ConstituentExpr lhs, ConstituentExpr rhs | + lhs = assign.getLValue() and + rhs = assign.getRValue() + | + lhs = n1.toExpr().getParent*() and rhs = n2.toExpr().getParent*() ) } @@ -172,5 +210,8 @@ module Ordering { predicate isValueComputation() { this = TValueComputationNode(_) } predicate isSideEffect() { this = TSideEffectNode(_, _) } + + Location getLocation() { result = toExpr().getLocation() } } + } diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index cad86d2285..6a4480bd9b 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -60,6 +60,7 @@ import Representation import Scope import SideEffects1 import SideEffects2 +import SideEffects4 import SmartPointers1 import SmartPointers2 import Statements @@ -132,6 +133,7 @@ newtype TCPPQuery = TScopePackageQuery(ScopeQuery q) or TSideEffects1PackageQuery(SideEffects1Query q) or TSideEffects2PackageQuery(SideEffects2Query q) or + TSideEffects4PackageQuery(SideEffects4Query q) or TSmartPointers1PackageQuery(SmartPointers1Query q) or TSmartPointers2PackageQuery(SmartPointers2Query q) or TStatementsPackageQuery(StatementsQuery q) or @@ -204,6 +206,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isScopeQueryMetadata(query, queryId, ruleId, category) or isSideEffects1QueryMetadata(query, queryId, ruleId, category) or isSideEffects2QueryMetadata(query, queryId, ruleId, category) or + isSideEffects4QueryMetadata(query, queryId, ruleId, category) or isSmartPointers1QueryMetadata(query, queryId, ruleId, category) or isSmartPointers2QueryMetadata(query, queryId, ruleId, category) or isStatementsQueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects4.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects4.qll new file mode 100644 index 0000000000..fb8ebc63d2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects4.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype SideEffects4Query = TMemoryUsageNotSequencedQuery() + +predicate isSideEffects4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `memoryUsageNotSequenced` query + SideEffects4Package::memoryUsageNotSequencedQuery() and + queryId = + // `@id` for the `memoryUsageNotSequenced` query + "cpp/misra/memory-usage-not-sequenced" and + ruleId = "RULE-4-6-1" and + category = "required" +} + +module SideEffects4Package { + Query memoryUsageNotSequencedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `memoryUsageNotSequenced` query + TQueryCPP(TSideEffects4PackageQuery(TMemoryUsageNotSequencedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll b/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll index 002501b8f7..09f6ef290a 100644 --- a/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll +++ b/cpp/common/src/codingstandards/cpp/orderofevaluation/VariableAccessOrdering.qll @@ -1,7 +1,7 @@ import cpp import codingstandards.cpp.Ordering -module VariableAccessInFullExpressionOrdering implements Ordering::ConfigSig { +module Cpp14VariableAccessInFullExpressionOrdering implements Ordering::ConfigSig { import Ordering::CppConfigBase predicate isCandidate(Expr e1, Expr e2) { isCandidate(_, e1, e2) } @@ -12,6 +12,17 @@ module VariableAccessInFullExpressionOrdering implements Ordering::ConfigSig { } } +module Cpp17VariableAccessInFullExpressionOrdering implements Ordering::ConfigSig { + import Ordering::CppConfigBase + + predicate isCandidate(Expr e1, Expr e2) { isCandidate(_, e1, e2) } + + pragma[inline] + predicate sequencingEdge(Ordering::ExprEvaluationNode n1, Ordering::ExprEvaluationNode n2) { + Ordering::cpp17Edge(n1, n2) + } +} + pragma[noinline] private predicate isConstituentOf(FullExpr e, VariableAccess ce) { ce.(ConstituentExpr).getFullExpr() = e @@ -35,6 +46,9 @@ predicate isCandidatePair( * an other full expression that is sequenced after the value computation (and therefore cannot influence them at that point in the evaluation). */ predicate isCandidate(FullExpr e, VariableAccess va1, VariableAccess va2) { + // Ensure va1 and va2 are reciprocal candidates. + isCandidate(e, va2, va1) + or exists(Variable v, VariableEffect ve | isCandidatePair(e, va1, va2, v, v) and ve.getAnAccess() = va1 and diff --git a/cpp/common/src/codingstandards/cpp/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.qll b/cpp/common/src/codingstandards/cpp/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.qll new file mode 100644 index 0000000000..e224fcb154 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.qll @@ -0,0 +1,38 @@ +/** + * Provides a configurable module ExpressionWithUnsequencedSideEffects with a `problems` predicate + * for the following issue: + * Code behavior should not have unsequenced or indeterminately sequenced operations on + * the same memory location. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.SideEffect +import codingstandards.cpp.sideeffect.DefaultEffects +import codingstandards.cpp.Ordering + +signature module ExpressionWithUnsequencedSideEffectsConfigSig { + Query getQuery(); + + predicate isUnsequenced(VariableAccess va1, VariableAccess va2); +} + +module ExpressionWithUnsequencedSideEffects { + query predicate problems( + FullExpr e, string message, VariableAccess va1, string va1Desc, VariableEffect ve, + string veDesc, VariableAccess va2, string va2Desc, Variable v, string vName + ) { + not isExcluded(e, Config::getQuery()) and + e = va1.(ConstituentExpr).getFullExpr() and + va1 = ve.getAnAccess() and + Config::isUnsequenced(va1, va2) and + v = va1.getTarget() and + message = + "The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@." and + va1Desc = "sub-expression" and + veDesc = "expression" and + va2Desc = "sub-expression" and + vName = v.getName() + } +} diff --git a/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.expected b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.expected new file mode 100644 index 0000000000..7d8242dff5 --- /dev/null +++ b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.expected @@ -0,0 +1,12 @@ +| test.cpp:5:12:5:24 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:5:21:5:22 | l1 | sub-expression | test.cpp:5:21:5:24 | ... ++ | expression | test.cpp:5:15:5:16 | l1 | sub-expression | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:13:12:13:13 | call to f2 | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:13:15:13:16 | l1 | sub-expression | test.cpp:13:15:13:18 | ... ++ | expression | test.cpp:13:21:13:22 | l1 | sub-expression | test.cpp:12:7:12:8 | l1 | l1 | +| test.cpp:21:7:21:8 | call to m1 | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:21:10:21:11 | p1 | sub-expression | test.cpp:21:10:21:13 | ... ++ | expression | test.cpp:21:3:21:4 | p1 | sub-expression | test.cpp:20:13:20:14 | p1 | p1 | +| test.cpp:26:29:26:53 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:26:34:26:35 | p1 | sub-expression | test.cpp:25:34:25:40 | ... += ... | expression | test.cpp:26:48:26:49 | p1 | sub-expression | test.cpp:26:14:26:15 | p1 | p1 | +| test.cpp:26:29:26:53 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:26:48:26:49 | p1 | sub-expression | test.cpp:25:34:25:40 | ... += ... | expression | test.cpp:26:34:26:35 | p1 | sub-expression | test.cpp:26:14:26:15 | p1 | p1 | +| test.cpp:32:3:32:16 | ... = ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:32:8:32:9 | l1 | sub-expression | test.cpp:32:8:32:16 | ... = ... | expression | test.cpp:32:13:32:14 | l1 | sub-expression | test.cpp:29:7:29:8 | l1 | l1 | +| test.cpp:32:3:32:16 | ... = ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:32:13:32:14 | l1 | sub-expression | test.cpp:32:13:32:16 | ... ++ | expression | test.cpp:32:8:32:9 | l1 | sub-expression | test.cpp:29:7:29:8 | l1 | l1 | +| test.cpp:39:12:39:18 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:39:12:39:13 | l1 | sub-expression | test.cpp:39:12:39:13 | l1 | expression | test.cpp:39:17:39:18 | l1 | sub-expression | test.cpp:37:16:37:17 | l1 | l1 | +| test.cpp:39:12:39:18 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:39:17:39:18 | l1 | sub-expression | test.cpp:39:17:39:18 | l1 | expression | test.cpp:39:12:39:13 | l1 | sub-expression | test.cpp:37:16:37:17 | l1 | l1 | +| test.cpp:62:11:62:12 | call to m1 | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:62:4:62:5 | p1 | sub-expression | test.cpp:62:4:62:7 | ... ++ | expression | test.cpp:62:14:62:15 | p1 | sub-expression | test.cpp:61:14:61:15 | p1 | p1 | +| test.cpp:67:3:67:20 | ... >> ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:67:5:67:6 | p1 | sub-expression | test.cpp:67:5:67:8 | ... ++ | expression | test.cpp:67:16:67:17 | p1 | sub-expression | test.cpp:66:15:66:16 | p1 | p1 | +| test.cpp:67:3:67:20 | ... >> ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:67:16:67:17 | p1 | sub-expression | test.cpp:67:16:67:19 | ... ++ | expression | test.cpp:67:5:67:6 | p1 | sub-expression | test.cpp:66:15:66:16 | p1 | p1 | diff --git a/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.ql b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.ql new file mode 100644 index 0000000000..596d890f30 --- /dev/null +++ b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.ql @@ -0,0 +1,14 @@ +import codingstandards.cpp.rules.expressionwithunsequencedsideeffects.ExpressionWithUnsequencedSideEffects +import codingstandards.cpp.Ordering +import codingstandards.cpp.orderofevaluation.VariableAccessOrdering +import Ordering::Make as FullExprOrdering + +module TestFileConfig implements ExpressionWithUnsequencedSideEffectsConfigSig { + Query getQuery() { result instanceof TestQuery } + + predicate isUnsequenced(VariableAccess va1, VariableAccess va2) { + FullExprOrdering::isUnsequenced(va1, va2) + } +} + +import ExpressionWithUnsequencedSideEffects diff --git a/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.expected b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.expected new file mode 100644 index 0000000000..00a4b11143 --- /dev/null +++ b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.expected @@ -0,0 +1,6 @@ +| test.cpp:5:12:5:24 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:5:21:5:22 | l1 | sub-expression | test.cpp:5:21:5:24 | ... ++ | expression | test.cpp:5:15:5:16 | l1 | sub-expression | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:13:12:13:13 | call to f2 | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:13:15:13:16 | l1 | sub-expression | test.cpp:13:15:13:18 | ... ++ | expression | test.cpp:13:21:13:22 | l1 | sub-expression | test.cpp:12:7:12:8 | l1 | l1 | +| test.cpp:26:29:26:53 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:26:34:26:35 | p1 | sub-expression | test.cpp:25:34:25:40 | ... += ... | expression | test.cpp:26:48:26:49 | p1 | sub-expression | test.cpp:26:14:26:15 | p1 | p1 | +| test.cpp:26:29:26:53 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:26:48:26:49 | p1 | sub-expression | test.cpp:25:34:25:40 | ... += ... | expression | test.cpp:26:34:26:35 | p1 | sub-expression | test.cpp:26:14:26:15 | p1 | p1 | +| test.cpp:39:12:39:18 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:39:12:39:13 | l1 | sub-expression | test.cpp:39:12:39:13 | l1 | expression | test.cpp:39:17:39:18 | l1 | sub-expression | test.cpp:37:16:37:17 | l1 | l1 | +| test.cpp:39:12:39:18 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:39:17:39:18 | l1 | sub-expression | test.cpp:39:17:39:18 | l1 | expression | test.cpp:39:12:39:13 | l1 | sub-expression | test.cpp:37:16:37:17 | l1 | l1 | diff --git a/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.ql b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.ql new file mode 100644 index 0000000000..a244137fcd --- /dev/null +++ b/cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffectsCpp17.ql @@ -0,0 +1,14 @@ +import codingstandards.cpp.rules.expressionwithunsequencedsideeffects.ExpressionWithUnsequencedSideEffects +import codingstandards.cpp.Ordering +import codingstandards.cpp.orderofevaluation.VariableAccessOrdering +import Ordering::Make as FullExprOrdering + +module TestFileConfig implements ExpressionWithUnsequencedSideEffectsConfigSig { + Query getQuery() { result instanceof TestQuery } + + predicate isUnsequenced(VariableAccess va1, VariableAccess va2) { + FullExprOrdering::isUnsequenced(va1, va2) + } +} + +import ExpressionWithUnsequencedSideEffects diff --git a/cpp/autosar/test/rules/A5-0-1/test.cpp b/cpp/common/test/rules/expressionwithunsequencedsideeffects/test.cpp similarity index 58% rename from cpp/autosar/test/rules/A5-0-1/test.cpp rename to cpp/common/test/rules/expressionwithunsequencedsideeffects/test.cpp index 3476c21c82..9e09291c0e 100644 --- a/cpp/autosar/test/rules/A5-0-1/test.cpp +++ b/cpp/common/test/rules/expressionwithunsequencedsideeffects/test.cpp @@ -18,9 +18,8 @@ struct S1 { }; void f4(S1 *p1) { - p1->m1(p1++); // NON_COMPLIANT in C++14 and COMPLIANT since C++17 the - // expression that names the function is sequenced before every - // argument expression and every default argument + p1->m1(p1++); // C++14: NON_COMPLIANT + // C++17: COMPLIANT } int addc(int *n, int c) { return *n += c; } @@ -30,7 +29,8 @@ void f6() { int l1 = 0; int l2; - l2 = l1 = l1++; // NON_COMPLIANT + l2 = l1 = l1++; // C++14: NON_COMPLIANT + // C++17: COMPLIANT } void f7() { @@ -56,4 +56,18 @@ void f10(int *p1) { *p1++ = 1; // COMPLIANT *p1++ = 2; // COMPLIANT *p1++ = 3; // COMPLIANT -} \ No newline at end of file +} + +void f11(S1 *p1) { + (p1++)->m1(p1); // C++14: NON_COMPLIANT + // C++17: COMPLIANT +} + +void f12(int *p1) { + (*p1++) >> (*p1++); // C++14: NON_COMPLIANT + // C++17: COMPLIANT + // This case should be non-compliant in C++14, but `isCandidate` only holds + // for operations and calls, while the subscript operator is neither. + (p1++)[*p1++]; // C++14: NON_COMPLIANT[False negative] + // C++17: COMPLIANT +} diff --git a/cpp/misra/src/rules/RULE-4-6-1/MemoryUsageNotSequenced.ql b/cpp/misra/src/rules/RULE-4-6-1/MemoryUsageNotSequenced.ql new file mode 100644 index 0000000000..1e85c60d2e --- /dev/null +++ b/cpp/misra/src/rules/RULE-4-6-1/MemoryUsageNotSequenced.ql @@ -0,0 +1,33 @@ +/** + * @id cpp/misra/memory-usage-not-sequenced + * @name RULE-4-6-1: Operations on a memory location shall be sequenced appropriately + * @description Code behavior should not have unsequenced or indeterminately sequenced operations on + * the same memory location. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-4-6-1 + * scope/system + * portability + * correctness + * maintainability + * external/misra/enforcement/undecidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.expressionwithunsequencedsideeffects.ExpressionWithUnsequencedSideEffects +import codingstandards.cpp.Ordering +import codingstandards.cpp.orderofevaluation.VariableAccessOrdering +import Ordering::Make as FullExprOrdering + +module MemoryUsageNotSequencedConfig implements ExpressionWithUnsequencedSideEffectsConfigSig { + Query getQuery() { result = SideEffects4Package::memoryUsageNotSequencedQuery() } + + predicate isUnsequenced(VariableAccess va1, VariableAccess va2) { + FullExprOrdering::isUnsequenced(va1, va2) + } +} + +import ExpressionWithUnsequencedSideEffects diff --git a/cpp/misra/test/rules/RULE-4-6-1/MemoryUsageNotSequenced.testref b/cpp/misra/test/rules/RULE-4-6-1/MemoryUsageNotSequenced.testref new file mode 100644 index 0000000000..fe6eefe6ba --- /dev/null +++ b/cpp/misra/test/rules/RULE-4-6-1/MemoryUsageNotSequenced.testref @@ -0,0 +1 @@ +cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.ql \ No newline at end of file diff --git a/rule_packages/cpp/OrderOfEvaluation.json b/rule_packages/cpp/OrderOfEvaluation.json index 00ec0dbc65..684d92f504 100644 --- a/rule_packages/cpp/OrderOfEvaluation.json +++ b/rule_packages/cpp/OrderOfEvaluation.json @@ -16,6 +16,7 @@ "precision": "high", "severity": "error", "short_name": "ExpressionShouldNotRelyOnOrderOfEvaluation", + "shared_implementation_short_name": "ExpressionWithUnsequencedSideEffects", "tags": [ "correctness" ] diff --git a/rule_packages/cpp/SideEffects4.json b/rule_packages/cpp/SideEffects4.json index 865a0dcdfa..7210981814 100644 --- a/rule_packages/cpp/SideEffects4.json +++ b/rule_packages/cpp/SideEffects4.json @@ -13,6 +13,7 @@ "precision": "very-high", "severity": "error", "short_name": "MemoryUsageNotSequenced", + "shared_implementation_short_name": "ExpressionWithUnsequencedSideEffects", "tags": [ "scope/system", "portability", From cc1a74e0106589fddca18303db4cb4b077608928 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 20 Feb 2026 17:05:54 -0800 Subject: [PATCH 04/10] Add change note --- change_notes/2026-2-20-update-expression-sequencing.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 change_notes/2026-2-20-update-expression-sequencing.md diff --git a/change_notes/2026-2-20-update-expression-sequencing.md b/change_notes/2026-2-20-update-expression-sequencing.md new file mode 100644 index 0000000000..e44f93de99 --- /dev/null +++ b/change_notes/2026-2-20-update-expression-sequencing.md @@ -0,0 +1,4 @@ + - `A5-0-1` - `ExpressionShouldNotRelyONOrderOfEvaluation.ql` + - Fixed a bug where some sequenced operations were not detected as such due to an error in the "candidate selection" process. This could have complex effects on results, but should mostly fix false positives. + - `RULE-13-2`, `A5-0-1` - `UnsequencedSideEffects.ql`, `UnsequencedAtomicReads.ql`, `ExpressionShouldNotRelyONOrderOfEvaluation.ql` + - Implementation of ordering has been refactored to share more code across specifications (C11-C17, C++14, and C++17 sequencing rules). No change in results is expected from this refactor. \ No newline at end of file From eda6821eda2192663621de0be14d4606152558ba Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 20 Feb 2026 17:06:04 -0800 Subject: [PATCH 05/10] Fix formatting --- cpp/common/src/codingstandards/cpp/Ordering.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/Ordering.qll b/cpp/common/src/codingstandards/cpp/Ordering.qll index 94b0b51062..7de29aa872 100644 --- a/cpp/common/src/codingstandards/cpp/Ordering.qll +++ b/cpp/common/src/codingstandards/cpp/Ordering.qll @@ -137,7 +137,7 @@ module Ordering { exists(Call call, Expr qual, Expr arg | call.getQualifier() = qual and call.getAnArgument() = arg - | + | qual = n1.toExpr().getParent*() and arg = n2.toExpr().getParent*() ) or @@ -213,5 +213,4 @@ module Ordering { Location getLocation() { result = toExpr().getLocation() } } - } From 2c22776114cf75394e4b1b65ce52c1e4573dc57e Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 20 Feb 2026 17:09:26 -0800 Subject: [PATCH 06/10] Add portability tag to schema --- schemas/rule-package.schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/schemas/rule-package.schema.json b/schemas/rule-package.schema.json index fff79fede0..78714e08f7 100644 --- a/schemas/rule-package.schema.json +++ b/schemas/rule-package.schema.json @@ -336,6 +336,7 @@ "security", "concurrency", "performance", + "portability", "external/cert/audit", "external/autosar/audit", "external/autosar/default-disabled", From 11398904d21d11d60e2b9a69f5fa0125e5ddf0cb Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 20 Feb 2026 21:53:20 -0800 Subject: [PATCH 07/10] Fix EXP50-CPP --- change_notes/2026-2-20-update-expression-sequencing.md | 5 +++-- ...pendOnTheOrderOfScalarObjectEvaluationForSideEffects.ql | 7 +++---- ...TheOrderOfScalarObjectEvaluationForSideEffects.expected | 3 +++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/change_notes/2026-2-20-update-expression-sequencing.md b/change_notes/2026-2-20-update-expression-sequencing.md index e44f93de99..3534d10feb 100644 --- a/change_notes/2026-2-20-update-expression-sequencing.md +++ b/change_notes/2026-2-20-update-expression-sequencing.md @@ -1,4 +1,5 @@ - - `A5-0-1` - `ExpressionShouldNotRelyONOrderOfEvaluation.ql` + - `A5-0-1`, `EXP50-CPP` - `ExpressionShouldNotRelyONOrderOfEvaluation.ql`, `DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql`: - Fixed a bug where some sequenced operations were not detected as such due to an error in the "candidate selection" process. This could have complex effects on results, but should mostly fix false positives. - - `RULE-13-2`, `A5-0-1` - `UnsequencedSideEffects.ql`, `UnsequencedAtomicReads.ql`, `ExpressionShouldNotRelyONOrderOfEvaluation.ql` + - Some unsequenced operations that previously reported one alert may now report two, due to the extra candidates being considered. + - `RULE-13-2`, `A5-0-1`, `EXP50-CPP` - `UnsequencedSideEffects.ql`, `UnsequencedAtomicReads.ql`, `ExpressionShouldNotRelyONOrderOfEvaluation.ql`, `DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql`: - Implementation of ordering has been refactored to share more code across specifications (C11-C17, C++14, and C++17 sequencing rules). No change in results is expected from this refactor. \ No newline at end of file diff --git a/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql b/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql index 4c268e9c7e..82b7d4e8dc 100644 --- a/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql +++ b/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql @@ -23,16 +23,15 @@ import codingstandards.cpp.Ordering import codingstandards.cpp.orderofevaluation.VariableAccessOrdering import codingstandards.cpp.Expr import codingstandards.cpp.Variable +import Ordering::Make as FullExprOrdering -from - VariableAccessInFullExpressionOrdering config, FullExpr e, ScalarVariable v, VariableEffect ve, - VariableAccess va1, VariableAccess va2 +from FullExpr e, ScalarVariable v, VariableEffect ve, VariableAccess va1, VariableAccess va2 where not isExcluded(e, SideEffects1Package::doNotDependOnTheOrderOfScalarObjectEvaluationForSideEffectsQuery()) and e = va1.(ConstituentExpr).getFullExpr() and va1 = ve.getAnAccess() and - config.isUnsequenced(va1, va2) and + FullExprOrdering::isUnsequenced(va1, va2) and v = va1.getTarget() select e, "Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@.", v, v.getName(), ve, "side-effect", va2, "side-effect or value computation" diff --git a/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.expected b/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.expected index 53ba5ce8e7..a7d5f0e42f 100644 --- a/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.expected +++ b/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.expected @@ -1,6 +1,9 @@ +| test.cpp:8:3:9:8 | ... = ... | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:4:5:4:6 | g1 | g1 | test.cpp:8:3:9:8 | ... = ... | side-effect | test.cpp:8:10:8:11 | g1 | side-effect or value computation | | test.cpp:8:3:9:8 | ... = ... | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:4:5:4:6 | g1 | g1 | test.cpp:8:8:8:11 | ++ ... | side-effect | test.cpp:8:3:8:4 | g1 | side-effect or value computation | | test.cpp:10:3:10:15 | ... = ... | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:4:5:4:6 | g1 | g1 | test.cpp:10:6:10:9 | ... ++ | side-effect | test.cpp:10:14:10:15 | g1 | side-effect or value computation | +| test.cpp:14:3:14:12 | ... += ... | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:4:5:4:6 | g1 | g1 | test.cpp:14:3:14:12 | ... += ... | side-effect | test.cpp:14:9:14:10 | g1 | side-effect or value computation | | test.cpp:14:3:14:12 | ... += ... | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:4:5:4:6 | g1 | g1 | test.cpp:14:9:14:12 | ... ++ | side-effect | test.cpp:14:3:14:4 | g1 | side-effect or value computation | +| test.cpp:15:3:16:11 | ... = ... | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:4:5:4:6 | g1 | g1 | test.cpp:15:3:16:11 | ... = ... | side-effect | test.cpp:15:8:15:9 | g1 | side-effect or value computation | | test.cpp:15:3:16:11 | ... = ... | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:4:5:4:6 | g1 | g1 | test.cpp:15:8:16:11 | ... += ... | side-effect | test.cpp:15:3:15:4 | g1 | side-effect or value computation | | test.cpp:21:3:21:4 | call to f2 | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:20:13:20:14 | p1 | p1 | test.cpp:21:6:21:9 | ... ++ | side-effect | test.cpp:21:12:21:13 | p1 | side-effect or value computation | | test.cpp:28:13:28:13 | call to operator<< | Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@. | test.cpp:26:13:26:14 | p1 | p1 | test.cpp:27:16:27:19 | ... ++ | side-effect | test.cpp:27:24:27:25 | p1 | side-effect or value computation | From 5eef1680e9f13f26522731f4cac215f37d9fadf1 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sat, 21 Feb 2026 11:33:03 -0800 Subject: [PATCH 08/10] Update EXP30-C to handle refactorehd ordering --- .../DependenceOnOrderOfScalarEvaluationForSideEffects.ql | 7 +++---- change_notes/2026-2-20-update-expression-sequencing.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql b/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql index 51b505ec63..9d0c7a26de 100644 --- a/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql +++ b/c/cert/src/rules/EXP30-C/DependenceOnOrderOfScalarEvaluationForSideEffects.ql @@ -21,15 +21,14 @@ import codingstandards.c.cert import codingstandards.cpp.SideEffect import codingstandards.c.Ordering import codingstandards.c.orderofevaluation.VariableAccessOrdering +import Ordering::Make as FullExpressionOrdering -from - VariableAccessInFullExpressionOrdering config, FullExpr e, ScalarVariable v, VariableEffect ve, - VariableAccess va1, VariableAccess va2 +from FullExpr e, ScalarVariable v, VariableEffect ve, VariableAccess va1, VariableAccess va2 where not isExcluded(e, SideEffects1Package::dependenceOnOrderOfScalarEvaluationForSideEffectsQuery()) and e = va1.(ConstituentExpr).getFullExpr() and va1 = ve.getAnAccess() and - config.isUnsequenced(va1, va2) and + FullExpressionOrdering::isUnsequenced(va1, va2) and v = va1.getTarget() select e, "Scalar object referenced by $@ has a $@ that is unsequenced in relative to another $@.", v, v.getName(), ve, "side-effect", va2, "side-effect or value computation" diff --git a/change_notes/2026-2-20-update-expression-sequencing.md b/change_notes/2026-2-20-update-expression-sequencing.md index 3534d10feb..a2ab9265ba 100644 --- a/change_notes/2026-2-20-update-expression-sequencing.md +++ b/change_notes/2026-2-20-update-expression-sequencing.md @@ -1,5 +1,5 @@ - `A5-0-1`, `EXP50-CPP` - `ExpressionShouldNotRelyONOrderOfEvaluation.ql`, `DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql`: - Fixed a bug where some sequenced operations were not detected as such due to an error in the "candidate selection" process. This could have complex effects on results, but should mostly fix false positives. - Some unsequenced operations that previously reported one alert may now report two, due to the extra candidates being considered. - - `RULE-13-2`, `A5-0-1`, `EXP50-CPP` - `UnsequencedSideEffects.ql`, `UnsequencedAtomicReads.ql`, `ExpressionShouldNotRelyONOrderOfEvaluation.ql`, `DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql`: + - `RULE-13-2`, `A5-0-1`, `EXP50-CPP`, `EXP30-C` - `UnsequencedSideEffects.ql`, `UnsequencedAtomicReads.ql`, `ExpressionShouldNotRelyONOrderOfEvaluation.ql`, `DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql`, `DependenceOnOrderOfScalarEvaluationForSideEffects.ql`: - Implementation of ordering has been refactored to share more code across specifications (C11-C17, C++14, and C++17 sequencing rules). No change in results is expected from this refactor. \ No newline at end of file From 5ebb9d8a2008791206260c0b243cee5dff93a0ad Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sat, 21 Feb 2026 11:36:30 -0800 Subject: [PATCH 09/10] Add note about sequential full exprs to change note --- change_notes/2026-2-20-update-expression-sequencing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/change_notes/2026-2-20-update-expression-sequencing.md b/change_notes/2026-2-20-update-expression-sequencing.md index a2ab9265ba..5251da8fd3 100644 --- a/change_notes/2026-2-20-update-expression-sequencing.md +++ b/change_notes/2026-2-20-update-expression-sequencing.md @@ -1,5 +1,5 @@ - `A5-0-1`, `EXP50-CPP` - `ExpressionShouldNotRelyONOrderOfEvaluation.ql`, `DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql`: - - Fixed a bug where some sequenced operations were not detected as such due to an error in the "candidate selection" process. This could have complex effects on results, but should mostly fix false positives. - - Some unsequenced operations that previously reported one alert may now report two, due to the extra candidates being considered. + - Fixed a bug where some sequenced operations were not detected as such due to an error in the "candidate selection" process. This could have complex effects on results, but should mostly fix false positives. Some unsequenced operations that previously reported one alert may now report two, due to the extra candidates being considered. + - Sequencing between full expressions no longer requires that the expressions are sequential; expressions in separate if statements, for instance, are not necessarily sequential, but they are still ordered. It is unclear if this change will have any effect on results, but it should be more accurate to the standard. - `RULE-13-2`, `A5-0-1`, `EXP50-CPP`, `EXP30-C` - `UnsequencedSideEffects.ql`, `UnsequencedAtomicReads.ql`, `ExpressionShouldNotRelyONOrderOfEvaluation.ql`, `DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql`, `DependenceOnOrderOfScalarEvaluationForSideEffects.ql`: - Implementation of ordering has been refactored to share more code across specifications (C11-C17, C++14, and C++17 sequencing rules). No change in results is expected from this refactor. \ No newline at end of file From 6a48e22efcd3157eedc121676cab90c4fad0d39b Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sat, 21 Feb 2026 14:37:24 -0800 Subject: [PATCH 10/10] Fix test expectations --- .../test/rules/RULE-13-2/UnsequencedAtomicReads.expected | 6 +++--- .../ExpressionShouldNotRelyOnOrderOfEvaluation.expected | 9 --------- .../ExpressionShouldNotRelyOnOrderOfEvaluation.qlref | 1 - .../ExpressionShouldNotRelyOnOrderOfEvaluation.testref | 1 + 4 files changed, 4 insertions(+), 13 deletions(-) delete mode 100644 cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.expected delete mode 100644 cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.qlref create mode 100644 cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.testref diff --git a/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected index 4fa06eb069..276db0040e 100644 --- a/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected +++ b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected @@ -1,5 +1,5 @@ -WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,31-39) -WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,67-75) -WARNING: module 'TaintTracking' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,5-18) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:114,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:114,67-75) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:114,5-18) | test.c:44:12:44:18 | ... + ... | Atomic variable $@ has a $@ that is unsequenced with $@. | test.c:42:15:42:16 | a1 | a1 | test.c:44:12:44:13 | a1 | previous read | test.c:44:17:44:18 | a1 | another read | | test.c:46:3:46:37 | ... + ... | Atomic variable $@ has a $@ that is unsequenced with $@. | test.c:42:15:42:16 | a1 | a1 | test.c:46:16:46:17 | a1 | previous read | test.c:46:35:46:36 | a1 | another read | diff --git a/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.expected b/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.expected deleted file mode 100644 index f5ec85bcaa..0000000000 --- a/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.expected +++ /dev/null @@ -1,9 +0,0 @@ -| test.cpp:5:12:5:24 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:5:21:5:22 | l1 | sub-expression | test.cpp:5:21:5:24 | ... ++ | expression | test.cpp:5:15:5:16 | l1 | sub-expression | test.cpp:2:7:2:8 | l1 | l1 | -| test.cpp:13:12:13:13 | call to f2 | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:13:15:13:16 | l1 | sub-expression | test.cpp:13:15:13:18 | ... ++ | expression | test.cpp:13:21:13:22 | l1 | sub-expression | test.cpp:12:7:12:8 | l1 | l1 | -| test.cpp:21:7:21:8 | call to m1 | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:21:10:21:11 | p1 | sub-expression | test.cpp:21:10:21:13 | ... ++ | expression | test.cpp:21:3:21:4 | p1 | sub-expression | test.cpp:20:13:20:14 | p1 | p1 | -| test.cpp:27:29:27:53 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:27:34:27:35 | p1 | sub-expression | test.cpp:26:34:26:40 | ... += ... | expression | test.cpp:27:48:27:49 | p1 | sub-expression | test.cpp:27:14:27:15 | p1 | p1 | -| test.cpp:27:29:27:53 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:27:48:27:49 | p1 | sub-expression | test.cpp:26:34:26:40 | ... += ... | expression | test.cpp:27:34:27:35 | p1 | sub-expression | test.cpp:27:14:27:15 | p1 | p1 | -| test.cpp:33:3:33:16 | ... = ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:33:8:33:9 | l1 | sub-expression | test.cpp:33:8:33:16 | ... = ... | expression | test.cpp:33:13:33:14 | l1 | sub-expression | test.cpp:30:7:30:8 | l1 | l1 | -| test.cpp:33:3:33:16 | ... = ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:33:13:33:14 | l1 | sub-expression | test.cpp:33:13:33:16 | ... ++ | expression | test.cpp:33:8:33:9 | l1 | sub-expression | test.cpp:30:7:30:8 | l1 | l1 | -| test.cpp:39:12:39:18 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:39:12:39:13 | l1 | sub-expression | test.cpp:39:12:39:13 | l1 | expression | test.cpp:39:17:39:18 | l1 | sub-expression | test.cpp:37:16:37:17 | l1 | l1 | -| test.cpp:39:12:39:18 | ... + ... | The evaluation is depended on the order of evaluation of $@, that is modified by $@ and $@, that both access $@. | test.cpp:39:17:39:18 | l1 | sub-expression | test.cpp:39:17:39:18 | l1 | expression | test.cpp:39:12:39:13 | l1 | sub-expression | test.cpp:37:16:37:17 | l1 | l1 | diff --git a/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.qlref b/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.qlref deleted file mode 100644 index 7ec21d78f8..0000000000 --- a/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.testref b/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.testref new file mode 100644 index 0000000000..fe6eefe6ba --- /dev/null +++ b/cpp/autosar/test/rules/A5-0-1/ExpressionShouldNotRelyOnOrderOfEvaluation.testref @@ -0,0 +1 @@ +cpp/common/test/rules/expressionwithunsequencedsideeffects/ExpressionWithUnsequencedSideEffects.ql \ No newline at end of file