Skip to content

Commit 4d89446

Browse files
authored
Merge pull request #22809 from asgerf/unified/flow-summaries
Unified: Add flow summary support
2 parents 5be6d86 + be58844 commit 4d89446

25 files changed

Lines changed: 806 additions & 95 deletions

‎unified/ql/consistency-queries/DataFlowConsistency.ql‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ module ConsistencyInput implements InputSig<Location, DataFlowInput> {
1212
// have post-update nodes for reverse-reads.
1313
any(DataFlowPlugin p).step(n, any(Step s | s.read(_)), _)
1414
}
15+
16+
predicate postWithInFlowExclude(DataFlow::Node n) {
17+
// In summaries, 'inout' parameters result in flow directly into the post-update node for an argument
18+
n instanceof TSummaryNode
19+
}
1520
}
1621

1722
module ConsistencyOutput =

‎unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import DataFlowGraph
1010
import DataFlowInstantiation
1111
import DataFlowNode
1212
import DataFlowPlugin
13+
import FlowSummary
1314
import LocalSsa
1415
import ParameterPositions
1516
import Step

‎unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ private import codeql.unified.internal.typeinference.TypeInference as T
44

55
DataFlowCallable viableCallable(DataFlowCall c) {
66
result.asSourceCallable() = T::resolveCallTarget(c.asExplicitCall())
7+
or
8+
result.asSummarizedCallable().matchesCall(c.asExplicitCall())
79
}

‎unified/ql/lib/codeql/unified/internal/dataflow/Content.qll‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
private import unified
22
private import AllDataFlow
33

4+
/**
5+
* Holds if a MaD access path token of form `name[arg]` exists.
6+
*/
7+
predicate isAccessPathTokenPresent(string name, string arg) {
8+
arg = any(FlowSummaryOutput::Private::AccessPathToken tok).getAnArgument(name)
9+
}
10+
411
private newtype TContent =
512
TArrayElement() or
613
TNamedMember(string name) {
@@ -11,6 +18,8 @@ private newtype TContent =
1118
name = [0 .. 20].toString()
1219
or
1320
name = getEnumCaseParameterFieldFromArgument(_, _)
21+
or
22+
isAccessPathTokenPresent("Member", name)
1423
} or
1524
TCapturedVariable(CaptureSsaInput::CapturedVariable v)
1625

‎unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ private import codeql.unified.internal.ExprPositions
55
private newtype TDataFlowCall =
66
TExplicitCall(CallExpr call) {
77
not isInBindingContext(call, _) // ignore constructor patterns
8+
} or
9+
TSummaryCall(SummarizedCallable callable, SummaryNode callee) {
10+
FlowSummaryOutput::summaryCallbackRange(callable, callee)
811
}
912

1013
/**
@@ -17,15 +20,33 @@ class DataFlowCall extends TDataFlowCall {
1720
/** Gets the `CallExpr` wrapped by this dataflow call, if any. */
1821
CallExpr asExplicitCall() { this = TExplicitCall(result) }
1922

23+
/** Holds if this is a synthesized call to `callee` in `callable`. */
24+
predicate isSummaryCall(SummarizedCallable callable, SummaryNode callee) {
25+
this = TSummaryCall(callable, callee)
26+
}
27+
2028
/** Gets a textual representation of this call. */
21-
string toString() { result = this.asExplicitCall().toString() }
29+
string toString() {
30+
result = this.asExplicitCall().toString()
31+
or
32+
exists(SummarizedCallable callable, SummaryNode callee |
33+
this.isSummaryCall(callable, callee) and
34+
result = "[summary] call to " + callee.toString() + " in " + callable.toString()
35+
)
36+
}
2237

2338
/** Gets the location of this call, if any. */
24-
Location getLocation() { result = this.asExplicitCall().getLocation() }
39+
Location getLocation() {
40+
result = this.asExplicitCall().getLocation()
41+
or
42+
this.isSummaryCall(_, _) and result instanceof EmptyLocation
43+
}
2544

2645
/** Gets the callable containing this call. */
2746
DataFlowCallable getEnclosingCallable() {
2847
result.asSourceCallable() = this.asExplicitCall().getEnclosingCallable()
48+
or
49+
this.isSummaryCall(result.asSummarizedCallable(), _)
2950
}
3051
}
3152

‎unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCallable.qll‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ private import AllDataFlow
33
private import codeql.unified.internal.ExprPositions
44

55
private newtype TDataFlowCallable =
6-
TSourceCallable(Callable callable) { not isInTypeContext(callable) }
6+
TSourceCallable(Callable callable) { not isInTypeContext(callable) } or
7+
TSummarizedCallable(SummarizedCallable callable)
78

89
/**
910
* A callable entity: either a function-like entity in source code or
@@ -15,11 +16,21 @@ class DataFlowCallable extends TDataFlowCallable {
1516
/** Gets the `Callable` wrapped by this dataflow callable, if any. */
1617
Callable asSourceCallable() { this = TSourceCallable(result) }
1718

19+
/** Gets the `SummarizedCallable` wrapped by this dataflow callable, if any. */
20+
SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
21+
1822
/** Gets a string representation of this call. */
19-
string toString() { result = this.asSourceCallable().toString() }
23+
string toString() {
24+
result = this.asSourceCallable().toString() or
25+
result = this.asSummarizedCallable().toString()
26+
}
2027

2128
/** Gets the location of this call, if any. */
22-
Location getLocation() { result = this.asSourceCallable().getLocation() }
29+
Location getLocation() {
30+
result = this.asSourceCallable().getLocation()
31+
or
32+
this instanceof TSummarizedCallable and result instanceof EmptyLocation
33+
}
2334
}
2435

2536
DataFlowCallable toDataFlowCallable(Callable callable) { result.asSourceCallable() = callable }

‎unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ private import codeql.unified.internal.LocalNameBinding
55
predicate step(BuilderNode node1, Step step, BuilderNode node2) {
66
any(DataFlowPlugin p).step(node1, step, node2)
77
or
8+
exists(Expr expr1, Expr expr2, boolean mustFlow |
9+
any(DataFlowPlugin p).exprStep(expr1, expr2, mustFlow)
10+
|
11+
node1.isResultValue(expr1) and
12+
(if mustFlow = true then step.valueMustFlow() else step.value()) and
13+
node2.isResultValue(expr2)
14+
or
15+
node1.isPostUpdate(expr2) and
16+
step.value() and
17+
node2.isPostUpdate(expr1)
18+
)
19+
or
820
exists(Callable callable |
921
node1.isReceiverParameter(callable) and
1022
step.value() and

‎unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll‎

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,38 @@ module DataFlowInput implements InputSig<Location> {
2222
//
2323
// Parameter, argument, return, and out nodes and their positions/kinds
2424
//
25+
private predicate isReturnNode(Node n, ReturnKind kind) {
26+
// TODO: Handle short-hand returns such as `func foo() -> Int { 5 }`
27+
n.asExpr() = any(ReturnExpr r).getValue() and kind.isStandardReturnValue()
28+
or
29+
FlowSummaryOutput::summaryReturnNode(getSummaryNode(n), kind)
30+
}
31+
2532
class ReturnNode extends Node {
26-
ReturnNode() {
27-
// TODO: Handle short-hand returns such as `func foo() -> Int { 5 }`
28-
this.asExpr() = any(ReturnExpr r).getValue()
29-
}
33+
ReturnNode() { isReturnNode(this, _) }
3034

31-
ReturnKind getKind() { exists(result) }
35+
ReturnKind getKind() { isReturnNode(this, result) }
36+
}
37+
38+
private predicate isOutNode(Node n, DataFlowCall call, ReturnKind kind) {
39+
n.asExpr() = call.asExplicitCall() and kind.isStandardReturnValue()
40+
or
41+
exists(FlowSummaryOutput::SummaryNode callee |
42+
FlowSummaryOutput::summaryOutNode(callee, getSummaryNode(n), kind) and
43+
call.isSummaryCall(_, callee)
44+
)
3245
}
3346

3447
class OutNode extends Node {
35-
OutNode() { this.asExpr() instanceof CallExpr }
48+
OutNode() { isOutNode(this, _, _) }
3649
}
3750

38-
class ReturnKind = Unit;
51+
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { isOutNode(result, call, kind) }
52+
53+
class ReturnKind extends Unit {
54+
/** Holds if this is the standard return value */
55+
predicate isStandardReturnValue() { any() }
56+
}
3957

4058
import ParameterPositions
4159
//
@@ -58,6 +76,9 @@ module DataFlowInput implements InputSig<Location> {
5876
)
5977
or
6078
p.(BuilderNode).isImplicitParameter(c, pos)
79+
or
80+
FlowSummaryOutput::summaryParameterNode(getSummaryNode(p), pos) and
81+
c = p.getEnclosingCallableEx()
6182
}
6283

6384
class ParameterNode extends Node {
@@ -80,6 +101,11 @@ module DataFlowInput implements InputSig<Location> {
80101
)
81102
or
82103
n.(BuilderNode).isImplicitArgument(call, pos, false)
104+
or
105+
exists(FlowSummaryOutput::SummaryNode callee |
106+
FlowSummaryOutput::summaryArgumentNode(callee, getSummaryNode(n), pos) and
107+
call.isSummaryCall(_, callee)
108+
)
83109
}
84110

85111
class ArgumentNode extends Node {
@@ -91,10 +117,6 @@ module DataFlowInput implements InputSig<Location> {
91117
isArgumentNodeImpl(n, call, pos)
92118
}
93119

94-
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
95-
result.asExpr() = call.asExplicitCall() and exists(kind)
96-
}
97-
98120
//
99121
// Post-update nodes
100122
//
@@ -129,6 +151,8 @@ module DataFlowInput implements InputSig<Location> {
129151
private Step valueStepEx() {
130152
result.value()
131153
or
154+
result.valueMustFlow()
155+
or
132156
exists(ContentSet contents | bypassContentSet(contents) |
133157
result.read(contents)
134158
or
@@ -142,15 +166,29 @@ module DataFlowInput implements InputSig<Location> {
142166
localSsaStep(node1, node2, _) and model = ""
143167
or
144168
captureSsaLocalFlowStep(node1, node2) and model = ""
169+
or
170+
FlowSummaryOutput::summaryLocalStep(node1, node2, true, model)
171+
or
172+
model = "" and
173+
exists(ContentSet contents | bypassContentSet(contents) |
174+
FlowSummaryOutput::summaryReadStep(getSummaryNode(node1), contents, getSummaryNode(node2))
175+
or
176+
FlowSummaryOutput::summaryStoreStep(getSummaryNode(node1), contents, getSummaryNode(node2))
177+
)
145178
}
146179

147-
predicate jumpStep(Node node1, Node node2) { step(node1, any(Step s | s.jump()), node2) }
180+
predicate jumpStep(Node node1, Node node2) {
181+
step(node1, any(Step s | s.jump()), node2) or
182+
FlowSummaryOutput::summaryJumpStep(node1, node2)
183+
}
148184

149185
predicate readStep(Node node1, ContentSet c, Node node2) {
150186
(
151187
step(node1, any(Step s | s.read(c)), node2)
152188
or
153189
captureSsaReadStep(node1, c, node2)
190+
or
191+
FlowSummaryOutput::summaryReadStep(getSummaryNode(node1), c, getSummaryNode(node2))
154192
) and
155193
not bypassContentSet(c)
156194
}
@@ -160,15 +198,29 @@ module DataFlowInput implements InputSig<Location> {
160198
step(node1, any(Step s | s.store(c)), node2)
161199
or
162200
captureSsaStoreStep(node1, c, node2)
201+
or
202+
FlowSummaryOutput::summaryStoreStep(getSummaryNode(node1), c, getSummaryNode(node2))
163203
) and
164204
not bypassContentSet(c)
165205
}
166206

167-
predicate clearsContent(Node n, ContentSet c) { captureSsaClearsContent(n, c) } // TODO
207+
predicate clearsContent(Node n, ContentSet c) {
208+
captureSsaClearsContent(n, c)
209+
or
210+
FlowSummaryOutput::summaryClearsContent(getSummaryNode(n), c) and
211+
not bypassContentSet(c)
212+
}
168213

169-
predicate expectsContent(Node n, ContentSet c) { none() } // TODO
214+
predicate expectsContent(Node n, ContentSet c) {
215+
FlowSummaryOutput::summaryExpectsContent(getSummaryNode(n), c) and
216+
not bypassContentSet(c)
217+
}
170218

171-
predicate localMustFlowStep(Node node1, Node node2) { localSsaMustFlowStep(node1, node2) } // TODO
219+
predicate localMustFlowStep(Node node1, Node node2) {
220+
step(node1, any(Step s | s.valueMustFlow()), node2) or
221+
localSsaMustFlowStep(node1, node2) or
222+
FlowSummaryOutput::summaryLocalMustFlowStep(getSummaryNode(node1), getSummaryNode(node2))
223+
}
172224

173225
//
174226
// Misc
@@ -207,7 +259,14 @@ module DataFlowInput implements InputSig<Location> {
207259

208260
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() } // TODO
209261

210-
predicate allowParameterReturnInSelf(ParameterNode p) { captureSsaAllowParameterReturnInSelf(p) }
262+
predicate allowParameterReturnInSelf(ParameterNode p) {
263+
captureSsaAllowParameterReturnInSelf(p)
264+
or
265+
exists(DataFlowCallable c, ParameterPosition pos |
266+
FlowSummaryOutput::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos) and
267+
isParameterNodeImpl(p, c, pos)
268+
)
269+
}
211270

212271
class LambdaCallKind extends Unit { }
213272

@@ -216,9 +275,11 @@ module DataFlowInput implements InputSig<Location> {
216275
exists(kind)
217276
}
218277

219-
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
220-
receiver.(BuilderNode).isImplicitArgument(call, any(ArgumentPosition p | p.isCallee()), false) and
278+
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node callee) {
279+
callee.(BuilderNode).isImplicitArgument(call, any(ArgumentPosition p | p.isCallee()), false) and
221280
exists(kind)
281+
or
282+
call.isSummaryCall(_, getSummaryNode(callee)) and exists(kind)
222283
}
223284

224285
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {

0 commit comments

Comments
 (0)