Skip to content

Commit 377ef59

Browse files
authored
Merge branch 'main' into 13332-codeql-model-editor-csharp
2 parents efff014 + ab11068 commit 377ef59

File tree

55 files changed

+2190
-2107
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2190
-2107
lines changed

cpp/ql/test/library-tests/special_members/generated_copy/functions.expected

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
| copy.cpp:13:9:13:9 | operator= | protected_cc::Sub2& protected_cc::Sub2::operator=(protected_cc::Sub2 const&) | | |
1212
| copy.cpp:13:9:13:9 | operator= | protected_cc::Sub2& protected_cc::Sub2::operator=(protected_cc::Sub2&&) | | |
1313
| copy.cpp:17:9:17:9 | HasMember | void protected_cc::HasMember::HasMember() | deleted | |
14-
| copy.cpp:17:9:17:9 | HasMember | void protected_cc::HasMember::HasMember(protected_cc::HasMember const&) | | |
15-
| copy.cpp:17:9:17:9 | HasMember | void protected_cc::HasMember::HasMember(protected_cc::HasMember&&) | | |
14+
| copy.cpp:17:9:17:9 | HasMember | void protected_cc::HasMember::HasMember(protected_cc::HasMember const&) | deleted | |
1615
| copy.cpp:17:9:17:9 | operator= | protected_cc::HasMember& protected_cc::HasMember::operator=(protected_cc::HasMember const&) | | |
1716
| copy.cpp:17:9:17:9 | operator= | protected_cc::HasMember& protected_cc::HasMember::operator=(protected_cc::HasMember&&) | | |
1817
| copy.cpp:25:5:25:5 | C | void deleted_cc::C::C(deleted_cc::C const&) | deleted | |

cpp/ql/test/library-tests/specifiers2/specifiers2.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@
186186
| Variable | specifiers2pp.cpp:16:13:16:22 | privateInt | privateInt | private |
187187
| Variable | specifiers2pp.cpp:17:21:17:30 | mutableInt | mutableInt | private |
188188
| Variable | specifiers2pp.cpp:20:13:20:24 | protectedInt | protectedInt | protected |
189-
| Variable | specifiers2pp.cpp:52:25:52:27 | vci | vci | static |
190189
| VariableDeclarationEntry | specifiers2.c:5:12:5:12 | declaration of i | i | extern |
191190
| VariableDeclarationEntry | specifiers2.c:6:12:6:12 | declaration of i | i | extern |
192191
| VariableDeclarationEntry | specifiers2.c:8:12:8:12 | declaration of j | j | extern |

csharp/ql/consistency-queries/DataFlowConsistency.ql

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ private module Input implements InputSig<CsharpDataFlow> {
4747
or
4848
not exists(LocalFlow::getAPostUpdateNodeForArg(n.getControlFlowNode()))
4949
or
50-
n instanceof ImplicitCapturedArgumentNode
51-
or
5250
n instanceof ParamsArgumentNode
5351
or
5452
n.asExpr() instanceof CIL::Expr
@@ -104,8 +102,6 @@ private module Input implements InputSig<CsharpDataFlow> {
104102
not split = cfn.getASplit()
105103
)
106104
or
107-
call instanceof TransitiveCapturedDataFlowCall
108-
or
109105
call.(NonDelegateDataFlowCall).getDispatchCall().isReflection()
110106
)
111107
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import csharp
2+
import semmle.code.csharp.dataflow.internal.DataFlowPrivate::VariableCapture::Flow::ConsistencyChecks
3+
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate::VariableCapture::Flow::ConsistencyChecks as ConsistencyChecks
4+
private import semmle.code.csharp.controlflow.BasicBlocks
5+
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
6+
7+
query predicate uniqueEnclosingCallable(BasicBlock bb, string msg) {
8+
ConsistencyChecks::uniqueEnclosingCallable(bb, msg) and
9+
getNodeCfgScope(bb.getFirstNode()) instanceof Callable
10+
}
11+
12+
query predicate consistencyOverview(string msg, int n) { none() }
13+
14+
query predicate uniqueCallableLocation(Callable c, string msg) {
15+
ConsistencyChecks::uniqueCallableLocation(c, msg) and
16+
count(c.getBody()) = 1
17+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: majorAnalysis
3+
---
4+
* Improved support for flow through captured variables that properly adheres to inter-procedural control flow.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* C# 12: Add QL library support (`ExperimentalAttribute`) for the experimental attribute.

csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll

Lines changed: 59 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,52 @@ module PreSsa {
3131
}
3232

3333
predicate implicitEntryDef(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v) {
34-
not v instanceof LocalScopeVariable and
3534
c = v.getACallable() and
36-
scopeFirst(c, bb)
35+
scopeFirst(c, bb) and
36+
(
37+
not v instanceof LocalScopeVariable
38+
or
39+
v.(SimpleLocalScopeVariable).isReadonlyCapturedBy(c)
40+
)
41+
}
42+
43+
/** Holds if `a` is assigned in callable `c`. */
44+
pragma[nomagic]
45+
private predicate assignableDefinition(Assignable a, Callable c) {
46+
exists(AssignableDefinition def |
47+
def.getTarget() = a and
48+
c = def.getEnclosingCallable()
49+
|
50+
not c instanceof Constructor or
51+
a instanceof LocalScopeVariable
52+
)
53+
}
54+
55+
pragma[nomagic]
56+
private predicate assignableUniqueWriter(Assignable a, Callable c) {
57+
c = unique(Callable c0 | assignableDefinition(a, c0) | c0)
58+
}
59+
60+
/** Holds if `a` is accessed in callable `c`. */
61+
pragma[nomagic]
62+
private predicate assignableAccess(Assignable a, Callable c) {
63+
exists(AssignableAccess aa | aa.getTarget() = a | c = aa.getEnclosingCallable())
64+
}
65+
66+
/**
67+
* A local scope variable that is amenable to SSA analysis.
68+
*
69+
* This is either a local variable that is not captured, or one
70+
* where all writes happen in the defining callable.
71+
*/
72+
class SimpleLocalScopeVariable extends LocalScopeVariable {
73+
SimpleLocalScopeVariable() { assignableUniqueWriter(this, this.getCallable()) }
74+
75+
/** Holds if this local scope variable is read-only captured by `c`. */
76+
predicate isReadonlyCapturedBy(Callable c) {
77+
assignableAccess(this, c) and
78+
c != this.getCallable()
79+
}
3780
}
3881

3982
module SsaInput implements SsaImplCommon::InputSig<Location> {
@@ -47,40 +90,6 @@ module PreSsa {
4790
ExitBasicBlock() { scopeLast(_, this.getLastElement(), _) }
4891
}
4992

50-
/** Holds if `a` is assigned in non-constructor callable `c`. */
51-
pragma[nomagic]
52-
private predicate assignableDefinition(Assignable a, Callable c) {
53-
exists(AssignableDefinition def | def.getTarget() = a |
54-
c = def.getEnclosingCallable() and
55-
not c instanceof Constructor
56-
)
57-
}
58-
59-
/** Holds if `a` is accessed in callable `c`. */
60-
pragma[nomagic]
61-
private predicate assignableAccess(Assignable a, Callable c) {
62-
exists(AssignableAccess aa | aa.getTarget() = a | c = aa.getEnclosingCallable())
63-
}
64-
65-
pragma[nomagic]
66-
private predicate assignableNoCapturing(Assignable a, Callable c) {
67-
assignableAccess(a, c) and
68-
/*
69-
* The code below is equivalent to
70-
* ```ql
71-
* not exists(Callable other | assignableDefinition(a, other) | other != c)
72-
* ```
73-
* but it avoids a Cartesian product in the compiler generated antijoin
74-
* predicate.
75-
*/
76-
77-
(
78-
not assignableDefinition(a, _)
79-
or
80-
c = unique(Callable c0 | assignableDefinition(a, c0) | c0)
81-
)
82-
}
83-
8493
pragma[noinline]
8594
private predicate assignableNoComplexQualifiers(Assignable a) {
8695
forall(QualifiableExpr qe | qe.(AssignableAccess).getTarget() = a | qe.targetIsThisInstance())
@@ -94,15 +103,22 @@ module PreSsa {
94103
private Callable c;
95104

96105
SourceVariable() {
106+
assignableAccess(this, c) and
97107
(
98-
this instanceof LocalScopeVariable
108+
this instanceof SimpleLocalScopeVariable
99109
or
100-
this = any(Field f | not f.isVolatile())
101-
or
102-
this = any(TrivialProperty tp | not tp.isOverridableOrImplementable())
103-
) and
104-
assignableNoCapturing(this, c) and
105-
assignableNoComplexQualifiers(this)
110+
(
111+
this = any(Field f | not f.isVolatile())
112+
or
113+
this = any(TrivialProperty tp | not tp.isOverridableOrImplementable())
114+
) and
115+
(
116+
not assignableDefinition(this, _)
117+
or
118+
assignableUniqueWriter(this, c)
119+
) and
120+
assignableNoComplexQualifiers(this)
121+
)
106122
}
107123

108124
/** Gets a callable in which this simple assignable can be analyzed. */

csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -218,19 +218,6 @@ private predicate isNullDefaultArgument(Ssa::ExplicitDefinition def, AlwaysNullE
218218
)
219219
}
220220

221-
/**
222-
* Holds if `edef` is an implicit entry definition for a captured variable that
223-
* may be guarded, because a call to the capturing callable is guarded.
224-
*/
225-
private predicate isMaybeGuardedCapturedDef(Ssa::ImplicitEntryDefinition edef) {
226-
exists(Ssa::ExplicitDefinition def, ControlFlow::Nodes::ElementNode c, G::Guard g, NullValue nv |
227-
def.isCapturedVariableDefinitionFlowIn(edef, c, _) and
228-
g = def.getARead() and
229-
g.controlsNode(c, nv) and
230-
nv.isNonNull()
231-
)
232-
}
233-
234221
/** Holds if `def` is an SSA definition that may be `null`. */
235222
private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) {
236223
not nonNullDef(def) and
@@ -268,7 +255,6 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
268255
exists(Dereference d | dereferenceAt(_, _, def, d) |
269256
d.hasNullableType() and
270257
not def instanceof Ssa::PhiNode and
271-
not isMaybeGuardedCapturedDef(def) and
272258
reason = def.getSourceVariable().getAssignable() and
273259
msg = "because it has a nullable type"
274260
)
@@ -583,14 +569,8 @@ class Dereference extends G::DereferenceableExpr {
583569
*/
584570
predicate isAlwaysNull(Ssa::SourceVariable v) {
585571
this = v.getAnAccess() and
586-
// Exclude fields, properties, and captured variables, as they may not have an
587-
// accurate SSA representation
588-
v.getAssignable() =
589-
any(LocalScopeVariable lsv |
590-
strictcount(Callable c |
591-
c = any(AssignableDefinition ad | ad.getTarget() = lsv).getEnclosingCallable()
592-
) = 1
593-
) and
572+
// Exclude fields and properties, as they may not have an accurate SSA representation
573+
v.getAssignable() instanceof LocalScopeVariable and
594574
(
595575
forex(Ssa::Definition def0 | this = def0.getARead() | this.isAlwaysNull0(def0))
596576
or

csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,8 @@ module Ssa {
447447
final AssignableDefinition getADefinition() { result = SsaImpl::getADefinition(this) }
448448

449449
/**
450+
* DEPRECATED.
451+
*
450452
* Holds if this definition updates a captured local scope variable, and the updated
451453
* value may be read from the implicit entry definition `def` using one or more calls
452454
* (as indicated by `additionalCalls`), starting from call `c`.
@@ -467,13 +469,15 @@ module Ssa {
467469
* If this definition is the update of `i` on line 5, then the value may be read inside
468470
* `M2` via the call on line 6.
469471
*/
470-
final predicate isCapturedVariableDefinitionFlowIn(
472+
deprecated final predicate isCapturedVariableDefinitionFlowIn(
471473
ImplicitEntryDefinition def, ControlFlow::Nodes::ElementNode c, boolean additionalCalls
472474
) {
473-
SsaImpl::isCapturedVariableDefinitionFlowIn(this, def, c, additionalCalls)
475+
none()
474476
}
475477

476478
/**
479+
* DEPRECATED.
480+
*
477481
* Holds if this definition updates a captured local scope variable, and the updated
478482
* value may be read from the implicit call definition `cdef` using one or more calls
479483
* (as indicated by `additionalCalls`).
@@ -494,10 +498,10 @@ module Ssa {
494498
* If this definition is the update of `i` on line 4, then the value may be read outside
495499
* of `M2` via the call on line 5.
496500
*/
497-
final predicate isCapturedVariableDefinitionFlowOut(
501+
deprecated final predicate isCapturedVariableDefinitionFlowOut(
498502
ImplicitCallDefinition cdef, boolean additionalCalls
499503
) {
500-
SsaImpl::isCapturedVariableDefinitionFlowOut(this, cdef, additionalCalls)
504+
none()
501505
}
502506

503507
override Element getElement() { result = ad.getElement() }
@@ -526,8 +530,6 @@ module Ssa {
526530
or
527531
SsaImpl::updatesNamedFieldOrProp(bb, i, _, v, _)
528532
or
529-
SsaImpl::updatesCapturedVariable(bb, i, _, v, _, _)
530-
or
531533
SsaImpl::variableWriteQualifier(bb, i, v, _)
532534
)
533535
}
@@ -572,10 +574,9 @@ module Ssa {
572574
private Call c;
573575

574576
ImplicitCallDefinition() {
575-
exists(ControlFlow::BasicBlock bb, SourceVariable v, int i | this.definesAt(v, bb, i) |
577+
exists(ControlFlow::BasicBlock bb, SourceVariable v, int i |
578+
this.definesAt(v, bb, i) and
576579
SsaImpl::updatesNamedFieldOrProp(bb, i, c, v, _)
577-
or
578-
SsaImpl::updatesCapturedVariable(bb, i, c, v, _, _)
579580
)
580581
}
581582

@@ -593,9 +594,6 @@ module Ssa {
593594
result.getEnclosingCallable() = setter and
594595
result.getTarget() = this.getSourceVariable().getAssignable()
595596
)
596-
or
597-
SsaImpl::updatesCapturedVariable(_, _, this.getCall(), _, result, _) and
598-
result.getTarget() = this.getSourceVariable().getAssignable()
599597
}
600598

601599
override string toString() {

csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,16 @@ module BaseSsa {
2424
)
2525
}
2626

27+
private predicate implicitEntryDef(
28+
Callable c, ControlFlow::BasicBlocks::EntryBlock bb, SsaInput::SourceVariable v
29+
) {
30+
v.isReadonlyCapturedBy(c) and
31+
c = bb.getCallable()
32+
}
33+
2734
private module SsaInput implements SsaImplCommon::InputSig<Location> {
35+
private import semmle.code.csharp.controlflow.internal.PreSsa
36+
2837
class BasicBlock = ControlFlow::BasicBlock;
2938

3039
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
@@ -35,20 +44,17 @@ module BaseSsa {
3544

3645
class ExitBasicBlock = ControlFlow::BasicBlocks::ExitBlock;
3746

38-
pragma[noinline]
39-
private Callable getAnAssigningCallable(LocalScopeVariable v) {
40-
result = any(AssignableDefinition def | def.getTarget() = v).getEnclosingCallable()
41-
}
42-
43-
class SourceVariable extends LocalScopeVariable {
44-
SourceVariable() { not getAnAssigningCallable(this) != getAnAssigningCallable(this) }
45-
}
47+
class SourceVariable = PreSsa::SimpleLocalScopeVariable;
4648

4749
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
4850
exists(AssignableDefinition def |
4951
definitionAt(def, bb, i, v) and
5052
if def.isCertain() then certain = true else certain = false
5153
)
54+
or
55+
implicitEntryDef(_, bb, v) and
56+
i = -1 and
57+
certain = true
5258
}
5359

5460
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
@@ -87,7 +93,15 @@ module BaseSsa {
8793
not result instanceof PhiNode
8894
}
8995

90-
Location getLocation() { result = this.getDefinition().getLocation() }
96+
Location getLocation() {
97+
result = this.getDefinition().getLocation()
98+
or
99+
exists(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v |
100+
this.definesAt(v, bb, -1) and
101+
implicitEntryDef(c, bb, v) and
102+
result = c.getLocation()
103+
)
104+
}
91105
}
92106

93107
class PhiNode extends SsaImpl::PhiNode, Definition {

0 commit comments

Comments
 (0)