Skip to content

Commit b6a9c9a

Browse files
authored
Merge pull request #22792 from asgerf/unified/bypass-optional
Unified: Bypass Optional<T> contents in data flow
2 parents 4812cdd + e1cbde7 commit b6a9c9a

6 files changed

Lines changed: 131 additions & 67 deletions

File tree

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,26 @@ module DebugGraph<relevantNodeSig/1 relevantNode> {
176176
value = node.toString()
177177
}
178178

179+
private predicate isBypassedStoreReadStep(Step s) {
180+
exists(ContentSet contents |
181+
s.read(contents)
182+
or
183+
s.store(contents)
184+
|
185+
bypassContentSet(contents)
186+
)
187+
}
188+
179189
query predicate edges(Node node1, Node node2, string key, string value) {
180190
key = "semmle.label" and
181191
relevantDataFlowNode(node1) and
182192
relevantDataFlowNode(node2) and
183193
(
184194
exists(Step step |
185195
step(node1, step, node2) and
186-
value = step.toString()
196+
if isBypassedStoreReadStep(step)
197+
then value = "value [bypassed " + step.toString() + "]"
198+
else value = step.toString()
187199
)
188200
or
189201
exists(boolean isUseStep |

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,20 +127,33 @@ module DataFlowInput implements InputSig<Location> {
127127
//
128128
// Steps
129129
//
130+
pragma[nomagic]
131+
private Step valueStepEx() {
132+
result.value()
133+
or
134+
exists(ContentSet contents | bypassContentSet(contents) |
135+
result.read(contents)
136+
or
137+
result.store(contents)
138+
)
139+
}
140+
130141
predicate simpleLocalFlowStep(Node node1, Node node2, string model) {
131-
step(node1, any(Step s | s.value()), node2) and model = ""
142+
step(node1, valueStepEx(), node2) and model = ""
132143
or
133144
localSsaStep(node1, node2, _) and model = ""
134145
}
135146

136147
predicate jumpStep(Node node1, Node node2) { step(node1, any(Step s | s.jump()), node2) }
137148

138149
predicate readStep(Node node1, ContentSet c, Node node2) {
139-
step(node1, any(Step s | s.read(c)), node2)
150+
step(node1, any(Step s | s.read(c)), node2) and
151+
not bypassContentSet(c)
140152
}
141153

142154
predicate storeStep(Node node1, ContentSet c, Node node2) {
143-
step(node1, any(Step s | s.store(c)), node2)
155+
step(node1, any(Step s | s.store(c)), node2) and
156+
not bypassContentSet(c)
144157
}
145158

146159
predicate clearsContent(Node n, ContentSet c) { none() } // TODO

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,16 @@ private module Plugins {
1313
class DataFlowPlugin extends Unit {
1414
/** Holds if there is a language-specific step from `node1 -> step -> node2`. */
1515
predicate step(Node node1, Step step, Node node2) { none() }
16+
17+
/**
18+
* Holds if a read/store step with the given `contents` should be converted to a value step.
19+
*
20+
* This affects the steps generated by the language-agnostic code as well as steps from the plugin itself.
21+
*/
22+
predicate bypassContentSet(ContentSet contents) { none() }
1623
}
24+
25+
/**
26+
* Holds if a read/store step with the given `contents` should be converted to a value step.
27+
*/
28+
predicate bypassContentSet(ContentSet contents) { any(DataFlowPlugin p).bypassContentSet(contents) }

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

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ private import unified
66
private import AllDataFlow
77

88
private class SwiftDataFlowPlugin extends DataFlowPlugin {
9+
override predicate bypassContentSet(ContentSet contents) {
10+
contents.asSingleton().asNamedMember() = "some.0"
11+
}
12+
913
// Note: For now we assume all code is Swift, but in the future we must restrict these rules to Swift-files
1014
override predicate step(Node node1, Step step, Node node2) {
1115
exists(BinaryExpr expr |
@@ -27,33 +31,31 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin {
2731
)
2832
or
2933
exists(UnaryExpr expr |
30-
expr.getOperator().(PostfixOperator).getValue() = "!" and
31-
node1.isResultValue(expr.getOperand()) and
32-
(step.readName("some.0") or step.taint()) and
33-
node2.isResultValue(expr)
34+
expr.getOperator().(PostfixOperator).getValue() = "!"
3435
or
35-
expr.getOperator().(PrefixOperator).getValue() = ["try", "try!", "await"] and
36+
expr.getOperator().(PrefixOperator).getValue() = ["try", "try!", "try?", "await"]
37+
|
3638
node1.isResultValue(expr.getOperand()) and
3739
step.value() and
3840
node2.isResultValue(expr)
39-
or
40-
expr.getOperator().(PrefixOperator).getValue() = "try?" and
41-
// TODO: preserve the value of some.0 if it is already stored in that
42-
node1.isResultValue(expr.getOperand()) and
43-
step.storeName("some.0") and
44-
node2.isResultValue(expr)
4541
)
4642
or
4743
exists(TypeCastExpr expr |
48-
// The `as?` type cast boxes the incoming value in Optional depending on whether the type cast succeeded
49-
expr.getOperator().getValue() = "as?" and
44+
// Type-cast operators:
45+
// - "as": Safe upcast conversion
46+
// - "as?": Wrap in Optional<T> if cast succeeds
47+
// - "as!": Throw if cast fails
48+
//
49+
// Since we bypass Optional<T> content, we model all of these as value steps.
50+
expr.getOperator().getValue() = ["as", "as?", "as!"] and
5051
node1.isResultValue(expr.getExpr()) and
51-
step.storeName("some.0") and
52+
step.value() and
5253
node2.isResultValue(expr)
53-
or
54-
// Safe upcast conversion ("as") and downcast-or-throw ("as!") propagate the value directly
55-
expr.getOperator().getValue() = ["as", "as!"] and
56-
node1.isResultValue(expr.getExpr()) and
54+
)
55+
or
56+
exists(NullCoalescingExpr expr |
57+
// We bypass Optional<T> content so we add value-flow out of both operands of "??"
58+
node1.isResultValue(expr.getAnOperand()) and
5759
step.value() and
5860
node2.isResultValue(expr)
5961
)

0 commit comments

Comments
 (0)