Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b424693
C#: Don't extract expanded assignments and swap child indices for ass…
michaelnebel Mar 20, 2026
6acaaf7
C#: Update the DB scheme to reflect that assign arithmetic- and bitwi…
michaelnebel Mar 20, 2026
d49b42f
C#: Update the child indices for assignments, update Assign classes t…
michaelnebel Mar 20, 2026
4fa2e1f
C#: Deprecate the expanded assignment predicate as we no longer extra…
michaelnebel Mar 20, 2026
927a36e
C#: Cleaup expanded assignments from the dispatch logic.
michaelnebel Mar 20, 2026
8edaab6
C#: Introduce a new kind of assignable definitions for compound assig…
michaelnebel Mar 20, 2026
edbb637
C#: Update the CFG implementation based on the new operations and rem…
michaelnebel Mar 20, 2026
81fd681
C#: Upgrade libraries and queries to use the new Operation classes.
michaelnebel Mar 20, 2026
30380b4
C#: Add/update tests and expected output.
michaelnebel Mar 20, 2026
2425afc
C#: Add operation types to the DB scheme.
michaelnebel Mar 23, 2026
c47160c
C#: Use the DB types and replace the abstract class implementation.
michaelnebel Mar 23, 2026
7399480
C#: Adress review comments.
michaelnebel Mar 23, 2026
003e48e
C#: Remove expr_parent_adjusted.
michaelnebel Mar 23, 2026
63ec1dd
Cfg: Support Throw expressions.
aschackmull Mar 5, 2026
07036c3
Cfg: Support GotoStmt.
aschackmull Mar 5, 2026
578f97f
Cfg: Support short-circuiting compound assignments.
aschackmull Mar 3, 2026
86c216d
C#: Introduce ControlFlowElementOrCallable.
aschackmull Mar 9, 2026
128b950
Cfg/Java: Move InstanceOfExpr CFG into shared lib.
aschackmull Mar 11, 2026
064c54e
Cfg: Extend consistency checks.
aschackmull Mar 12, 2026
9526576
C#: Rename ControlFlow::Node to ControlFlowNode.
aschackmull Mar 12, 2026
32dd156
Cfg: Add dominance predicates to shared ControlFlowNode.
aschackmull Mar 18, 2026
a1045bd
C#: Update some references in preparation for CFG swap.
aschackmull Mar 18, 2026
e148d6e
C#: Update some references to ControlFlow::Nodes.
aschackmull Mar 18, 2026
a793712
C#: Rename remaining references to ControlFlow::Nodes.
aschackmull Mar 18, 2026
0822894
C#: Rename ControlFlow::BasicBlock to BasicBlock.
aschackmull Mar 18, 2026
8f9d5d0
C#: Replace CFG.
aschackmull Mar 9, 2026
87458e2
C#: Fix test.
aschackmull Mar 20, 2026
569aae6
C#: Fix UncheckedCastInEquals.
aschackmull Mar 20, 2026
711c3ee
C#: Bugfix in enclosing callable.
aschackmull Mar 24, 2026
031cac9
C#: Fix CFG position of property setter calls.
aschackmull Mar 26, 2026
93163a7
fu: CFG cond context for extension method calls
aschackmull Mar 26, 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 @@ -22,26 +22,12 @@ public static Assignment Create(ExpressionNodeInfo info)

protected override void PopulateExpression(TextWriter trapFile)
{
var operatorKind = OperatorKind;
if (operatorKind.HasValue)
{
// Convert assignment such as `a += b` into `a = a + b`.
var simpleAssignExpr = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.SIMPLE_ASSIGN, this, 2, isCompilerGenerated: true, null));
Create(Context, Syntax.Left, simpleAssignExpr, 1);
var opexpr = new Expression(new ExpressionInfo(Context, Type, Location, operatorKind.Value, simpleAssignExpr, 0, isCompilerGenerated: true, null));
Create(Context, Syntax.Left, opexpr, 0, isCompilerGenerated: true);
Create(Context, Syntax.Right, opexpr, 1);
opexpr.OperatorCall(trapFile, Syntax);
}
else
{
Create(Context, Syntax.Left, this, 1);
Create(Context, Syntax.Right, this, 0);
Create(Context, Syntax.Left, this, 0);
Create(Context, Syntax.Right, this, 1);

if (Kind == ExprKind.ADD_EVENT || Kind == ExprKind.REMOVE_EVENT)
{
OperatorCall(trapFile, Syntax);
}
if (Kind != ExprKind.SIMPLE_ASSIGN && Kind != ExprKind.ASSIGN_COALESCE)
{
OperatorCall(trapFile, Syntax);
}
}

Expand Down Expand Up @@ -108,56 +94,5 @@ private static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax)

return kind;
}

/// <summary>
/// Gets the kind of this assignment operator (<code>null</code> if the
/// assignment is not an assignment operator). For example, the operator
/// kind of `*=` is `*`.
/// </summary>
private ExprKind? OperatorKind
{
get
{
var kind = Kind;
if (kind == ExprKind.REMOVE_EVENT || kind == ExprKind.ADD_EVENT || kind == ExprKind.SIMPLE_ASSIGN)
return null;

if (CallType.AdjustKind(kind) == ExprKind.OPERATOR_INVOCATION)
return ExprKind.OPERATOR_INVOCATION;

switch (kind)
{
case ExprKind.ASSIGN_ADD:
return ExprKind.ADD;
case ExprKind.ASSIGN_AND:
return ExprKind.BIT_AND;
case ExprKind.ASSIGN_DIV:
return ExprKind.DIV;
case ExprKind.ASSIGN_LSHIFT:
return ExprKind.LSHIFT;
case ExprKind.ASSIGN_MUL:
return ExprKind.MUL;
case ExprKind.ASSIGN_OR:
return ExprKind.BIT_OR;
case ExprKind.ASSIGN_REM:
return ExprKind.REM;
case ExprKind.ASSIGN_RSHIFT:
return ExprKind.RSHIFT;
case ExprKind.ASSIGN_URSHIFT:
return ExprKind.URSHIFT;
case ExprKind.ASSIGN_SUB:
return ExprKind.SUB;
case ExprKind.ASSIGN_XOR:
return ExprKind.BIT_XOR;
case ExprKind.ASSIGN_COALESCE:
return ExprKind.NULL_COALESCING;
default:
Context.ModelError(Syntax, $"Couldn't unfold assignment of type {kind}");
return ExprKind.UNKNOWN;
}
}
}

public new CallType CallType => GetCallType(Context, Syntax);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,30 +83,31 @@ protected override void PopulateExpression(TextWriter trapFile)
{
var assignmentInfo = new ExpressionNodeInfo(Context, init, this, child++).SetKind(ExprKind.SIMPLE_ASSIGN);
var assignmentEntity = new Expression(assignmentInfo);
var typeInfoRight = Context.GetTypeInfo(assignment.Right);
if (typeInfoRight.Type is null)
// The type may be null for nested initializers such as
// ```csharp
// new ClassWithArrayField() { As = { [0] = a } }
// ```
// In this case we take the type from the assignment
// `As = { [0] = a }` instead
typeInfoRight = assignmentInfo.TypeInfo;
CreateFromNode(new ExpressionNodeInfo(Context, assignment.Right, assignmentEntity, 0, typeInfoRight));

var target = Context.GetSymbolInfo(assignment.Left);

// If the target is null, then assume that this is an array initializer (of the form `[...] = ...`)

var access = target.Symbol is null ?
new Expression(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 1).SetKind(ExprKind.ARRAY_ACCESS)) :
Access.Create(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 1), target.Symbol, false, Context.CreateEntity(target.Symbol));
new Expression(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 0).SetKind(ExprKind.ARRAY_ACCESS)) :
Access.Create(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 0), target.Symbol, false, Context.CreateEntity(target.Symbol));

if (assignment.Left is ImplicitElementAccessSyntax iea)
{
// An array/indexer initializer of the form `[...] = ...`
access.PopulateArguments(trapFile, iea.ArgumentList.Arguments, 0);
}

var typeInfoRight = Context.GetTypeInfo(assignment.Right);
if (typeInfoRight.Type is null)
{
// The type may be null for nested initializers such as
// ```csharp
// new ClassWithArrayField() { As = { [0] = a } }
// ```
// In this case we take the type from the assignment
// `As = { [0] = a }` instead
typeInfoRight = assignmentInfo.TypeInfo;
}
CreateFromNode(new ExpressionNodeInfo(Context, assignment.Right, assignmentEntity, 1, typeInfoRight));
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ protected override void PopulateExpression(TextWriter trapFile)
var loc = Context.CreateLocation(init.GetLocation());

var assignment = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, isCompilerGenerated: false, null));
Create(Context, init.Expression, assignment, 0);
Property.Create(Context, property);

var access = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 1, isCompilerGenerated: false, null));
var access = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 0, isCompilerGenerated: false, null));
trapFile.expr_access(access, propEntity);

Create(Context, init.Expression, assignment, 1);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ protected Expression DeclareRangeVariable(Context cx, IExpressionParentEntity pa
child
);

Expression.Create(cx, Expr, decl, 0);

var nameLoc = cx.CreateLocation(name.GetLocation());
var access = new Expression(new ExpressionInfo(cx, type, nameLoc, ExprKind.LOCAL_VARIABLE_ACCESS, decl, 1, isCompilerGenerated: false, null));
var access = new Expression(new ExpressionInfo(cx, type, nameLoc, ExprKind.LOCAL_VARIABLE_ACCESS, decl, 0, isCompilerGenerated: false, null));
cx.TrapWriter.Writer.expr_access(access, LocalVariable.Create(cx, variableSymbol));

Expression.Create(cx, Expr, decl, 1);

return decl;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,11 @@ public static VariableDeclaration CreateDeclarator(Context cx, VariableDeclarato

if (d.Initializer is not null)
{
Create(cx, d.Initializer.Value, ret, 0);

// Create an access
var access = new Expression(new ExpressionInfo(cx, type, localVar.Location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 1, isCompilerGenerated: false, null));
var access = new Expression(new ExpressionInfo(cx, type, localVar.Location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 0, isCompilerGenerated: false, null));
cx.TrapWriter.Writer.expr_access(access, localVar);

Create(cx, d.Initializer.Value, ret, 1);
}

if (d.Parent is VariableDeclarationSyntax decl)
Expand Down
4 changes: 2 additions & 2 deletions csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ private Expression AddInitializerAssignment(TextWriter trapFile, ExpressionSynta
{
var type = Symbol.GetAnnotatedType();
var simpleAssignExpr = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, isCompilerGenerated: true, constValue));
Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 0));
var access = new Expression(new ExpressionInfo(Context, type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, isCompilerGenerated: true, constValue));
var access = new Expression(new ExpressionInfo(Context, type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 0, isCompilerGenerated: true, constValue));
trapFile.expr_access(access, this);
Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 1));
return access;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ public override void Populate(TextWriter trapFile)
var loc = Context.CreateLocation(initializer!.GetLocation());
var annotatedType = AnnotatedTypeSymbol.CreateNotAnnotated(Symbol.Type);
var simpleAssignExpr = new Expression(new ExpressionInfo(Context, annotatedType, loc, ExprKind.SIMPLE_ASSIGN, this, child++, isCompilerGenerated: true, null));
Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 0));
var access = new Expression(new ExpressionInfo(Context, annotatedType, Location, ExprKind.PROPERTY_ACCESS, simpleAssignExpr, 1, isCompilerGenerated: true, null));
var access = new Expression(new ExpressionInfo(Context, annotatedType, Location, ExprKind.PROPERTY_ACCESS, simpleAssignExpr, 0, isCompilerGenerated: true, null));
trapFile.expr_access(access, this);
Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 1));
if (!Symbol.IsStatic)
{
This.CreateImplicit(Context, Symbol.ContainingType, Location, access, -1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,14 @@ 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(BitwiseXorExpr xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
loop.getAControlFlowExitNode().getASuccessor*() = xor2.getAControlFlowNode() and
xor2.getAnOperand() = v.getAnAccess()
)
or
exists(AssignXorExpr xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
loop.getAControlFlowExitNode().getASuccessor*() = xor2.getAControlFlowNode() and
xor2.getAnOperand() = v.getAnAccess()
)
exists(BitwiseXorOperation xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
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 $@.",
additional_xor, "additional xor", v, v.toString()
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
25 changes: 0 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,16 +55,6 @@ 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()
)
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private predicate maybeUsedInElfHashFunction(Variable v, Operation xor, Operatio
Expr e1, Expr e2, AssignExpr addAssign, AssignExpr xorAssign, Operation notOp,
AssignExpr notAssign
|
(add instanceof AddExpr or add instanceof AssignAddExpr) and
add instanceof AddOperation and
e1.getAChild*() = add.getAnOperand() and
e1 instanceof BinaryBitwiseOperation and
e2 = e1.(BinaryBitwiseOperation).getLeftOperand() and
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>
Loading
Loading