Skip to content

Commit e1d4166

Browse files
committed
C#: Data flow through this parameter
1 parent bc00877 commit e1d4166

File tree

8 files changed

+1274
-2
lines changed

8 files changed

+1274
-2
lines changed

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,61 @@ private import semmle.code.csharp.dispatch.Dispatch
1111
private import semmle.code.csharp.frameworks.EntityFramework
1212
private import semmle.code.csharp.frameworks.NHibernate
1313

14+
/** Calculation of the relative order in which `this` references are read. */
15+
private module ThisFlow {
16+
class BasicBlock = ControlFlow::BasicBlock;
17+
18+
/** Holds if `n` is a `this` access at control flow node `cfn`. */
19+
private predicate thisAccess(Node n, ControlFlow::Node cfn) {
20+
n.(InstanceParameterNode).getCallable() = cfn.(ControlFlow::Nodes::EntryNode).getCallable()
21+
or
22+
n.asExprAtNode(cfn) = any(Expr e | e instanceof ThisAccess or e instanceof BaseAccess)
23+
}
24+
25+
private predicate thisAccess(Node n, BasicBlock bb, int i) { thisAccess(n, bb.getNode(i)) }
26+
27+
private predicate thisRank(Node n, BasicBlock bb, int rankix) {
28+
exists(int i |
29+
i = rank[rankix](int j | thisAccess(_, bb, j)) and
30+
thisAccess(n, bb, i)
31+
)
32+
}
33+
34+
private int lastRank(BasicBlock bb) { result = max(int rankix | thisRank(_, bb, rankix)) }
35+
36+
private predicate blockPrecedesThisAccess(BasicBlock bb) { thisAccess(_, bb.getASuccessor*(), _) }
37+
38+
private predicate thisAccessBlockReaches(BasicBlock bb1, BasicBlock bb2) {
39+
thisAccess(_, bb1, _) and bb2 = bb1.getASuccessor()
40+
or
41+
exists(BasicBlock mid |
42+
thisAccessBlockReaches(bb1, mid) and
43+
bb2 = mid.getASuccessor() and
44+
not thisAccess(_, mid, _) and
45+
blockPrecedesThisAccess(bb2)
46+
)
47+
}
48+
49+
private predicate thisAccessBlockStep(BasicBlock bb1, BasicBlock bb2) {
50+
thisAccessBlockReaches(bb1, bb2) and
51+
thisAccess(_, bb2, _)
52+
}
53+
54+
/** Holds if `n1` and `n2` are control-flow adjacent references to `this`. */
55+
predicate adjacentThisRefs(Node n1, Node n2) {
56+
exists(int rankix, BasicBlock bb |
57+
thisRank(n1, bb, rankix) and
58+
thisRank(n2, bb, rankix + 1)
59+
)
60+
or
61+
exists(BasicBlock bb1, BasicBlock bb2 |
62+
thisRank(n1, bb1, lastRank(bb1)) and
63+
thisAccessBlockStep(bb1, bb2) and
64+
thisRank(n2, bb2, 1)
65+
)
66+
}
67+
}
68+
1469
/** Provides predicates related to local data flow. */
1570
module LocalFlow {
1671
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -183,6 +238,7 @@ module DataFlowPrivateCached {
183238
TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof Expr } or
184239
TCilExprNode(CIL::Expr e) { e.getImplementation() instanceof CIL::BestImplementation } or
185240
TSsaDefinitionNode(Ssa::Definition def) or
241+
TInstanceParameterNode(Callable c) { c.hasBody() and not c.(Modifiable).isStatic() } or
186242
TCilParameterNode(CIL::Parameter p) { p.getMethod().hasBody() } or
187243
TTaintedParameterNode(Parameter p) { p.getCallable().hasBody() } or
188244
TTaintedReturnNode(ControlFlow::Nodes::ElementNode cfn) {
@@ -198,14 +254,17 @@ module DataFlowPrivateCached {
198254
) {
199255
cfn.getElement() instanceof DelegateArgumentToLibraryCallable and
200256
any(DelegateArgumentConfiguration x).hasExprPath(_, cfn, _, call)
201-
}
257+
} or
258+
TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation }
202259

203260
cached
204261
DotNet::Callable getEnclosingCallable(Node node) {
205262
result = node.(ExprNode).getExpr().getEnclosingCallable()
206263
or
207264
result = node.(SsaDefinitionNode).getDefinition().getEnclosingCallable()
208265
or
266+
node = TInstanceParameterNode(result)
267+
or
209268
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.getCallable())
210269
or
211270
result = getEnclosingCallable(node.(TaintedParameterNode).getUnderlyingNode())
@@ -219,6 +278,10 @@ module DataFlowPrivateCached {
219278
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
220279
result = cfn.getEnclosingCallable()
221280
)
281+
or
282+
exists(ControlFlow::Nodes::ElementNode cfn | node = TMallocNode(cfn) |
283+
result = cfn.getEnclosingCallable()
284+
)
222285
}
223286

224287
cached
@@ -227,6 +290,8 @@ module DataFlowPrivateCached {
227290
or
228291
result = node.(SsaDefinitionNode).getDefinition().getSourceVariable().getType()
229292
or
293+
exists(Callable c | node = TInstanceParameterNode(c) | result = c.getDeclaringType())
294+
or
230295
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.getType())
231296
or
232297
result = getType(node.(TaintedParameterNode).getUnderlyingNode())
@@ -238,6 +303,10 @@ module DataFlowPrivateCached {
238303
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
239304
result = cfn.getElement().(Expr).getType()
240305
)
306+
or
307+
exists(ControlFlow::Nodes::ElementNode cfn | node = TMallocNode(cfn) |
308+
result = cfn.getElement().(Expr).getType()
309+
)
241310
}
242311

243312
cached
@@ -246,6 +315,8 @@ module DataFlowPrivateCached {
246315
or
247316
result = node.(SsaDefinitionNode).getDefinition().getLocation()
248317
or
318+
exists(Callable c | node = TInstanceParameterNode(c) | result = c.getLocation())
319+
or
249320
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.getLocation())
250321
or
251322
result = getLocation(node.(TaintedParameterNode).getUnderlyingNode())
@@ -259,6 +330,10 @@ module DataFlowPrivateCached {
259330
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
260331
result = cfn.getLocation()
261332
)
333+
or
334+
exists(ControlFlow::Nodes::ElementNode cfn | node = TMallocNode(cfn) |
335+
result = cfn.getLocation()
336+
)
262337
}
263338

264339
cached
@@ -275,6 +350,8 @@ module DataFlowPrivateCached {
275350
result = def.toString()
276351
)
277352
or
353+
node = TInstanceParameterNode(_) and result = "this"
354+
or
278355
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.toString())
279356
or
280357
result = toString(node.(TaintedParameterNode).getUnderlyingNode())
@@ -288,6 +365,8 @@ module DataFlowPrivateCached {
288365
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
289366
result = "[output] " + cfn
290367
)
368+
or
369+
node = TMallocNode(_) and result = "malloc"
291370
}
292371

293372
/**
@@ -313,6 +392,8 @@ module DataFlowPrivateCached {
313392
nodeTo = TExprNode(cfnTo)
314393
)
315394
or
395+
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
396+
or
316397
// Flow into SSA pseudo definition
317398
exists(Ssa::Definition def, Ssa::PseudoDefinition pseudo |
318399
LocalFlow::localFlowSsaInput(nodeFrom, def)
@@ -390,6 +471,18 @@ private module ParameterNodes {
390471
override predicate isParameterOf(DotNet::Callable c, int i) { c.getParameter(i) = parameter }
391472
}
392473

474+
/** An implicit instance (`this`) parameter. */
475+
class InstanceParameterNode extends ParameterNode, TInstanceParameterNode {
476+
private Callable callable;
477+
478+
InstanceParameterNode() { this = TInstanceParameterNode(callable) }
479+
480+
/** Gets the callable containing this implicit instance parameter. */
481+
Callable getCallable() { result = callable }
482+
483+
override predicate isParameterOf(DotNet::Callable c, int pos) { callable = c and pos = -1 }
484+
}
485+
393486
/**
394487
* A tainted parameter. Tainted parameters are a mere implementation detail, used
395488
* to restrict tainted flow into callables to just taint tracking (just like flow
@@ -539,7 +632,11 @@ private module ArgumentNodes {
539632
}
540633

541634
private DotNet::Expr getArgument(DotNet::Expr call, int i) {
542-
call = any(DispatchCall dc | result = dc.getArgument(i)).getCall()
635+
call = any(DispatchCall dc |
636+
result = dc.getArgument(i)
637+
or
638+
result = dc.getQualifier() and i = -1 and not dc.getAStaticTarget().(Modifiable).isStatic()
639+
).getCall()
543640
or
544641
result = call.(DelegateCall).getArgument(i)
545642
or
@@ -650,6 +747,23 @@ private module ArgumentNodes {
650747
)
651748
}
652749
}
750+
751+
/**
752+
* A node that corresponds to the value of an object creation (`new C()`) before
753+
* the constructor has run.
754+
*/
755+
class MallocNode extends ArgumentNode, TMallocNode {
756+
private ControlFlow::Nodes::ElementNode cfn;
757+
758+
MallocNode() { this = TMallocNode(cfn) }
759+
760+
override predicate argumentOf(DataFlowCall call, int pos) {
761+
call = TNonDelegateCall(cfn, _) and
762+
pos = -1
763+
}
764+
765+
override ControlFlow::Node getControlFlowNode() { result = cfn }
766+
}
653767
}
654768
import ArgumentNodes
655769

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class ParameterNode extends Node {
8282
explicitParameterNode(this, _) or
8383
this.(SsaDefinitionNode).getDefinition() instanceof
8484
ImplicitCapturedParameterNodeImpl::SsaCapturedEntryDefinition or
85+
this = C::TInstanceParameterNode(_) or
8586
this = C::TCilParameterNode(_) or
8687
this = C::TTaintedParameterNode(_)
8788
}

0 commit comments

Comments
 (0)