Skip to content

Commit 2d5c40d

Browse files
authored
Merge pull request #14048 from asgerf/shared/variable-capture-write-source-node
Variable capture: allow arbitrary data-flow nodes to be the source of a write
2 parents c68d0bc + d4cfa8c commit 2d5c40d

File tree

2 files changed

+51
-40
lines changed

2 files changed

+51
-40
lines changed

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,6 @@ private module CaptureInput implements VariableCapture::InputSig {
113113
VariableWrite() { super.getDestVar() = v }
114114

115115
CapturedVariable getVariable() { result = v }
116-
117-
Expr getSource() {
118-
result = this.(VariableAssign).getSource() or
119-
result = this.(AssignOp)
120-
}
121116
}
122117

123118
class VariableRead extends Expr instanceof RValue {
@@ -155,14 +150,27 @@ class CapturedParameter = CaptureInput::CapturedParameter;
155150
module CaptureFlow = VariableCapture::Flow<CaptureInput>;
156151

157152
private CaptureFlow::ClosureNode asClosureNode(Node n) {
158-
result = n.(CaptureNode).getSynthesizedCaptureNode() or
159-
result.(CaptureFlow::ExprNode).getExpr() = n.asExpr() or
153+
result = n.(CaptureNode).getSynthesizedCaptureNode()
154+
or
155+
result.(CaptureFlow::ExprNode).getExpr() = n.asExpr()
156+
or
160157
result.(CaptureFlow::ExprPostUpdateNode).getExpr() =
161-
n.(PostUpdateNode).getPreUpdateNode().asExpr() or
162-
result.(CaptureFlow::ParameterNode).getParameter() = n.asParameter() or
163-
result.(CaptureFlow::ThisParameterNode).getCallable() = n.(InstanceParameterNode).getCallable() or
158+
n.(PostUpdateNode).getPreUpdateNode().asExpr()
159+
or
160+
result.(CaptureFlow::ParameterNode).getParameter() = n.asParameter()
161+
or
162+
result.(CaptureFlow::ThisParameterNode).getCallable() = n.(InstanceParameterNode).getCallable()
163+
or
164164
exprNode(result.(CaptureFlow::MallocNode).getClosureExpr()).(PostUpdateNode).getPreUpdateNode() =
165165
n
166+
or
167+
exists(CaptureInput::VariableWrite write |
168+
result.(CaptureFlow::VariableWriteSourceNode).getVariableWrite() = write
169+
|
170+
n.asExpr() = write.(VariableAssign).getSource()
171+
or
172+
n.asExpr() = write.(AssignOp)
173+
)
166174
}
167175

168176
private predicate captureStoreStep(Node node1, CapturedVariableContent c, Node node2) {

shared/dataflow/codeql/dataflow/VariableCapture.qll

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,6 @@ signature module InputSig {
9595
/** Gets the variable that is the target of this write. */
9696
CapturedVariable getVariable();
9797

98-
/** Gets the expression that is the source of this write. */
99-
Expr getSource();
100-
10198
/** Gets the location of this write. */
10299
Location getLocation();
103100

@@ -210,6 +207,22 @@ signature module OutputSig<InputSig I> {
210207
I::ClosureExpr getClosureExpr();
211208
}
212209

210+
/**
211+
* A node representing the incoming value about to be written at the given assignment.
212+
*
213+
* The captured-variable library will generate flows out of this node, and assume that other
214+
* parts of the language implementation produce the relevant data flows into this node.
215+
*
216+
* For ordinary assignments, this could be mapped to the right-hand side of the assignment.
217+
*
218+
* For more general cases, where an lvalue has no direct corresponding rvalue, this can be mapped
219+
* to a data-flow node that wraps the lvalue, with language-specific incoming data flows.
220+
*/
221+
class VariableWriteSourceNode extends ClosureNode {
222+
/** Gets the variable write for which this node is the incoming value being written to the variable. */
223+
I::VariableWrite getVariableWrite();
224+
}
225+
213226
/** Holds if `post` is a `PostUpdateNode` for `pre`. */
214227
predicate capturePostUpdateNode(SynthesizedCaptureNode post, SynthesizedCaptureNode pre);
215228

@@ -239,7 +252,6 @@ module Flow<InputSig Input> implements OutputSig<Input> {
239252
private class RelevantExpr extends FinalExpr {
240253
RelevantExpr() {
241254
this instanceof VariableRead or
242-
any(VariableWrite vw).getSource() = this or
243255
this instanceof ClosureExpr or
244256
any(ClosureExpr ce).hasAliasedAccess(this)
245257
}
@@ -353,14 +365,6 @@ module Flow<InputSig Input> implements OutputSig<Input> {
353365

354366
query predicate uniqueWriteTarget(string msg) { uniqueWriteTarget(_, msg) }
355367

356-
private predicate uniqueWriteSource(VariableWrite vw, string msg) {
357-
msg = "VariableWrite has no source expression" and not exists(vw.getSource())
358-
or
359-
msg = "VariableWrite has multiple source expressions" and 2 <= strictcount(vw.getSource())
360-
}
361-
362-
query predicate uniqueWriteSource(string msg) { uniqueWriteSource(_, msg) }
363-
364368
private predicate uniqueWriteCfgNode(VariableWrite vw, string msg) {
365369
msg = "VariableWrite has no cfg node" and not vw.hasCfgNode(_, _)
366370
or
@@ -370,17 +374,6 @@ module Flow<InputSig Input> implements OutputSig<Input> {
370374

371375
query predicate uniqueWriteCfgNode(string msg) { uniqueWriteCfgNode(_, msg) }
372376

373-
private predicate localWriteStep(VariableWrite vw, string msg) {
374-
exists(BasicBlock bb1, BasicBlock bb2 |
375-
vw.hasCfgNode(bb1, _) and
376-
vw.getSource().hasCfgNode(bb2, _) and
377-
bb1.getEnclosingCallable() != bb2.getEnclosingCallable() and
378-
msg = "VariableWrite is not a local step"
379-
)
380-
}
381-
382-
query predicate localWriteStep(string msg) { localWriteStep(_, msg) }
383-
384377
query predicate uniqueReadVariable(VariableRead vr, string msg) {
385378
msg = "VariableRead has no source variable" and not exists(vr.getVariable())
386379
or
@@ -436,9 +429,7 @@ module Flow<InputSig Input> implements OutputSig<Input> {
436429
n = strictcount(Expr e | uniqueLocation(e, msg)) or
437430
n = strictcount(Expr e | uniqueCfgNode(e, msg)) or
438431
n = strictcount(VariableWrite vw | uniqueWriteTarget(vw, msg)) or
439-
n = strictcount(VariableWrite vw | uniqueWriteSource(vw, msg)) or
440432
n = strictcount(VariableWrite vw | uniqueWriteCfgNode(vw, msg)) or
441-
n = strictcount(VariableWrite vw | localWriteStep(vw, msg)) or
442433
n = strictcount(VariableRead vr | uniqueReadVariable(vr, msg)) or
443434
n = strictcount(ClosureExpr ce | closureMustHaveBody(ce, msg)) or
444435
n = strictcount(ClosureExpr ce, Expr access | closureAliasMustBeInSameScope(ce, access, msg)) or
@@ -719,13 +710,12 @@ module Flow<InputSig Input> implements OutputSig<Input> {
719710
TExprNode(Expr expr, boolean isPost) {
720711
expr instanceof VariableRead and isPost = [false, true]
721712
or
722-
exists(VariableWrite vw | expr = vw.getSource() and isPost = false)
723-
or
724713
synthRead(_, _, _, _, expr) and isPost = [false, true]
725714
} or
726715
TParamNode(CapturedParameter p) or
727716
TThisParamNode(Callable c) { captureAccess(_, c) } or
728-
TMallocNode(ClosureExpr ce) { hasConstructorCapture(ce, _) }
717+
TMallocNode(ClosureExpr ce) { hasConstructorCapture(ce, _) } or
718+
TVariableWriteSourceNode(VariableWrite write)
729719

730720
class ClosureNode extends TClosureNode {
731721
/** Gets a textual representation of this node. */
@@ -751,6 +741,11 @@ module Flow<InputSig Input> implements OutputSig<Input> {
751741
result = "this" and this = TThisParamNode(_)
752742
or
753743
result = "malloc" and this = TMallocNode(_)
744+
or
745+
exists(VariableWrite write |
746+
this = TVariableWriteSourceNode(write) and
747+
result = "Source of write to " + write.getVariable().toString()
748+
)
754749
}
755750

756751
/** Gets the location of this node. */
@@ -778,6 +773,10 @@ module Flow<InputSig Input> implements OutputSig<Input> {
778773
exists(Callable c | this = TThisParamNode(c) and result = c.getLocation())
779774
or
780775
exists(ClosureExpr ce | this = TMallocNode(ce) and result = ce.getLocation())
776+
or
777+
exists(VariableWrite write |
778+
this = TVariableWriteSourceNode(write) and result = write.getLocation()
779+
)
781780
}
782781
}
783782

@@ -837,6 +836,10 @@ module Flow<InputSig Input> implements OutputSig<Input> {
837836
ClosureExpr getClosureExpr() { this = TMallocNode(result) }
838837
}
839838

839+
class VariableWriteSourceNode extends ClosureNode, TVariableWriteSourceNode {
840+
VariableWrite getVariableWrite() { this = TVariableWriteSourceNode(result) }
841+
}
842+
840843
predicate capturePostUpdateNode(SynthesizedCaptureNode post, SynthesizedCaptureNode pre) {
841844
exists(CapturedVariable v, BasicBlock bb, int i |
842845
pre = TSynthRead(v, bb, i, false) and post = TSynthRead(v, bb, i, true)
@@ -881,7 +884,7 @@ module Flow<InputSig Input> implements OutputSig<Input> {
881884
or
882885
exists(VariableWrite vw, CapturedVariable v |
883886
captureWrite(v, bb, i, true, vw) and
884-
n = TExprNode(vw.getSource(), false) and
887+
n = TVariableWriteSourceNode(vw) and
885888
isPost = false and
886889
cc = TVariable(v)
887890
)
@@ -928,7 +931,7 @@ module Flow<InputSig Input> implements OutputSig<Input> {
928931
// write to v inside the closure body
929932
exists(BasicBlock bb, int i, VariableWrite vw |
930933
captureWrite(v, bb, i, false, vw) and
931-
node1 = TExprNode(vw.getSource(), false) and
934+
node1 = TVariableWriteSourceNode(vw) and
932935
node2 = TSynthThisQualifier(bb, i, true)
933936
)
934937
}

0 commit comments

Comments
 (0)