Skip to content

Commit 85f275c

Browse files
authored
Merge pull request #1347 from hvitved/csharp/dataflow/this-flow
C#: Data flow through `this` parameter
2 parents 9fb61d5 + c82a2f0 commit 85f275c

File tree

9 files changed

+1377
-97
lines changed

9 files changed

+1377
-97
lines changed

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

Lines changed: 174 additions & 93 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 {
@@ -173,6 +228,14 @@ module DataFlowPrivateCached {
173228
or
174229
any(ArgumentNode n).argumentOf(_, _)
175230
or
231+
exists(any(Node n).getEnclosingCallable())
232+
or
233+
exists(any(Node n).getControlFlowNode())
234+
or
235+
exists(any(Node n).getType())
236+
or
237+
exists(any(Node n).getLocation())
238+
or
176239
exists(any(Node n).toString())
177240
or
178241
exists(any(OutNode n).getCall())
@@ -183,6 +246,7 @@ module DataFlowPrivateCached {
183246
TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof Expr } or
184247
TCilExprNode(CIL::Expr e) { e.getImplementation() instanceof CIL::BestImplementation } or
185248
TSsaDefinitionNode(Ssa::Definition def) or
249+
TInstanceParameterNode(Callable c) { c.hasBody() and not c.(Modifiable).isStatic() } or
186250
TCilParameterNode(CIL::Parameter p) { p.getMethod().hasBody() } or
187251
TTaintedParameterNode(Parameter p) { p.getCallable().hasBody() } or
188252
TTaintedReturnNode(ControlFlow::Nodes::ElementNode cfn) {
@@ -198,97 +262,8 @@ module DataFlowPrivateCached {
198262
) {
199263
cfn.getElement() instanceof DelegateArgumentToLibraryCallable and
200264
any(DelegateArgumentConfiguration x).hasExprPath(_, cfn, _, call)
201-
}
202-
203-
cached
204-
DotNet::Callable getEnclosingCallable(Node node) {
205-
result = node.(ExprNode).getExpr().getEnclosingCallable()
206-
or
207-
result = node.(SsaDefinitionNode).getDefinition().getEnclosingCallable()
208-
or
209-
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.getCallable())
210-
or
211-
result = getEnclosingCallable(node.(TaintedParameterNode).getUnderlyingNode())
212-
or
213-
result = getEnclosingCallable(node.(TaintedReturnNode).getUnderlyingNode())
214-
or
215-
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitCapturedArgumentNode(cfn, _) |
216-
result = cfn.getEnclosingCallable()
217-
)
218-
or
219-
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
220-
result = cfn.getEnclosingCallable()
221-
)
222-
}
223-
224-
cached
225-
DotNet::Type getType(Node node) {
226-
result = node.(ExprNode).getExpr().getType()
227-
or
228-
result = node.(SsaDefinitionNode).getDefinition().getSourceVariable().getType()
229-
or
230-
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.getType())
231-
or
232-
result = getType(node.(TaintedParameterNode).getUnderlyingNode())
233-
or
234-
result = getType(node.(TaintedReturnNode).getUnderlyingNode())
235-
or
236-
exists(LocalScopeVariable v | node = TImplicitCapturedArgumentNode(_, v) | result = v.getType())
237-
or
238-
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
239-
result = cfn.getElement().(Expr).getType()
240-
)
241-
}
242-
243-
cached
244-
Location getLocation(Node node) {
245-
result = node.(ExprNode).getExpr().getLocation()
246-
or
247-
result = node.(SsaDefinitionNode).getDefinition().getLocation()
248-
or
249-
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.getLocation())
250-
or
251-
result = getLocation(node.(TaintedParameterNode).getUnderlyingNode())
252-
or
253-
result = getLocation(node.(TaintedReturnNode).getUnderlyingNode())
254-
or
255-
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitCapturedArgumentNode(cfn, _) |
256-
result = cfn.getLocation()
257-
)
258-
or
259-
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
260-
result = cfn.getLocation()
261-
)
262-
}
263-
264-
cached
265-
string toString(Node node) {
266-
exists(ControlFlow::Nodes::ElementNode cfn | node = TExprNode(cfn) | result = cfn.toString())
267-
or
268-
node = TCilExprNode(_) and
269-
result = "CIL expression"
270-
or
271-
exists(Parameter p | explicitParameterNode(node, p) | result = p.toString())
272-
or
273-
exists(Ssa::Definition def | def = node.(SsaDefinitionNode).getDefinition() |
274-
not explicitParameterNode(node, _) and
275-
result = def.toString()
276-
)
277-
or
278-
exists(CIL::Parameter p | node = TCilParameterNode(p) | result = p.toString())
279-
or
280-
result = toString(node.(TaintedParameterNode).getUnderlyingNode())
281-
or
282-
result = toString(node.(TaintedReturnNode).getUnderlyingNode())
283-
or
284-
exists(LocalScopeVariable v | node = TImplicitCapturedArgumentNode(_, v) |
285-
result = "[implicit argument] " + v
286-
)
287-
or
288-
exists(ControlFlow::Nodes::ElementNode cfn | node = TImplicitDelegateOutNode(cfn, _) |
289-
result = "[output] " + cfn
290-
)
291-
}
265+
} or
266+
TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation }
292267

293268
/**
294269
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
@@ -313,6 +288,8 @@ module DataFlowPrivateCached {
313288
nodeTo = TExprNode(cfnTo)
314289
)
315290
or
291+
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
292+
or
316293
// Flow into SSA pseudo definition
317294
exists(Ssa::Definition def, Ssa::PseudoDefinition pseudo |
318295
LocalFlow::localFlowSsaInput(nodeFrom, def)
@@ -358,6 +335,17 @@ class SsaDefinitionNode extends Node, TSsaDefinitionNode {
358335

359336
/** Gets the underlying SSA definition. */
360337
Ssa::Definition getDefinition() { result = def }
338+
339+
override Callable getEnclosingCallable() { result = def.getEnclosingCallable() }
340+
341+
override Type getType() { result = def.getSourceVariable().getType() }
342+
343+
override Location getLocation() { result = def.getLocation() }
344+
345+
override string toString() {
346+
not explicitParameterNode(this, _) and
347+
result = def.toString()
348+
}
361349
}
362350

363351
private module ParameterNodes {
@@ -388,14 +376,42 @@ private module ParameterNodes {
388376
override DotNet::Parameter getParameter() { result = parameter }
389377

390378
override predicate isParameterOf(DotNet::Callable c, int i) { c.getParameter(i) = parameter }
379+
380+
override DotNet::Callable getEnclosingCallable() { result = parameter.getCallable() }
381+
382+
override DotNet::Type getType() { result = parameter.getType() }
383+
384+
override Location getLocation() { result = parameter.getLocation() }
385+
386+
override string toString() { result = parameter.toString() }
387+
}
388+
389+
/** An implicit instance (`this`) parameter. */
390+
class InstanceParameterNode extends ParameterNode, TInstanceParameterNode {
391+
private Callable callable;
392+
393+
InstanceParameterNode() { this = TInstanceParameterNode(callable) }
394+
395+
/** Gets the callable containing this implicit instance parameter. */
396+
Callable getCallable() { result = callable }
397+
398+
override predicate isParameterOf(DotNet::Callable c, int pos) { callable = c and pos = -1 }
399+
400+
override Callable getEnclosingCallable() { result = callable }
401+
402+
override Type getType() { result = callable.getDeclaringType() }
403+
404+
override Location getLocation() { result = callable.getLocation() }
405+
406+
override string toString() { result = "this" }
391407
}
392408

393409
/**
394410
* A tainted parameter. Tainted parameters are a mere implementation detail, used
395411
* to restrict tainted flow into callables to just taint tracking (just like flow
396412
* out of `TaintedReturnNode`s is restricted to taint tracking).
397413
*/
398-
class TaintedParameterNode extends ParameterNode {
414+
class TaintedParameterNode extends ParameterNode, TTaintedParameterNode {
399415
private Parameter parameter;
400416

401417
TaintedParameterNode() { this = TTaintedParameterNode(parameter) }
@@ -412,6 +428,16 @@ private module ParameterNodes {
412428
// the actual parameters
413429
i = parameter.getPosition() + c.getNumberOfParameters()
414430
}
431+
432+
override Callable getEnclosingCallable() {
433+
result = this.getUnderlyingNode().getEnclosingCallable()
434+
}
435+
436+
override Type getType() { result = this.getUnderlyingNode().getType() }
437+
438+
override Location getLocation() { result = this.getUnderlyingNode().getLocation() }
439+
440+
override string toString() { result = this.getUnderlyingNode().toString() }
415441
}
416442

417443
module ImplicitCapturedParameterNodeImpl {
@@ -539,7 +565,11 @@ private module ArgumentNodes {
539565
}
540566

541567
private DotNet::Expr getArgument(DotNet::Expr call, int i) {
542-
call = any(DispatchCall dc | result = dc.getArgument(i)).getCall()
568+
call = any(DispatchCall dc |
569+
result = dc.getArgument(i)
570+
or
571+
result = dc.getQualifier() and i = -1 and not dc.getAStaticTarget().(Modifiable).isStatic()
572+
).getCall()
543573
or
544574
result = call.(DelegateCall).getArgument(i)
545575
or
@@ -649,6 +679,39 @@ private module ArgumentNodes {
649679
)
650680
)
651681
}
682+
683+
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
684+
685+
override Type getType() { result = v.getType() }
686+
687+
override Location getLocation() { result = cfn.getLocation() }
688+
689+
override string toString() { result = "[implicit argument] " + v }
690+
}
691+
692+
/**
693+
* A node that corresponds to the value of an object creation (`new C()`) before
694+
* the constructor has run.
695+
*/
696+
class MallocNode extends ArgumentNode, TMallocNode {
697+
private ControlFlow::Nodes::ElementNode cfn;
698+
699+
MallocNode() { this = TMallocNode(cfn) }
700+
701+
override predicate argumentOf(DataFlowCall call, int pos) {
702+
call = TNonDelegateCall(cfn, _) and
703+
pos = -1
704+
}
705+
706+
override ControlFlow::Node getControlFlowNode() { result = cfn }
707+
708+
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
709+
710+
override Type getType() { result = cfn.getElement().(Expr).getType() }
711+
712+
override Location getLocation() { result = cfn.getLocation() }
713+
714+
override string toString() { result = "malloc" }
652715
}
653716
}
654717
import ArgumentNodes
@@ -712,6 +775,16 @@ private module ReturnNodes {
712775
ExprReturnNode getUnderlyingNode() { result.getControlFlowNode() = cfn }
713776

714777
override YieldReturnKind getKind() { any() }
778+
779+
override Callable getEnclosingCallable() {
780+
result = this.getUnderlyingNode().getEnclosingCallable()
781+
}
782+
783+
override Type getType() { result = this.getUnderlyingNode().getType() }
784+
785+
override Location getLocation() { result = this.getUnderlyingNode().getLocation() }
786+
787+
override string toString() { result = this.getUnderlyingNode().toString() }
715788
}
716789

717790
/**
@@ -849,6 +922,14 @@ private module OutNodes {
849922
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
850923

851924
override ImplicitDelegateDataFlowCall getCall() { result.getNode() = this }
925+
926+
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
927+
928+
override Type getType() { result = cfn.getElement().(Expr).getType() }
929+
930+
override Location getLocation() { result = cfn.getLocation() }
931+
932+
override string toString() { result = "[output] " + cfn }
852933
}
853934
}
854935
import OutNodes

0 commit comments

Comments
 (0)