Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
93a594e
Cfg: Support Throw expressions.
aschackmull Mar 5, 2026
a53cffc
Cfg: Support GotoStmt.
aschackmull Mar 5, 2026
0b6c416
Cfg: Support short-circuiting compound assignments.
aschackmull Mar 3, 2026
035b83c
C#: Introduce ControlFlowElementOrCallable.
aschackmull Mar 9, 2026
6ffed85
Cfg/Java: Move InstanceOfExpr CFG into shared lib.
aschackmull Mar 11, 2026
88aaff8
Cfg: Extend consistency checks.
aschackmull Mar 12, 2026
61976e3
C#: Rename ControlFlow::Node to ControlFlowNode.
aschackmull Mar 12, 2026
b85b02a
Cfg: Add dominance predicates to shared ControlFlowNode.
aschackmull Mar 18, 2026
03f6bdb
C#: Update some references in preparation for CFG swap.
aschackmull Mar 18, 2026
b878ae3
C#: Update some references to ControlFlow::Nodes.
aschackmull Mar 18, 2026
13a4141
C#: Rename remaining references to ControlFlow::Nodes.
aschackmull Mar 18, 2026
9cf9a36
C#: Rename ControlFlow::BasicBlock to BasicBlock.
aschackmull Mar 18, 2026
ff978d1
C#: Replace CFG.
aschackmull Mar 9, 2026
b179033
C#: Fix test.
aschackmull Mar 20, 2026
700d56f
C#: Fix UncheckedCastInEquals.
aschackmull Mar 20, 2026
ac88b73
C#: Bugfix in enclosing callable.
aschackmull Mar 24, 2026
093eb57
C#: Fix CFG position of property setter calls.
aschackmull Mar 26, 2026
43fe411
C#: Accept SSA location changes.
aschackmull Mar 26, 2026
1a6670a
C#: Phi nodes are not expected to have associated Elements.
aschackmull Mar 26, 2026
6010640
C#: Accept bugfix.
aschackmull Mar 30, 2026
a5c99f9
C#: Accept harmless CFG changes.
aschackmull Mar 30, 2026
5d58909
C#: Accept CFG changes.
aschackmull Mar 30, 2026
49cc931
C#: Compile-time constants no longer have CFG nodes.
aschackmull Mar 30, 2026
e90243c
C#: Accept irrelevant changes.
aschackmull Mar 31, 2026
88256ee
C#: GuardedExpr no longer contains expressions guarded solely by disj…
aschackmull Mar 31, 2026
773881f
C#: Accept data flow inconsistency check for read+write calls.
aschackmull Mar 31, 2026
a997d9f
C#: Accept fixed consistency check.
aschackmull Mar 31, 2026
a695819
C#: Accept CFG changes for "first" relation.
aschackmull Mar 31, 2026
a7d4b00
C#: Accept changed location for phi nodes.
aschackmull Apr 7, 2026
371bc30
C#: CFG and data flow nodes now exist for LHSs.
aschackmull Apr 7, 2026
1d9c0ae
C#: Fix perf.
aschackmull Apr 8, 2026
bfbd0f7
C#: Fix some bad join orders.
aschackmull Apr 9, 2026
bbd403d
C#: Rework DataFlowCallable-to-cfg relation in terms of basic blocks …
aschackmull Apr 9, 2026
2d5a184
C#: Accept new CFG in tests.
aschackmull Apr 9, 2026
aaf9bb2
C#: Accept fewer CallContextSpecificCall due to no splitting.
aschackmull Apr 9, 2026
452913f
C#: Improve perf of UnsynchronizedStaticAccess.ql.
aschackmull Apr 10, 2026
d5c9fd1
C#/Cfg: A bit more qldoc.
aschackmull Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import csharp
import Solorigate
import experimental.code.csharp.Cryptography.NonCryptographicHashes

ControlFlowNode loopExitNode(LoopStmt loop) { result.isAfter(loop) }

from Variable v, Literal l, LoopStmt loop, Expr additional_xor
where
maybeUsedInFnvFunction(v, _, _, loop) and
exists(BitwiseXorOperation xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
loop.getAControlFlowExitNode().getASuccessor*() = xor2.getAControlFlowNode() and
loopExitNode(loop).getASuccessor*() = xor2.getAControlFlowNode() and
xor2.getAnOperand() = v.getAnAccess()
)
select l, "This literal is used in an $@ after an FNV-like hash calculation with variable $@.",
Expand Down
5 changes: 1 addition & 4 deletions csharp/ql/consistency-queries/CfgConsistency.ql
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
import csharp
import semmle.code.csharp.controlflow.internal.Completion
import ControlFlow
import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::Consistency
import semmle.code.csharp.controlflow.internal.Splitting
import ControlFlow::Consistency
32 changes: 7 additions & 25 deletions csharp/ql/consistency-queries/DataFlowConsistency.ql
Original file line number Diff line number Diff line change
@@ -1,26 +1,11 @@
import csharp
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl
private import semmle.code.csharp.dataflow.internal.DataFlowImplSpecific
private import semmle.code.csharp.dataflow.internal.TaintTrackingImplSpecific
private import codeql.dataflow.internal.DataFlowImplConsistency

private module Input implements InputSig<Location, CsharpDataFlow> {
private import CsharpDataFlow

private predicate isStaticAssignable(Assignable a) { a.(Modifiable).isStatic() }

predicate uniqueEnclosingCallableExclude(Node node) {
// TODO: Remove once static initializers are folded into the
// static constructors
isStaticAssignable(ControlFlowGraphImpl::getNodeCfgScope(node.getControlFlowNode()))
}

predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) {
// TODO: Remove once static initializers are folded into the
// static constructors
isStaticAssignable(ControlFlowGraphImpl::getNodeCfgScope(call.getControlFlowNode()))
}

predicate uniqueNodeLocationExclude(Node n) {
// Methods with multiple implementations
n instanceof ParameterNode
Expand Down Expand Up @@ -70,17 +55,14 @@ private module Input implements InputSig<Location, CsharpDataFlow> {
init.getInitializer().getNumberOfChildren() > 1
)
or
exists(ControlFlow::Nodes::ElementNode cfn, ControlFlow::Nodes::Split split |
exists(arg.asExprAtNode(cfn))
|
split = cfn.getASplit() and
not split = call.getControlFlowNode().getASplit()
or
split = call.getControlFlowNode().getASplit() and
not split = cfn.getASplit()
)
or
call.(NonDelegateDataFlowCall).getDispatchCall().isReflection()
or
// Exclude calls that are both getter and setter calls, as they share the same argument nodes.
exists(AccessorCall ac |
call.(NonDelegateDataFlowCall).getDispatchCall().getCall() = ac and
ac instanceof AssignableRead and
ac instanceof AssignableWrite
)
)
}
}
Expand Down
7 changes: 0 additions & 7 deletions csharp/ql/consistency-queries/VariableCaptureConsistency.ql
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import csharp
import semmle.code.csharp.dataflow.internal.DataFlowPrivate::VariableCapture::Flow::ConsistencyChecks
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate::VariableCapture::Flow::ConsistencyChecks as ConsistencyChecks
private import semmle.code.csharp.controlflow.BasicBlocks
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl

query predicate uniqueEnclosingCallable(BasicBlock bb, string msg) {
ConsistencyChecks::uniqueEnclosingCallable(bb, msg) and
getNodeCfgScope(bb.getFirstNode()) instanceof Callable
}

query predicate consistencyOverview(string msg, int n) { none() }

Expand Down
21 changes: 15 additions & 6 deletions csharp/ql/lib/printCfg.ql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @tags ide-contextual-queries/print-cfg
*/

private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
import csharp

external string selectedSourceFile();

Expand All @@ -21,15 +21,15 @@ external int selectedSourceColumn();

private predicate selectedSourceColumnAlias = selectedSourceColumn/0;

module ViewCfgQueryInput implements ViewCfgQueryInputSig<File> {
module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig<File> {
predicate selectedSourceFile = selectedSourceFileAlias/0;

predicate selectedSourceLine = selectedSourceLineAlias/0;

predicate selectedSourceColumn = selectedSourceColumnAlias/0;

predicate cfgScopeSpan(
CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn
Callable scope, File file, int startLine, int startColumn, int endLine, int endColumn
) {
file = scope.getFile() and
scope.getLocation().getStartLine() = startLine and
Expand All @@ -40,11 +40,20 @@ module ViewCfgQueryInput implements ViewCfgQueryInputSig<File> {
|
loc = scope.(Callable).getBody().getLocation()
or
loc = scope.(Field).getInitializer().getLocation()
loc = any(AssignExpr init | scope.(ObjectInitMethod).initializes(init)).getLocation()
or
loc = scope.(Property).getInitializer().getLocation()
exists(AssignableMember a, Constructor ctor |
scope = ctor and
ctor.isStatic() and
a.isStatic() and
a.getDeclaringType() = ctor.getDeclaringType()
|
loc = a.(Field).getInitializer().getLocation()
or
loc = a.(Property).getInitializer().getLocation()
)
)
}
}

import ViewCfgQuery<File, ViewCfgQueryInput>
import ControlFlow::ViewCfgQuery<File, ViewCfgQueryInput>
24 changes: 9 additions & 15 deletions csharp/ql/lib/semmle/code/csharp/Assignable.qll
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class AssignableRead extends AssignableAccess {
}

pragma[noinline]
private ControlFlow::Node getAnAdjacentReadSameVar() {
private ControlFlowNode getAnAdjacentReadSameVar() {
SsaImpl::adjacentReadPairSameVar(_, this.getAControlFlowNode(), result)
}

Expand Down Expand Up @@ -115,7 +115,7 @@ class AssignableRead extends AssignableAccess {
*/
pragma[nomagic]
AssignableRead getANextRead() {
forex(ControlFlow::Node cfn | cfn = result.getAControlFlowNode() |
forex(ControlFlowNode cfn | cfn = result.getAControlFlowNode() |
cfn = this.getAnAdjacentReadSameVar()
)
}
Expand Down Expand Up @@ -419,9 +419,7 @@ class AssignableDefinition extends TAssignableDefinition {
* the definitions of `x` and `y` in `M(out x, out y)` and `(x, y) = (0, 1)`
* relate to the same call to `M` and assignment node, respectively.
*/
deprecated ControlFlow::Node getAControlFlowNode() {
result = this.getExpr().getAControlFlowNode()
}
deprecated ControlFlowNode getAControlFlowNode() { result = this.getExpr().getAControlFlowNode() }

/**
* Gets the underlying expression that updates the targeted assignable when
Expand Down Expand Up @@ -494,7 +492,7 @@ class AssignableDefinition extends TAssignableDefinition {
*/
pragma[nomagic]
AssignableRead getAFirstRead() {
forex(ControlFlow::Node cfn | cfn = result.getAControlFlowNode() |
forex(ControlFlowNode cfn | cfn = result.getAControlFlowNode() |
exists(Ssa::ExplicitDefinition def | result = def.getAFirstReadAtNode(cfn) |
this = def.getADefinition()
)
Expand Down Expand Up @@ -572,9 +570,7 @@ module AssignableDefinitions {
}

/** Holds if a node in basic block `bb` assigns to `ref` parameter `p` via definition `def`. */
private predicate basicBlockRefParamDef(
ControlFlow::BasicBlock bb, Parameter p, AssignableDefinition def
) {
private predicate basicBlockRefParamDef(BasicBlock bb, Parameter p, AssignableDefinition def) {
def = any(RefArg arg).getAnAnalyzableRefDef(p) and
bb.getANode() = def.getExpr().getAControlFlowNode()
}
Expand All @@ -585,17 +581,15 @@ module AssignableDefinitions {
* any assignments to `p`.
*/
pragma[nomagic]
private predicate parameterReachesWithoutDef(Parameter p, ControlFlow::BasicBlock bb) {
private predicate parameterReachesWithoutDef(Parameter p, BasicBlock bb) {
forall(AssignableDefinition def | basicBlockRefParamDef(bb, p, def) |
isUncertainRefCall(def.getTargetAccess())
) and
(
any(RefArg arg).isAnalyzable(p) and
p.getCallable().getEntryPoint() = bb.getFirstNode()
or
exists(ControlFlow::BasicBlock mid | parameterReachesWithoutDef(p, mid) |
bb = mid.getASuccessor()
)
exists(BasicBlock mid | parameterReachesWithoutDef(p, mid) | bb = mid.getASuccessor())
)
}

Expand All @@ -607,7 +601,7 @@ module AssignableDefinitions {
cached
predicate isUncertainRefCall(RefArg arg) {
arg.isPotentialAssignment() and
exists(ControlFlow::BasicBlock bb, Parameter p | arg.isAnalyzable(p) |
exists(BasicBlock bb, Parameter p | arg.isAnalyzable(p) |
parameterReachesWithoutDef(p, bb) and
bb.getLastNode() = p.getCallable().getExitPoint()
)
Expand Down Expand Up @@ -688,7 +682,7 @@ module AssignableDefinitions {
/** Gets the underlying parameter. */
Parameter getParameter() { result = p }

deprecated override ControlFlow::Node getAControlFlowNode() {
deprecated override ControlFlowNode getAControlFlowNode() {
result = p.getCallable().getEntryPoint()
}

Expand Down
17 changes: 0 additions & 17 deletions csharp/ql/lib/semmle/code/csharp/Caching.qll
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,6 @@ private import csharp
* in the same stage across different files.
*/
module Stages {
cached
module ControlFlowStage {
private import semmle.code.csharp.controlflow.internal.Splitting

cached
predicate forceCachingInSameStage() { any() }

cached
private predicate forceCachingInSameStageRev() {
exists(Split s)
or
exists(ControlFlow::Node n)
or
forceCachingInSameStageRev()
}
}

cached
module GuardsStage {
private import semmle.code.csharp.controlflow.Guards
Expand Down
6 changes: 3 additions & 3 deletions csharp/ql/lib/semmle/code/csharp/Callable.qll
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private import TypeRef
* an anonymous function (`AnonymousFunctionExpr`), or a local function
* (`LocalFunction`).
*/
class Callable extends Parameterizable, ExprOrStmtParent, @callable {
class Callable extends Parameterizable, ControlFlowElementOrCallable, @callable {
/** Gets the return type of this callable. */
Type getReturnType() { none() }

Expand Down Expand Up @@ -157,10 +157,10 @@ class Callable extends Parameterizable, ExprOrStmtParent, @callable {
final predicate hasExpressionBody() { exists(this.getExpressionBody()) }

/** Gets the entry point in the control graph for this callable. */
ControlFlow::Nodes::EntryNode getEntryPoint() { result.getCallable() = this }
ControlFlow::EntryNode getEntryPoint() { result.getEnclosingCallable() = this }

/** Gets the exit point in the control graph for this callable. */
ControlFlow::Nodes::ExitNode getExitPoint() { result.getCallable() = this }
ControlFlow::ExitNode getExitPoint() { result.getEnclosingCallable() = this }

/**
* Gets the enclosing callable of this callable, if any.
Expand Down
11 changes: 2 additions & 9 deletions csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,6 @@ private module Cached {
result = parent.getAChildStmt()
}

pragma[inline]
private ControlFlowElement enclosingStart(ControlFlowElement cfe) {
result = cfe
or
getAChild(result).(AnonymousFunctionExpr) = cfe
}

private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) {
child = getAChild(parent) and
not child = getBody(_)
Expand All @@ -145,15 +138,15 @@ private module Cached {
cached
predicate enclosingBody(ControlFlowElement cfe, ControlFlowElement body) {
body = getBody(_) and
parent*(enclosingStart(cfe), body)
parent*(cfe, body)
}

/** Holds if the enclosing callable of `cfe` is `c`. */
cached
predicate enclosingCallable(ControlFlowElement cfe, Callable c) {
enclosingBody(cfe, getBody(c))
or
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
parent*(cfe, c.(Constructor).getInitializer())
or
parent*(cfe, c.(Constructor).getObjectInitializerCall())
or
Expand Down
Loading
Loading