From 3a3ec1be9057150a84b425a69de4e433c2d5b946 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 17 Jun 2026 12:52:28 +0200 Subject: [PATCH 1/2] Cfg: Distinguish parameters from their patterns. --- .../codeql/controlflow/ControlFlowGraph.qll | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index 522a416c768d..71d22c416ea8 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -52,6 +52,15 @@ signature module AstSig { /** A parameter of a callable. */ class Parameter extends AstNode { + /** + * Gets the pattern associated with this parameter. + * + * The pattern is included in the CFG while the parameter itself is not. + * Although, in simple cases that do not involve destructuring, it is + * allowed for the pattern to be equal to the parameter. + */ + AstNode getPattern(); + /** Gets the default value of this parameter, if any. */ Expr getDefaultValue(); } @@ -631,7 +640,7 @@ module Make0 Ast> { or n = any(Case case).getPattern(_) or - exists(n.(Parameter).getDefaultValue()) + exists(Parameter p | exists(p.getDefaultValue()) and n = p.getPattern()) ) } @@ -803,24 +812,27 @@ module Make0 Ast> { ) } + private predicate hasCfg(AstNode n) { + exists(getEnclosingCallable(n)) and + (n instanceof Parameter implies n = n.(Parameter).getPattern()) + } + cached private newtype TNode = - TBeforeNode(AstNode n) { Input1::cfgCachedStageRef() and exists(getEnclosingCallable(n)) } or - TAstNode(AstNode n) { postOrInOrder(n) and exists(getEnclosingCallable(n)) } or + TBeforeNode(AstNode n) { Input1::cfgCachedStageRef() and hasCfg(n) } or + TAstNode(AstNode n) { postOrInOrder(n) and hasCfg(n) } or TAfterValueNode(AstNode n, ConditionalSuccessor t) { inConditionalContext(n, t.getKind()) and - exists(getEnclosingCallable(n)) and + hasCfg(n) and not constantCondition(n, t.getDual()) } or TAfterNode(AstNode n) { - exists(getEnclosingCallable(n)) and + hasCfg(n) and not inConditionalContext(n, _) and not cannotTerminateNormally(n) and not simpleLeafNode(n) } or - TAdditionalNode(AstNode n, string tag) { - additionalNode(n, tag, _) and exists(getEnclosingCallable(n)) - } or + TAdditionalNode(AstNode n, string tag) { additionalNode(n, tag, _) and hasCfg(n) } or TEntryNode(Callable c) { callableHasBodyPart(c, _) } or TAnnotatedExitNode(Callable c, Boolean normal) { callableHasBodyPart(c, _) } or TExitNode(Callable c) { callableHasBodyPart(c, _) } @@ -1390,8 +1402,8 @@ module Make0 Ast> { } pragma[nomagic] - private AstNode getParameterOrBodyEntry(Callable c, CallableContextOption ctx, int i) { - result = getRankedParameter(c, ctx, i) + private AstNode getParameterPatternOrBodyEntry(Callable c, CallableContextOption ctx, int i) { + result = getRankedParameter(c, ctx, i).getPattern() or ( not exists(getRankedParameter(c, _, _)) and @@ -1409,18 +1421,18 @@ module Make0 Ast> { or exists(Callable c | n1.(EntryNodeImpl).getEnclosingCallable() = c and - n2.isBefore(getParameterOrBodyEntry(c, _, 1)) + n2.isBefore(getParameterPatternOrBodyEntry(c, _, 1)) or exists(CallableContextOption ctx, Parameter p, int i | p = getRankedParameter(c, ctx, i) | exists(MatchingSuccessor t | - n1.isAfterValue(p, t) and + n1.isAfterValue(p.getPattern(), t) and if t.isMatch() - then n2.isBefore(getParameterOrBodyEntry(c, ctx, i + 1)) + then n2.isBefore(getParameterPatternOrBodyEntry(c, ctx, i + 1)) else n2.isBefore(p.getDefaultValue()) ) or n1.isAfter(p.getDefaultValue()) and - n2.isBefore(getParameterOrBodyEntry(c, ctx, i + 1)) + n2.isBefore(getParameterPatternOrBodyEntry(c, ctx, i + 1)) ) or exists(Input1::CallableContext ctx, int i | @@ -1796,6 +1808,7 @@ module Make0 Ast> { * and therefore should use default left-to-right evaluation. */ private predicate defaultCfg(AstNode ast) { + hasCfg(ast) and not explicitStep(any(PreControlFlowNode n | n.isBefore(ast)), _) } From f844cd3754e94617e598cec43ff30f4a0969ae38 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 17 Jun 2026 18:17:36 +0200 Subject: [PATCH 2/2] Java/C#: Adapt to signature change. --- .../code/csharp/controlflow/internal/ControlFlowGraph.qll | 2 ++ java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 2 ++ 2 files changed, 4 insertions(+) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll index 18a967eee289..072f611a45aa 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll @@ -145,6 +145,8 @@ module Ast implements AstSig { final private class ParameterFinal = CS::Parameter; class Parameter extends ParameterFinal { + AstNode getPattern() { result = this } + Expr getDefaultValue() { // Avoid combinatorial explosions for callables with multiple bodies result = unique( | | super.getDefaultValue()) diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index 51f3046e1bfa..142f9f61e569 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -61,6 +61,8 @@ private module Ast implements AstSig { class Parameter extends AstNode { Parameter() { none() } + AstNode getPattern() { none() } + Expr getDefaultValue() { none() } }