Skip to content

Commit 01e3b26

Browse files
committed
Rust: Implement data flow through tuple structs
1 parent 6f37655 commit 01e3b26

File tree

19 files changed

+238
-161
lines changed

19 files changed

+238
-161
lines changed

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

Lines changed: 2 additions & 6 deletions
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

Lines changed: 0 additions & 2 deletions
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

Lines changed: 64 additions & 124 deletions
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,71 @@ 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+
abstract class FieldContent extends Content {
743+
pragma[nomagic]
744+
abstract FieldExprCfgNode getAnAccess();
751745
}
752746

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

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

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

762759
final override string toString() {
763-
exists(string name |
764-
name = v.getName().getText() and
760+
exists(Variant v, int pos, string vname |
761+
this.isVariantField(v, pos) and
762+
vname = v.getName().getText() and
765763
// only print indices when the arity is > 1
766-
if exists(getVariantTupleField(v, 1)) then result = name + "(" + pos_ + ")" else result = name
764+
if exists(v.getTupleField(1)) then result = vname + "(" + pos + ")" else result = vname
765+
)
766+
or
767+
exists(Struct s, int pos, string sname |
768+
this.isStructField(s, pos) and
769+
sname = s.getName().getText() and
770+
// only print indices when the arity is > 1
771+
if exists(s.getTupleField(1)) then result = sname + "(" + pos + ")" else result = sname
767772
)
768773
}
769774

770-
final override Location getLocation() { result = getVariantTupleField(v, pos_).getLocation() }
775+
final override Location getLocation() { result = field.getLocation() }
771776
}
772777

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

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

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

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

787790
final override string toString() {
788-
exists(string name |
789-
name = v.getName().getText() and
791+
exists(Variant v, string name, string vname |
792+
this.isVariantField(v, name) and
793+
vname = v.getName().getText() and
790794
// 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
795+
if strictcount(v.getRecordField(_)) > 1 then result = vname + "." + name else result = vname
794796
)
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()
797+
or
798+
exists(Struct s, string name, string sname |
799+
this.isStructField(s, name) and
800+
sname = s.getName().getText() and
801+
// only print field when the arity is > 1
802+
if strictcount(s.getRecordField(_)) > 1 then result = sname + "." + name else result = sname
817803
)
818804
}
805+
806+
final override Location getLocation() { result = field.getLocation() }
819807
}
820808

821809
/** A captured variable. */
@@ -859,23 +847,23 @@ final class ElementContent extends Content, TElementContent {
859847
* NOTE: Unlike `struct`s and `enum`s tuples are structural and not nominal,
860848
* hence we don't store a canonical path for them.
861849
*/
862-
final class TuplePositionContent extends Content, TTuplePositionContent {
850+
final class TuplePositionContent extends FieldContent, TTuplePositionContent {
863851
private int pos;
864852

865853
TuplePositionContent() { this = TTuplePositionContent(pos) }
866854

867855
int getPosition() { result = pos }
868856

857+
override FieldExprCfgNode getAnAccess() {
858+
// TODO: limit to tuple types
859+
result.getNameRef().getText().toInt() = pos
860+
}
861+
869862
override string toString() { result = "tuple." + pos.toString() }
870863

871864
override Location getLocation() { result instanceof EmptyLocation }
872865
}
873866

874-
/** Holds if `access` indexes a tuple at an index corresponding to `c`. */
875-
private predicate fieldTuplePositionContent(FieldExprCfgNode access, TuplePositionContent c) {
876-
access.getNameRef().getText().toInt() = c.getPosition()
877-
}
878-
879867
/** A value that represents a set of `Content`s. */
880868
abstract class ContentSet extends TContentSet {
881869
/** Gets a textual representation of this element. */
@@ -1098,23 +1086,6 @@ module RustDataFlow implements InputSig<Location> {
10981086
node2.(Node::FlowSummaryNode).getSummaryNode())
10991087
}
11001088

1101-
/** Gets the item that `p` resolves to, if any. */
1102-
private PathResolution::ItemNode resolvePath(PathAstNode p) {
1103-
result = PathResolution::resolvePath(p.getPath())
1104-
}
1105-
1106-
/** Holds if `p` destructs an enum variant `v`. */
1107-
pragma[nomagic]
1108-
private predicate tupleVariantDestruction(TupleStructPat p, Variant v) { v = resolvePath(p) }
1109-
1110-
/** Holds if `p` destructs an enum variant `v`. */
1111-
pragma[nomagic]
1112-
private predicate recordVariantDestruction(RecordPat p, Variant v) { v = resolvePath(p) }
1113-
1114-
/** Holds if `p` destructs a struct `s`. */
1115-
pragma[nomagic]
1116-
private predicate structDestruction(RecordPat p, Struct s) { s = resolvePath(p) }
1117-
11181089
/**
11191090
* Holds if data can flow from `node1` to `node2` via a read of `c`. Thus,
11201091
* `node1` references an object with a content `c.getAReadContent()` whose
@@ -1126,7 +1097,7 @@ module RustDataFlow implements InputSig<Location> {
11261097
pat = node1.asPat() and
11271098
node2.asPat() = pat.getField(pos)
11281099
|
1129-
tupleVariantDestruction(pat.getPat(), c.(VariantTupleFieldContent).getVariant(pos))
1100+
c = TTupleFieldContent(pat.getTupleStructPat().getTupleField(pos))
11301101
or
11311102
VariantInLib::tupleVariantCanonicalDestruction(pat.getPat(), c, pos)
11321103
)
@@ -1139,25 +1110,17 @@ module RustDataFlow implements InputSig<Location> {
11391110
or
11401111
exists(RecordPatCfgNode pat, string field |
11411112
pat = node1.asPat() and
1142-
(
1143-
// Pattern destructs a struct-like variant.
1144-
recordVariantDestruction(pat.getPat(), c.(VariantRecordFieldContent).getVariant(field))
1145-
or
1146-
// Pattern destructs a struct.
1147-
structDestruction(pat.getPat(), c.(StructFieldContent).getStruct(field))
1148-
) and
1113+
c = TRecordFieldContent(pat.getRecordPat().getRecordField(field)) and
11491114
node2.asPat() = pat.getFieldPat(field)
11501115
)
11511116
or
11521117
c instanceof ReferenceContent and
11531118
node1.asPat().(RefPatCfgNode).getPat() = node2.asPat()
11541119
or
11551120
exists(FieldExprCfgNode access |
1156-
// Read of a tuple entry
1157-
fieldTuplePositionContent(access, c) and
1158-
// TODO: Handle read of a struct field.
11591121
node1.asExpr() = access.getExpr() and
1160-
node2.asExpr() = access
1122+
node2.asExpr() = access and
1123+
access = c.(FieldContent).getAnAccess()
11611124
)
11621125
or
11631126
exists(IndexExprCfgNode arr |
@@ -1199,26 +1162,13 @@ module RustDataFlow implements InputSig<Location> {
11991162
cs, node2.(Node::FlowSummaryNode).getSummaryNode())
12001163
}
12011164

1202-
/** Holds if `ce` constructs an enum value of type `v`. */
1203-
pragma[nomagic]
1204-
private predicate tupleVariantConstruction(CallExpr ce, Variant v) {
1205-
v = resolvePath(ce.getFunction().(PathExpr))
1206-
}
1207-
1208-
/** Holds if `re` constructs an enum value of type `v`. */
12091165
pragma[nomagic]
1210-
private predicate recordVariantConstruction(RecordExpr re, Variant v) { v = resolvePath(re) }
1211-
1212-
/** Holds if `re` constructs a struct value of type `s`. */
1213-
pragma[nomagic]
1214-
private predicate structConstruction(RecordExpr re, Struct s) { s = resolvePath(re) }
1215-
1216-
private predicate tupleAssignment(Node node1, Node node2, TuplePositionContent c) {
1166+
private predicate fieldAssignment(Node node1, Node node2, FieldContent c) {
12171167
exists(AssignmentExprCfgNode assignment, FieldExprCfgNode access |
12181168
assignment.getLhs() = access and
1219-
fieldTuplePositionContent(access, c) and
12201169
node1.asExpr() = assignment.getRhs() and
1221-
node2.asExpr() = access.getExpr()
1170+
node2.asExpr() = access.getExpr() and
1171+
access = c.getAnAccess()
12221172
)
12231173
}
12241174

@@ -1228,20 +1178,13 @@ module RustDataFlow implements InputSig<Location> {
12281178
node1.asExpr() = call.getArgument(pos) and
12291179
node2.asExpr() = call
12301180
|
1231-
tupleVariantConstruction(call.getCallExpr(), c.(VariantTupleFieldContent).getVariant(pos))
1181+
c = TTupleFieldContent(call.getCallExpr().getTupleField(pos))
12321182
or
12331183
VariantInLib::tupleVariantCanonicalConstruction(call.getCallExpr(), c, pos)
12341184
)
12351185
or
12361186
exists(RecordExprCfgNode re, string field |
1237-
(
1238-
// Expression is for a struct-like enum variant.
1239-
recordVariantConstruction(re.getRecordExpr(),
1240-
c.(VariantRecordFieldContent).getVariant(field))
1241-
or
1242-
// Expression is for a struct.
1243-
structConstruction(re.getRecordExpr(), c.(StructFieldContent).getStruct(field))
1244-
) and
1187+
c = TRecordFieldContent(re.getRecordExpr().getRecordField(field)) and
12451188
node1.asExpr() = re.getFieldExpr(field) and
12461189
node2.asExpr() = re
12471190
)
@@ -1258,7 +1201,7 @@ module RustDataFlow implements InputSig<Location> {
12581201
node2.asExpr().(ArrayListExprCfgNode).getAnExpr()
12591202
]
12601203
or
1261-
tupleAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
1204+
fieldAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
12621205
or
12631206
exists(AssignmentExprCfgNode assignment, IndexExprCfgNode index |
12641207
c instanceof ElementContent and
@@ -1294,7 +1237,7 @@ module RustDataFlow implements InputSig<Location> {
12941237
* in `x.f = newValue`.
12951238
*/
12961239
predicate clearsContent(Node n, ContentSet cs) {
1297-
tupleAssignment(_, n, cs.(SingletonContentSet).getContent())
1240+
fieldAssignment(_, n, cs.(SingletonContentSet).getContent())
12981241
or
12991242
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(Node::FlowSummaryNode).getSummaryNode(),
13001243
cs)
@@ -1600,10 +1543,10 @@ private module Cached {
16001543

16011544
cached
16021545
newtype TContent =
1603-
TVariantTupleFieldContent(Variant v, int pos) { exists(getVariantTupleField(v, pos)) } or
1546+
TTupleFieldContent(TupleField field) or
1547+
TRecordFieldContent(RecordField field) or
16041548
// TODO: Remove once library types are extracted
16051549
TVariantInLibTupleFieldContent(VariantInLib::VariantInLib v, int pos) { pos = v.getAPosition() } or
1606-
TVariantRecordFieldContent(Variant v, string field) { exists(getVariantRecordField(v, field)) } or
16071550
TElementContent() or
16081551
TTuplePositionContent(int pos) {
16091552
pos in [0 .. max([
@@ -1612,9 +1555,6 @@ private module Cached {
16121555
]
16131556
)]
16141557
} or
1615-
TStructFieldContent(Struct s, string field) {
1616-
field = s.getFieldList().(RecordFieldList).getAField().getName().getText()
1617-
} or
16181558
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
16191559
TReferenceContent()
16201560

0 commit comments

Comments
 (0)