Skip to content

Commit 87f3713

Browse files
committed
Rust: Implement data flow through tuple structs
1 parent ceb63fa commit 87f3713

File tree

19 files changed

+261
-162
lines changed

19 files changed

+261
-162
lines changed

rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll

+2-6
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,8 @@ final class RecordPatCfgNode extends Nodes::RecordPatCfgNode {
265265
PatCfgNode getFieldPat(string field) {
266266
exists(RecordPatField rpf |
267267
rpf = node.getRecordPatFieldList().getAField() and
268-
any(ChildMapping mapping).hasCfgChild(node, rpf.getPat(), this, result)
269-
|
270-
field = rpf.getNameRef().getText()
271-
or
272-
not rpf.hasNameRef() and
273-
field = result.(IdentPatCfgNode).getName().getText()
268+
any(ChildMapping mapping).hasCfgChild(node, rpf.getPat(), this, result) and
269+
field = rpf.getFieldName()
274270
)
275271
}
276272
}

rust/ql/lib/codeql/rust/dataflow/DataFlow.qll

-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ module DataFlow {
2525

2626
final class Content = DataFlowImpl::Content;
2727

28-
final class VariantContent = DataFlowImpl::VariantContent;
29-
3028
final class ContentSet = DataFlowImpl::ContentSet;
3129

3230
/**

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

+67-125
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ private import codeql.rust.controlflow.CfgNodes
1414
private import codeql.rust.dataflow.Ssa
1515
private import codeql.rust.dataflow.FlowSummary
1616
private import FlowSummaryImpl as FlowSummaryImpl
17-
private import codeql.rust.elements.internal.PathResolution as PathResolution
1817

1918
/**
2019
* A return kind. A return kind describes how a value can be returned from a
@@ -661,7 +660,7 @@ private module VariantInLib {
661660
}
662661

663662
/** A tuple variant from library code. */
664-
class VariantInLibTupleFieldContent extends VariantContent, TVariantInLibTupleFieldContent {
663+
class VariantInLibTupleFieldContent extends Content, TVariantInLibTupleFieldContent {
665664
private VariantInLib::VariantInLib v;
666665
private int pos_;
667666

@@ -740,82 +739,73 @@ abstract class Content extends TContent {
740739
abstract Location getLocation();
741740
}
742741

743-
/**
744-
* A variant of an `enum`. In addition to the variant itself, this also includes the
745-
* position (for tuple variants) or the field name (for record variants).
746-
*/
747-
abstract class VariantContent extends Content { }
748-
749-
private TupleField getVariantTupleField(Variant v, int i) {
750-
result = v.getFieldList().(TupleFieldList).getField(i)
742+
/** A field belonging to either a variant or a struct. */
743+
abstract class FieldContent extends Content {
744+
/** Gets an access to this field. */
745+
pragma[nomagic]
746+
abstract FieldExprCfgNode getAnAccess();
751747
}
752748

753-
/** A tuple variant. */
754-
private class VariantTupleFieldContent extends VariantContent, TVariantTupleFieldContent {
755-
private Variant v;
756-
private int pos_;
749+
/** A tuple field belonging to either a variant or a struct. */
750+
class TupleFieldContent extends FieldContent, TTupleFieldContent {
751+
private TupleField field;
752+
753+
TupleFieldContent() { this = TTupleFieldContent(field) }
754+
755+
predicate isVariantField(Variant v, int pos) { field.isVariantField(v, pos) }
757756

758-
VariantTupleFieldContent() { this = TVariantTupleFieldContent(v, pos_) }
757+
predicate isStructField(Struct s, int pos) { field.isStructField(s, pos) }
759758

760-
Variant getVariant(int pos) { result = v and pos = pos_ }
759+
override FieldExprCfgNode getAnAccess() { none() } // TODO
761760

762761
final override string toString() {
763-
exists(string name |
764-
name = v.getName().getText() and
762+
exists(Variant v, int pos, string vname |
763+
this.isVariantField(v, pos) and
764+
vname = v.getName().getText() and
765765
// only print indices when the arity is > 1
766-
if exists(getVariantTupleField(v, 1)) then result = name + "(" + pos_ + ")" else result = name
766+
if exists(v.getTupleField(1)) then result = vname + "(" + pos + ")" else result = vname
767+
)
768+
or
769+
exists(Struct s, int pos, string sname |
770+
this.isStructField(s, pos) and
771+
sname = s.getName().getText() and
772+
// only print indices when the arity is > 1
773+
if exists(s.getTupleField(1)) then result = sname + "(" + pos + ")" else result = sname
767774
)
768775
}
769776

770-
final override Location getLocation() { result = getVariantTupleField(v, pos_).getLocation() }
777+
final override Location getLocation() { result = field.getLocation() }
771778
}
772779

773-
private RecordField getVariantRecordField(Variant v, string field) {
774-
result = v.getFieldList().(RecordFieldList).getAField() and
775-
field = result.getName().getText()
776-
}
780+
/** A record field belonging to either a variant or a struct. */
781+
class RecordFieldContent extends FieldContent, TRecordFieldContent {
782+
private RecordField field;
783+
784+
RecordFieldContent() { this = TRecordFieldContent(field) }
777785

778-
/** A record variant. */
779-
private class VariantRecordFieldContent extends VariantContent, TVariantRecordFieldContent {
780-
private Variant v;
781-
private string field_;
786+
predicate isVariantField(Variant v, string name) { field.isVariantField(v, name) }
782787

783-
VariantRecordFieldContent() { this = TVariantRecordFieldContent(v, field_) }
788+
predicate isStructField(Struct s, string name) { field.isStructField(s, name) }
784789

785-
Variant getVariant(string field) { result = v and field = field_ }
790+
override FieldExprCfgNode getAnAccess() { none() } // TODO
786791

787792
final override string toString() {
788-
exists(string name |
789-
name = v.getName().getText() and
793+
exists(Variant v, string name, string vname |
794+
this.isVariantField(v, name) and
795+
vname = v.getName().getText() and
790796
// only print field when the arity is > 1
791-
if strictcount(string f | exists(getVariantRecordField(v, f))) > 1
792-
then result = name + "{" + field_ + "}"
793-
else result = name
797+
if strictcount(v.getRecordField(_)) > 1 then result = vname + "." + name else result = vname
794798
)
795-
}
796-
797-
final override Location getLocation() {
798-
result = getVariantRecordField(v, field_).getName().getLocation()
799-
}
800-
}
801-
802-
/** Content stored in a field on a struct. */
803-
class StructFieldContent extends Content, TStructFieldContent {
804-
private Struct s;
805-
private string field_;
806-
807-
StructFieldContent() { this = TStructFieldContent(s, field_) }
808-
809-
Struct getStruct(string field) { result = s and field = field_ }
810-
811-
override string toString() { result = s.getName().getText() + "." + field_.toString() }
812-
813-
override Location getLocation() {
814-
exists(Name f | f = s.getFieldList().(RecordFieldList).getAField().getName() |
815-
f.getText() = field_ and
816-
result = f.getLocation()
799+
or
800+
exists(Struct s, string name, string sname |
801+
this.isStructField(s, name) and
802+
sname = s.getName().getText() and
803+
// only print field when the arity is > 1
804+
if strictcount(s.getRecordField(_)) > 1 then result = sname + "." + name else result = sname
817805
)
818806
}
807+
808+
final override Location getLocation() { result = field.getLocation() }
819809
}
820810

821811
/** A captured variable. */
@@ -859,13 +849,18 @@ final class ElementContent extends Content, TElementContent {
859849
* NOTE: Unlike `struct`s and `enum`s tuples are structural and not nominal,
860850
* hence we don't store a canonical path for them.
861851
*/
862-
final class TuplePositionContent extends Content, TTuplePositionContent {
852+
final class TuplePositionContent extends FieldContent, TTuplePositionContent {
863853
private int pos;
864854

865855
TuplePositionContent() { this = TTuplePositionContent(pos) }
866856

867857
int getPosition() { result = pos }
868858

859+
override FieldExprCfgNode getAnAccess() {
860+
// TODO: limit to tuple types
861+
result.getNameRef().getText().toInt() = pos
862+
}
863+
869864
override string toString() { result = "tuple." + pos.toString() }
870865

871866
override Location getLocation() { result instanceof EmptyLocation }
@@ -901,11 +896,6 @@ final class FunctionCallReturnContent extends Content, TFunctionCallReturnConten
901896
override Location getLocation() { result instanceof EmptyLocation }
902897
}
903898

904-
/** Holds if `access` indexes a tuple at an index corresponding to `c`. */
905-
private predicate fieldTuplePositionContent(FieldExprCfgNode access, TuplePositionContent c) {
906-
access.getNameRef().getText().toInt() = c.getPosition()
907-
}
908-
909899
/** A value that represents a set of `Content`s. */
910900
abstract class ContentSet extends TContentSet {
911901
/** Gets a textual representation of this element. */
@@ -1128,23 +1118,6 @@ module RustDataFlow implements InputSig<Location> {
11281118
node2.(Node::FlowSummaryNode).getSummaryNode())
11291119
}
11301120

1131-
/** Gets the item that `p` resolves to, if any. */
1132-
private PathResolution::ItemNode resolvePath(PathAstNode p) {
1133-
result = PathResolution::resolvePath(p.getPath())
1134-
}
1135-
1136-
/** Holds if `p` destructs an enum variant `v`. */
1137-
pragma[nomagic]
1138-
private predicate tupleVariantDestruction(TupleStructPat p, Variant v) { v = resolvePath(p) }
1139-
1140-
/** Holds if `p` destructs an enum variant `v`. */
1141-
pragma[nomagic]
1142-
private predicate recordVariantDestruction(RecordPat p, Variant v) { v = resolvePath(p) }
1143-
1144-
/** Holds if `p` destructs a struct `s`. */
1145-
pragma[nomagic]
1146-
private predicate structDestruction(RecordPat p, Struct s) { s = resolvePath(p) }
1147-
11481121
/**
11491122
* Holds if data can flow from `node1` to `node2` via a read of `c`. Thus,
11501123
* `node1` references an object with a content `c.getAReadContent()` whose
@@ -1156,7 +1129,7 @@ module RustDataFlow implements InputSig<Location> {
11561129
pat = node1.asPat() and
11571130
node2.asPat() = pat.getField(pos)
11581131
|
1159-
tupleVariantDestruction(pat.getPat(), c.(VariantTupleFieldContent).getVariant(pos))
1132+
c = TTupleFieldContent(pat.getTupleStructPat().getTupleField(pos))
11601133
or
11611134
VariantInLib::tupleVariantCanonicalDestruction(pat.getPat(), c, pos)
11621135
)
@@ -1169,25 +1142,17 @@ module RustDataFlow implements InputSig<Location> {
11691142
or
11701143
exists(RecordPatCfgNode pat, string field |
11711144
pat = node1.asPat() and
1172-
(
1173-
// Pattern destructs a struct-like variant.
1174-
recordVariantDestruction(pat.getPat(), c.(VariantRecordFieldContent).getVariant(field))
1175-
or
1176-
// Pattern destructs a struct.
1177-
structDestruction(pat.getPat(), c.(StructFieldContent).getStruct(field))
1178-
) and
1145+
c = TRecordFieldContent(pat.getRecordPat().getRecordField(field)) and
11791146
node2.asPat() = pat.getFieldPat(field)
11801147
)
11811148
or
11821149
c instanceof ReferenceContent and
11831150
node1.asPat().(RefPatCfgNode).getPat() = node2.asPat()
11841151
or
11851152
exists(FieldExprCfgNode access |
1186-
// Read of a tuple entry
1187-
fieldTuplePositionContent(access, c) and
1188-
// TODO: Handle read of a struct field.
11891153
node1.asExpr() = access.getExpr() and
1190-
node2.asExpr() = access
1154+
node2.asExpr() = access and
1155+
access = c.(FieldContent).getAnAccess()
11911156
)
11921157
or
11931158
exists(IndexExprCfgNode arr |
@@ -1236,49 +1201,29 @@ module RustDataFlow implements InputSig<Location> {
12361201
cs, node2.(Node::FlowSummaryNode).getSummaryNode())
12371202
}
12381203

1239-
/** Holds if `ce` constructs an enum value of type `v`. */
1240-
pragma[nomagic]
1241-
private predicate tupleVariantConstruction(CallExpr ce, Variant v) {
1242-
v = resolvePath(ce.getFunction().(PathExpr))
1243-
}
1244-
1245-
/** Holds if `re` constructs an enum value of type `v`. */
12461204
pragma[nomagic]
1247-
private predicate recordVariantConstruction(RecordExpr re, Variant v) { v = resolvePath(re) }
1248-
1249-
/** Holds if `re` constructs a struct value of type `s`. */
1250-
pragma[nomagic]
1251-
private predicate structConstruction(RecordExpr re, Struct s) { s = resolvePath(re) }
1252-
1253-
private predicate tupleAssignment(Node node1, Node node2, TuplePositionContent c) {
1205+
private predicate fieldAssignment(Node node1, Node node2, FieldContent c) {
12541206
exists(AssignmentExprCfgNode assignment, FieldExprCfgNode access |
12551207
assignment.getLhs() = access and
1256-
fieldTuplePositionContent(access, c) and
12571208
node1.asExpr() = assignment.getRhs() and
1258-
node2.asExpr() = access.getExpr()
1209+
node2.asExpr() = access.getExpr() and
1210+
access = c.getAnAccess()
12591211
)
12601212
}
12611213

12621214
pragma[nomagic]
12631215
private predicate storeContentStep(Node node1, Content c, Node node2) {
12641216
exists(CallExprCfgNode call, int pos |
1265-
node1.asExpr() = call.getArgument(pos) and
1217+
node1.asExpr() = call.getArgument(pragma[only_bind_into](pos)) and
12661218
node2.asExpr() = call
12671219
|
1268-
tupleVariantConstruction(call.getCallExpr(), c.(VariantTupleFieldContent).getVariant(pos))
1220+
c = TTupleFieldContent(call.getCallExpr().getTupleField(pragma[only_bind_into](pos)))
12691221
or
12701222
VariantInLib::tupleVariantCanonicalConstruction(call.getCallExpr(), c, pos)
12711223
)
12721224
or
12731225
exists(RecordExprCfgNode re, string field |
1274-
(
1275-
// Expression is for a struct-like enum variant.
1276-
recordVariantConstruction(re.getRecordExpr(),
1277-
c.(VariantRecordFieldContent).getVariant(field))
1278-
or
1279-
// Expression is for a struct.
1280-
structConstruction(re.getRecordExpr(), c.(StructFieldContent).getStruct(field))
1281-
) and
1226+
c = TRecordFieldContent(re.getRecordExpr().getRecordField(field)) and
12821227
node1.asExpr() = re.getFieldExpr(field) and
12831228
node2.asExpr() = re
12841229
)
@@ -1295,7 +1240,7 @@ module RustDataFlow implements InputSig<Location> {
12951240
node2.asExpr().(ArrayListExprCfgNode).getAnExpr()
12961241
]
12971242
or
1298-
tupleAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
1243+
fieldAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
12991244
or
13001245
exists(AssignmentExprCfgNode assignment, IndexExprCfgNode index |
13011246
c instanceof ElementContent and
@@ -1338,7 +1283,7 @@ module RustDataFlow implements InputSig<Location> {
13381283
* in `x.f = newValue`.
13391284
*/
13401285
predicate clearsContent(Node n, ContentSet cs) {
1341-
tupleAssignment(_, n, cs.(SingletonContentSet).getContent())
1286+
fieldAssignment(_, n, cs.(SingletonContentSet).getContent())
13421287
or
13431288
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(Node::FlowSummaryNode).getSummaryNode(),
13441289
cs)
@@ -1644,10 +1589,10 @@ private module Cached {
16441589

16451590
cached
16461591
newtype TContent =
1647-
TVariantTupleFieldContent(Variant v, int pos) { exists(getVariantTupleField(v, pos)) } or
1592+
TTupleFieldContent(TupleField field) or
1593+
TRecordFieldContent(RecordField field) or
16481594
// TODO: Remove once library types are extracted
16491595
TVariantInLibTupleFieldContent(VariantInLib::VariantInLib v, int pos) { pos = v.getAPosition() } or
1650-
TVariantRecordFieldContent(Variant v, string field) { exists(getVariantRecordField(v, field)) } or
16511596
TElementContent() or
16521597
TTuplePositionContent(int pos) {
16531598
pos in [0 .. max([
@@ -1656,9 +1601,6 @@ private module Cached {
16561601
]
16571602
)]
16581603
} or
1659-
TStructFieldContent(Struct s, string field) {
1660-
field = s.getFieldList().(RecordFieldList).getAField().getName().getText()
1661-
} or
16621604
TFunctionCallReturnContent() or
16631605
TFunctionCallArgumentContent(int pos) {
16641606
pos in [0 .. any(CallExpr c).getArgList().getNumberOfArgs() - 1]

0 commit comments

Comments
 (0)