Skip to content

Commit 02c27dd

Browse files
committed
Rust: Flow through enum constructors
1 parent aeb7b46 commit 02c27dd

File tree

5 files changed

+222
-78
lines changed

5 files changed

+222
-78
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ module DataFlow {
1919

2020
final class PostUpdateNode = Node::PostUpdateNode;
2121

22+
final class Content = DataFlowImpl::Content;
23+
24+
final class ContentSet = DataFlowImpl::ContentSet;
25+
2226
/**
2327
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
2428
* (intra-procedural) step.

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

Lines changed: 178 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ module Node {
115115
*/
116116
ExprCfgNode asExpr() { none() }
117117

118+
/**
119+
* Gets the pattern that corresponds to this node, if any.
120+
*/
121+
PatCfgNode asPat() { none() }
122+
118123
/** Gets the enclosing callable. */
119124
DataFlowCallable getEnclosingCallable() { result = TCfgScope(this.getCfgScope()) }
120125

@@ -177,8 +182,7 @@ module Node {
177182

178183
PatNode() { this = TPatNode(n) }
179184

180-
/** Gets the `PatCfgNode` in the CFG that this node corresponds to. */
181-
PatCfgNode getPat() { result = n }
185+
override PatCfgNode asPat() { result = n }
182186
}
183187

184188
abstract class ParameterNode extends AstCfgFlowNode { }
@@ -333,18 +337,137 @@ module LocalFlow {
333337
nodeFrom.(Node::AstCfgFlowNode).getCfgNode() =
334338
nodeTo.(Node::SsaNode).getDefinitionExt().(Ssa::WriteDefinition).getControlFlowNode()
335339
or
336-
nodeFrom.(Node::PositionalParameterNode).getParameter().getPat() =
337-
nodeTo.(Node::PatNode).getPat()
340+
nodeFrom.(Node::PositionalParameterNode).getParameter().getPat() = nodeTo.asPat()
338341
or
339342
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
340343
or
341344
exists(AssignmentExprCfgNode a |
342345
a.getRhs() = nodeFrom.getCfgNode() and
343346
a.getLhs() = nodeTo.getCfgNode()
344347
)
348+
or
349+
exists(MatchExprCfgNode match |
350+
nodeFrom.asExpr() = match.getExpr() and
351+
nodeTo.asPat() = match.getArmPat(_)
352+
)
345353
}
346354
}
347355

356+
abstract class Content extends TContent {
357+
abstract string toString();
358+
}
359+
360+
abstract private class VariantContent extends Content {
361+
string name;
362+
363+
bindingset[this, name]
364+
VariantContent() { exists(name) }
365+
}
366+
367+
private class VariantTupleContent extends VariantContent, TVariantTupleContent {
368+
private CrateOriginOption crate;
369+
private string path;
370+
private int i;
371+
372+
VariantTupleContent() { this = TVariantTupleContent(crate, path, name, i) }
373+
374+
final override string toString() {
375+
if exists(TVariantTupleContent(crate, path, name, 1))
376+
then result = name + "(" + i + ")"
377+
else result = name
378+
}
379+
}
380+
381+
abstract class ContentSet extends TContentSet {
382+
/** Gets a textual representation of this element. */
383+
abstract string toString();
384+
385+
/** Gets a content that may be stored into when storing into this set. */
386+
abstract Content getAStoreContent();
387+
388+
/** Gets a content that may be read from when reading from this set. */
389+
abstract Content getAReadContent();
390+
}
391+
392+
private class SingletonContentSet extends ContentSet, TSingletonContentSet {
393+
private Content c;
394+
395+
SingletonContentSet() { this = TSingletonContentSet(c) }
396+
397+
Content getContent() { result = c }
398+
399+
override string toString() { result = c.toString() }
400+
401+
override Content getAStoreContent() { result = c }
402+
403+
override Content getAReadContent() { result = c }
404+
}
405+
406+
private import codeql.util.Option
407+
408+
private class CrateOrigin extends string {
409+
CrateOrigin() {
410+
this = [any(Item i).getCrateOrigin(), any(Resolvable r).getResolvedCrateOrigin()]
411+
}
412+
}
413+
414+
private class CrateOriginOption = Option<CrateOrigin>::Option;
415+
416+
pragma[nomagic]
417+
private predicate hasExtendedCanonicalPath(Item i, CrateOriginOption crate, string path) {
418+
path = i.getExtendedCanonicalPath() and
419+
(
420+
crate.asSome() = i.getCrateOrigin()
421+
or
422+
crate.isNone() and
423+
not i.hasCrateOrigin()
424+
)
425+
}
426+
427+
pragma[nomagic]
428+
private predicate resolvesExtendedCanonicalPath(Resolvable r, CrateOriginOption crate, string path) {
429+
path = r.getResolvedPath() and
430+
(
431+
crate.asSome() = r.getResolvedCrateOrigin()
432+
or
433+
crate.isNone() and
434+
not r.hasResolvedCrateOrigin()
435+
)
436+
}
437+
438+
pragma[nomagic]
439+
private predicate callResolvesExtendedCanonicalPath(
440+
CallExprBase call, CrateOriginOption crate, string path
441+
) {
442+
exists(Resolvable r | resolvesExtendedCanonicalPath(r, crate, path) |
443+
r = call.(MethodCallExpr)
444+
or
445+
r = call.(CallExpr).getExpr().(PathExpr).getPath()
446+
)
447+
}
448+
449+
/** Holds if qualified path `p` resolves to variant `c`. */
450+
private predicate pathResolvesToVariant(Path p, VariantContent c, int i) {
451+
exists(CrateOriginOption crate, string path |
452+
resolvesExtendedCanonicalPath(p.getQualifier(), crate, path) and
453+
c = TVariantTupleContent(crate, path, p.getPart().getNameRef().getText(), i)
454+
)
455+
or
456+
// TODO: Remove once library types are extracted
457+
not p.hasQualifier() and
458+
c = TVariantTupleContent(_, "crate::std::option::Option", p.getPart().getNameRef().getText(), i)
459+
}
460+
461+
/** Holds if `ce` constructs an enum value of type `c`. */
462+
private predicate variantConstructor(CallExpr ce, VariantContent c, int i) {
463+
pathResolvesToVariant(ce.getExpr().(PathExpr).getPath(), c, i)
464+
}
465+
466+
/** Holds if `p` destructs an enum value of type `c`. */
467+
private predicate variantDestructor(TupleStructPat p, VariantContent c, int i) {
468+
pathResolvesToVariant(p.getPath(), c, i)
469+
}
470+
348471
private class DataFlowCallableAlias = DataFlowCallable;
349472

350473
private class ReturnKindAlias = ReturnKind;
@@ -353,6 +476,10 @@ private class DataFlowCallAlias = DataFlowCall;
353476

354477
private class ParameterPositionAlias = ParameterPosition;
355478

479+
private class ContentAlias = Content;
480+
481+
private class ContentSetAlias = ContentSet;
482+
356483
module RustDataFlow implements InputSig<Location> {
357484
/**
358485
* An element, viewed as a node in a data flow graph. Either an expression
@@ -399,55 +526,11 @@ module RustDataFlow implements InputSig<Location> {
399526

400527
final class ReturnKind = ReturnKindAlias;
401528

402-
private import codeql.util.Option
403-
404-
private class CrateOrigin extends string {
405-
CrateOrigin() {
406-
this = [any(Item i).getCrateOrigin(), any(Resolvable r).getResolvedCrateOrigin()]
407-
}
408-
}
409-
410-
private class CrateOriginOption = Option<CrateOrigin>::Option;
411-
412-
pragma[nomagic]
413-
private predicate hasExtendedCanonicalPath(
414-
DataFlowCallable c, CrateOriginOption crate, string path
415-
) {
416-
exists(Item i |
417-
i = c.asCfgScope() and
418-
path = i.getExtendedCanonicalPath()
419-
|
420-
crate.asSome() = i.getCrateOrigin()
421-
or
422-
crate.isNone() and
423-
not i.hasCrateOrigin()
424-
)
425-
}
426-
427-
pragma[nomagic]
428-
private predicate resolvesExtendedCanonicalPath(
429-
DataFlowCall c, CrateOriginOption crate, string path
430-
) {
431-
exists(Resolvable r |
432-
path = r.getResolvedPath() and
433-
(
434-
r = c.asMethodCallExprCfgNode().getExpr()
435-
or
436-
r = c.asCallExprCfgNode().getExpr().(PathExprCfgNode).getPath()
437-
)
438-
|
439-
crate.asSome() = r.getResolvedCrateOrigin()
440-
or
441-
crate.isNone() and
442-
not r.hasResolvedCrateOrigin()
443-
)
444-
}
445-
446529
/** Gets a viable implementation of the target of the given `Call`. */
447530
DataFlowCallable viableCallable(DataFlowCall call) {
448531
exists(string path, CrateOriginOption crate |
449-
hasExtendedCanonicalPath(result, crate, path) and
450-
resolvesExtendedCanonicalPath(call, crate, path)
532+
hasExtendedCanonicalPath(result.asCfgScope(), crate, path) and
533+
callResolvesExtendedCanonicalPath(call.asCallBaseExprCfgNode().getExpr(), crate, path)
451534
)
452535
}
453536

@@ -469,24 +552,15 @@ module RustDataFlow implements InputSig<Location> {
469552

470553
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
471554

472-
final class Content = Void;
555+
class Content = ContentAlias;
473556

474-
predicate forceHighPrecision(Content c) { none() }
475-
476-
class ContentSet extends TContentSet {
477-
/** Gets a textual representation of this element. */
478-
string toString() { result = "ContentSet" }
479-
480-
/** Gets a content that may be stored into when storing into this set. */
481-
Content getAStoreContent() { none() }
557+
class ContentSet = ContentSetAlias;
482558

483-
/** Gets a content that may be read from when reading from this set. */
484-
Content getAReadContent() { none() }
485-
}
559+
predicate forceHighPrecision(Content c) { none() }
486560

487-
final class ContentApprox = Void;
561+
final class ContentApprox = Content; // todo
488562

489-
ContentApprox getContentApprox(Content c) { any() }
563+
ContentApprox getContentApprox(Content c) { result = c }
490564

491565
class ParameterPosition = ParameterPositionAlias;
492566

@@ -519,14 +593,31 @@ module RustDataFlow implements InputSig<Location> {
519593
* `node1` references an object with a content `c.getAReadContent()` whose
520594
* value ends up in `node2`.
521595
*/
522-
predicate readStep(Node node1, ContentSet c, Node node2) { none() }
596+
predicate readStep(Node node1, ContentSet c, Node node2) {
597+
node1.asPat() =
598+
any(TupleStructPatCfgNode pat, int i |
599+
variantDestructor(pat.getPat(), c.(SingletonContentSet).getContent(), i) and
600+
node2.asPat() = pat.getField(i)
601+
|
602+
pat
603+
)
604+
}
523605

524606
/**
525607
* Holds if data can flow from `node1` to `node2` via a store into `c`. Thus,
526608
* `node2` references an object with a content `c.getAStoreContent()` that
527609
* contains the value of `node1`.
528610
*/
529-
predicate storeStep(Node node1, ContentSet c, Node node2) { none() }
611+
predicate storeStep(Node node1, ContentSet c, Node node2) {
612+
// todo: use post-update
613+
node2.asExpr() =
614+
any(CallExprCfgNode call, int i |
615+
variantConstructor(call.getCallExpr(), c.(SingletonContentSet).getContent(), i) and
616+
node1.asExpr() = call.getArgument(i)
617+
|
618+
call
619+
)
620+
}
530621

531622
/**
532623
* Holds if values stored inside content `c` are cleared at node `n`. For example,
@@ -593,8 +684,6 @@ module RustDataFlow implements InputSig<Location> {
593684
class DataFlowSecondLevelScope = Void;
594685
}
595686

596-
final class ContentSet = RustDataFlow::ContentSet;
597-
598687
import MakeImpl<Location, RustDataFlow>
599688

600689
/** A collection of cached types and predicates to be evaluated in the same stage. */
@@ -612,14 +701,6 @@ private module Cached {
612701
cached
613702
newtype TDataFlowCall = TCall(CallExprBaseCfgNode c)
614703

615-
cached
616-
newtype TOptionalContentSet =
617-
TAnyElementContent() or
618-
TAnyContent()
619-
620-
cached
621-
class TContentSet = TAnyElementContent or TAnyContent;
622-
623704
cached
624705
newtype TDataFlowCallable = TCfgScope(CfgScope scope)
625706

@@ -635,6 +716,27 @@ private module Cached {
635716
i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1]
636717
} or
637718
TSelfParameterPosition()
719+
720+
cached
721+
newtype TContent =
722+
TVariantTupleContent(CrateOriginOption crate, string path, string name, int i) {
723+
exists(Enum e, Variant v |
724+
hasExtendedCanonicalPath(e, crate, path) and
725+
v = e.getVariantList().getAVariant() and
726+
name = v.getName().getText() and
727+
i in [0 .. v.getFieldList().(TupleFieldList).getNumberOfFields() - 1]
728+
)
729+
or
730+
// TODO: Remove once library types are extracted
731+
crate.isNone() and
732+
path = "crate::std::option::Option" and
733+
name = "Some" and
734+
i = 0
735+
}
736+
737+
// todo: add TVariantRecordContent
738+
cached
739+
newtype TContentSet = TSingletonContentSet(Content c)
638740
}
639741

640742
import Cached

0 commit comments

Comments
 (0)