Skip to content

Commit befca9b

Browse files
authored
Merge branch 'main' into brodes/seh_flow_phase2_splitting_seh_edges
2 parents 57fc3fb + dce29db commit befca9b

File tree

20 files changed

+723
-274
lines changed

20 files changed

+723
-274
lines changed

go/extractor/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ toolchain go1.23.1
1010
// bazel mod tidy
1111
require (
1212
golang.org/x/mod v0.22.0
13-
golang.org/x/tools v0.27.0
13+
golang.org/x/tools v0.28.0
1414
)
1515

16-
require golang.org/x/sync v0.9.0 // indirect
16+
require golang.org/x/sync v0.10.0 // indirect

go/extractor/go.sum

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
2+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
13
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
24
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
3-
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
4-
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
5-
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
6-
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
5+
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
6+
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
7+
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
8+
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: feature
3+
---
4+
5+
- Added support for parameter annotations in API graphs. This means that in a function definition such as `def foo(x: Bar): ...`, you can now use the `getInstanceFromAnnotation()` method to step from `Bar` to `x`. In addition to this, the `getAnInstance` method now also includes instances arising from parameter annotations.

python/ql/lib/semmle/python/ApiGraphs.qll

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ module API {
195195
*/
196196
Node getReturn() { result = this.getASuccessor(Label::return()) }
197197

198+
/**
199+
* Gets a node representing instances of the class represented by this node, as specified via
200+
* type annotations.
201+
*/
202+
Node getInstanceFromAnnotation() { result = this.getASuccessor(Label::annotation()) }
203+
198204
/**
199205
* Gets a node representing the `i`th parameter of the function represented by this node.
200206
*
@@ -229,7 +235,9 @@ module API {
229235
/**
230236
* Gets a node representing an instance of the class (or a transitive subclass of the class) represented by this node.
231237
*/
232-
Node getAnInstance() { result = this.getASubclass*().getReturn() }
238+
Node getAnInstance() {
239+
result in [this.getASubclass*().getReturn(), this.getASubclass*().getInstanceFromAnnotation()]
240+
}
233241

234242
/**
235243
* Gets a node representing the result from awaiting this node.
@@ -834,6 +842,10 @@ module API {
834842
lbl = Label::return() and
835843
ref = pred.getACall()
836844
or
845+
// Getting an instance via a type annotation
846+
lbl = Label::annotation() and
847+
ref = pred.getAnAnnotatedInstance()
848+
or
837849
// Awaiting a node that is a use of `base`
838850
lbl = Label::await() and
839851
ref = pred.getAnAwaited()
@@ -1079,6 +1091,7 @@ module API {
10791091
} or
10801092
MkLabelSelfParameter() or
10811093
MkLabelReturn() or
1094+
MkLabelAnnotation() or
10821095
MkLabelSubclass() or
10831096
MkLabelAwait() or
10841097
MkLabelSubscript() or
@@ -1148,6 +1161,11 @@ module API {
11481161
override string toString() { result = "getReturn()" }
11491162
}
11501163

1164+
/** A label for annotations. */
1165+
class LabelAnnotation extends ApiLabel, MkLabelAnnotation {
1166+
override string toString() { result = "getAnnotatedInstance()" }
1167+
}
1168+
11511169
/** A label that gets the subclass of a class. */
11521170
class LabelSubclass extends ApiLabel, MkLabelSubclass {
11531171
override string toString() { result = "getASubclass()" }
@@ -1207,6 +1225,9 @@ module API {
12071225
/** Gets the `return` edge label. */
12081226
LabelReturn return() { any() }
12091227

1228+
/** Gets the `annotation` edge label. */
1229+
LabelAnnotation annotation() { any() }
1230+
12101231
/** Gets the `subclass` edge label. */
12111232
LabelSubclass subclass() { any() }
12121233

python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ class LocalSourceNode extends Node {
119119
*/
120120
CallCfgNode getACall() { Cached::call(this, result) }
121121

122+
/**
123+
* Gets a node that has this node as its annotation.
124+
*/
125+
Node getAnAnnotatedInstance() { Cached::annotatedInstance(this, result) }
126+
122127
/**
123128
* Gets an awaited value from this node.
124129
*/
@@ -275,6 +280,17 @@ private module Cached {
275280
)
276281
}
277282

283+
cached
284+
predicate annotatedInstance(LocalSourceNode node, Node instance) {
285+
exists(ExprNode n | node.flowsTo(n) |
286+
instance.asCfgNode().getNode() =
287+
any(AnnAssign ann | ann.getAnnotation() = n.asExpr()).getTarget()
288+
or
289+
instance.asCfgNode().getNode() =
290+
any(Parameter p | p.getAnnotation() = n.asCfgNode().getNode())
291+
)
292+
}
293+
278294
/**
279295
* Holds if `node` flows to a value that, when awaited, results in `awaited`.
280296
*/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from types import AssignmentAnnotation, ParameterAnnotation
2+
3+
def test_annotated_assignment():
4+
local_x : AssignmentAnnotation = create_x() #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation")
5+
local_x #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation").getAnnotatedInstance()
6+
7+
global_x : AssignmentAnnotation #$ use=moduleImport("types").getMember("AssignmentAnnotation")
8+
global_x #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation").getAnnotatedInstance()
9+
10+
def test_parameter_annotation(parameter_y: ParameterAnnotation): #$ use=moduleImport("types").getMember("ParameterAnnotation")
11+
parameter_y #$ use=moduleImport("types").getMember("ParameterAnnotation").getAnnotatedInstance()
12+
13+
type Alias = AssignmentAnnotation
14+
15+
global_z : Alias #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation")
16+
global_z #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation").getAnnotatedInstance()
17+
18+
def test_parameter_alias(parameter_z: Alias): #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation")
19+
parameter_z #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation").getAnnotatedInstance()
20+
21+
# local type aliases
22+
def test_local_type_alias():
23+
type LocalAlias = AssignmentAnnotation
24+
local_alias : LocalAlias = create_value() #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation")
25+
local_alias #$ MISSING: use=moduleImport("types").getMember("AssignmentAnnotation").getAnnotatedInstance()

rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,15 @@ private class StructFieldContent extends Content, TStructFieldContent {
623623
override string toString() { result = s.toString() + "." + field_.toString() }
624624
}
625625

626+
/**
627+
* An element in an array.
628+
*/
629+
final class ArrayElementContent extends Content, TArrayElement {
630+
ArrayElementContent() { this = TArrayElement() }
631+
632+
override string toString() { result = "array[]" }
633+
}
634+
626635
/**
627636
* Content stored at a position in a tuple.
628637
*
@@ -656,7 +665,7 @@ abstract class ContentSet extends TContentSet {
656665
abstract Content getAReadContent();
657666
}
658667

659-
final private class SingletonContentSet extends ContentSet, TSingletonContentSet {
668+
final class SingletonContentSet extends ContentSet, TSingletonContentSet {
660669
private Content c;
661670

662671
SingletonContentSet() { this = TSingletonContentSet(c) }
@@ -879,6 +888,24 @@ module RustDataFlow implements InputSig<Location> {
879888
node2.asExpr() = access
880889
)
881890
or
891+
exists(IndexExprCfgNode arr |
892+
c instanceof ArrayElementContent and
893+
node1.asExpr() = arr.getBase() and
894+
node2.asExpr() = arr
895+
)
896+
or
897+
exists(ForExprCfgNode for |
898+
c instanceof ArrayElementContent and
899+
node1.asExpr() = for.getIterable() and
900+
node2.asPat() = for.getPat()
901+
)
902+
or
903+
exists(SlicePatCfgNode pat |
904+
c instanceof ArrayElementContent and
905+
node1.asPat() = pat and
906+
node2.asPat() = pat.getAPat()
907+
)
908+
or
882909
exists(TryExprCfgNode try |
883910
node1.asExpr() = try.getExpr() and
884911
node2.asExpr() = try and
@@ -951,7 +978,21 @@ module RustDataFlow implements InputSig<Location> {
951978
node2.asExpr() = tuple
952979
)
953980
or
981+
c instanceof ArrayElementContent and
982+
node1.asExpr() =
983+
[
984+
node2.asExpr().(ArrayRepeatExprCfgNode).getRepeatOperand(),
985+
node2.asExpr().(ArrayListExprCfgNode).getAnExpr()
986+
]
987+
or
954988
tupleAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
989+
or
990+
exists(AssignmentExprCfgNode assignment, IndexExprCfgNode index |
991+
c instanceof ArrayElementContent and
992+
assignment.getLhs() = index and
993+
node1.asExpr() = assignment.getRhs() and
994+
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = index.getBase()
995+
)
955996
)
956997
or
957998
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(Node::FlowSummaryNode).getSummaryNode(),
@@ -1067,7 +1108,11 @@ private module Cached {
10671108
TPatNode(PatCfgNode p) or
10681109
TExprPostUpdateNode(ExprCfgNode e) {
10691110
isArgumentForCall(e, _, _) or
1070-
e = [any(FieldExprCfgNode access).getExpr(), any(TryExprCfgNode try).getExpr()]
1111+
e =
1112+
[
1113+
any(IndexExprCfgNode i).getBase(), any(FieldExprCfgNode access).getExpr(),
1114+
any(TryExprCfgNode try).getExpr()
1115+
]
10711116
} or
10721117
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
10731118
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn)
@@ -1152,6 +1197,7 @@ private module Cached {
11521197
TVariantFieldContent(VariantCanonicalPath v, string field) {
11531198
field = v.getVariant().getFieldList().(RecordFieldList).getAField().getName().getText()
11541199
} or
1200+
TArrayElement() or
11551201
TTuplePositionContent(int pos) {
11561202
pos in [0 .. max([
11571203
any(TuplePat pat).getNumberOfFields(),

rust/ql/lib/codeql/rust/dataflow/internal/TaintTrackingImpl.qll

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
private import rust
22
private import codeql.dataflow.TaintTracking
33
private import codeql.rust.controlflow.CfgNodes
4-
private import DataFlowImpl
54
private import codeql.rust.dataflow.FlowSummary
6-
private import FlowSummaryImpl as FlowSummaryImpl
75
private import DataFlowImpl
6+
private import FlowSummaryImpl as FlowSummaryImpl
87

98
module RustTaintTracking implements InputSig<Location, RustDataFlow> {
109
predicate defaultTaintSanitizer(Node::Node node) { none() }
@@ -35,6 +34,15 @@ module RustTaintTracking implements InputSig<Location, RustDataFlow> {
3534
pred.asExpr() = index.getBase() and
3635
succ.asExpr() = index
3736
)
37+
or
38+
// Although data flow through collections is modeled using stores/reads,
39+
// we also allow taint to flow out of a tainted collection. This is
40+
// needed in order to support taint-tracking configurations where the
41+
// source is a collection.
42+
exists(SingletonContentSet cs |
43+
RustDataFlow::readStep(pred, cs, succ) and
44+
cs.getContent() instanceof ArrayElementContent
45+
)
3846
)
3947
or
4048
FlowSummaryImpl::Private::Steps::summaryLocalStep(pred.(Node::FlowSummaryNode).getSummaryNode(),
@@ -46,7 +54,10 @@ module RustTaintTracking implements InputSig<Location, RustDataFlow> {
4654
* and inputs to additional taint steps.
4755
*/
4856
bindingset[node]
49-
predicate defaultImplicitTaintRead(Node::Node node, ContentSet c) { none() }
57+
predicate defaultImplicitTaintRead(Node::Node node, ContentSet cs) {
58+
exists(node) and
59+
cs.(SingletonContentSet).getContent() instanceof ArrayElementContent
60+
}
5061

5162
/**
5263
* Holds if the additional step from `src` to `sink` should be considered in

rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,8 @@ module Impl {
467467
assignmentExprDescendant(mid) and
468468
getImmediateParent(e) = mid and
469469
not mid.(PrefixExpr).getOperatorName() = "*" and
470-
not mid instanceof FieldExpr
470+
not mid instanceof FieldExpr and
471+
not mid instanceof IndexExpr
471472
)
472473
}
473474

0 commit comments

Comments
 (0)