From 5e7cd461178d756b739700e397784b73b99a6286 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 22 Nov 2024 14:54:39 +0100 Subject: [PATCH] Rust: Flow through variants --- .../lib/codeql/rust/controlflow/CfgNodes.qll | 36 +- .../rust/controlflow/internal/CfgNodes.qll | 6 + rust/ql/lib/codeql/rust/dataflow/DataFlow.qll | 6 + .../rust/dataflow/internal/DataFlowImpl.qll | 348 ++++++++++++++---- .../dataflow/local/DataFlowStep.expected | 60 +++ .../dataflow/local/inline-flow.expected | 44 +++ .../test/library-tests/dataflow/local/main.rs | 10 +- 7 files changed, 427 insertions(+), 83 deletions(-) diff --git a/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll b/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll index 3ee3a3eeb611..07bc8946bc18 100644 --- a/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll +++ b/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll @@ -187,9 +187,37 @@ final class RecordExprCfgNode extends Nodes::RecordExprCfgNode { RecordExprCfgNode() { node = this.getRecordExpr() } - /** Gets the `i`th record expression. */ - ExprCfgNode getExpr(int i) { - any(ChildMapping mapping) - .hasCfgChild(node, node.getRecordExprFieldList().getField(i).getExpr(), this, result) + /** Gets the record expression for the field `field`. */ + pragma[nomagic] + ExprCfgNode getFieldExpr(string field) { + exists(RecordExprField ref | + ref = node.getRecordExprFieldList().getAField() and + any(ChildMapping mapping).hasCfgChild(node, ref.getExpr(), this, result) and + field = ref.getNameRef().getText() + ) + } +} + +/** + * A record pattern. For example: + * ```rust + * match x { + * Foo { a: 1, b: 2 } => "ok", + * Foo { .. } => "fail", + * } + * ``` + */ +final class RecordPatCfgNode extends Nodes::RecordPatCfgNode { + private RecordPatChildMapping node; + + RecordPatCfgNode() { node = this.getRecordPat() } + + /** Gets the record pattern for the field `field`. */ + PatCfgNode getFieldPat(string field) { + exists(RecordPatField rpf | + rpf = node.getRecordPatFieldList().getAField() and + any(ChildMapping mapping).hasCfgChild(node, rpf.getPat(), this, result) and + field = rpf.getNameRef().getText() + ) } } diff --git a/rust/ql/lib/codeql/rust/controlflow/internal/CfgNodes.qll b/rust/ql/lib/codeql/rust/controlflow/internal/CfgNodes.qll index fc0df95f24a2..0f3dee7a9f49 100644 --- a/rust/ql/lib/codeql/rust/controlflow/internal/CfgNodes.qll +++ b/rust/ql/lib/codeql/rust/controlflow/internal/CfgNodes.qll @@ -68,6 +68,12 @@ class RecordExprChildMapping extends ParentAstNode, RecordExpr { } } +class RecordPatChildMapping extends ParentAstNode, RecordPat { + override predicate relevantChild(AstNode child) { + child = this.getRecordPatFieldList().getAField().getPat() + } +} + class FormatArgsExprChildMapping extends ParentAstNode, CfgImpl::ExprTrees::FormatArgsExprTree { override predicate relevantChild(AstNode child) { child = this.getChildNode(_) } } diff --git a/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll b/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll index 3a09df2c45d1..c0d47f8f6a77 100644 --- a/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll +++ b/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll @@ -19,6 +19,12 @@ module DataFlow { final class PostUpdateNode = Node::PostUpdateNode; + final class Content = DataFlowImpl::Content; + + final class VariantContent = DataFlowImpl::VariantContent; + + final class ContentSet = DataFlowImpl::ContentSet; + /** * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 7c78db0588b6..808b410c8d00 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -115,6 +115,11 @@ module Node { */ ExprCfgNode asExpr() { none() } + /** + * Gets the pattern that corresponds to this node, if any. + */ + PatCfgNode asPat() { none() } + /** Gets the enclosing callable. */ DataFlowCallable getEnclosingCallable() { result = TCfgScope(this.getCfgScope()) } @@ -177,8 +182,7 @@ module Node { PatNode() { this = TPatNode(n) } - /** Gets the `PatCfgNode` in the CFG that this node corresponds to. */ - PatCfgNode getPat() { result = n } + override PatCfgNode asPat() { result = n } } /** @@ -322,8 +326,7 @@ module LocalFlow { nodeFrom.(Node::AstCfgFlowNode).getCfgNode() = nodeTo.(Node::SsaNode).getDefinitionExt().(Ssa::WriteDefinition).getControlFlowNode() or - nodeFrom.(Node::ParameterNode).getParameter().(ParamCfgNode).getPat() = - nodeTo.(Node::PatNode).getPat() + nodeFrom.(Node::ParameterNode).getParameter().(ParamCfgNode).getPat() = nodeTo.asPat() or SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _) or @@ -331,18 +334,165 @@ module LocalFlow { a.getRhs() = nodeFrom.getCfgNode() and a.getLhs() = nodeTo.getCfgNode() ) + or + exists(MatchExprCfgNode match | + nodeFrom.asExpr() = match.getScrutinee() and + nodeTo.asPat() = match.getArmPat(_) + ) + or + nodeFrom.asPat().(OrPatCfgNode).getAPat() = nodeTo.asPat() + } +} + +private import codeql.util.Option + +private class CrateOrigin extends string { + CrateOrigin() { + this = [any(Item i).getCrateOrigin(), any(Resolvable r).getResolvedCrateOrigin()] + } +} + +private class CrateOriginOption = Option::Option; + +pragma[nomagic] +private predicate hasExtendedCanonicalPath(Item i, CrateOriginOption crate, string path) { + path = i.getExtendedCanonicalPath() and + ( + crate.asSome() = i.getCrateOrigin() + or + crate.isNone() and + not i.hasCrateOrigin() + ) +} + +pragma[nomagic] +private predicate variantHasExtendedCanonicalPath( + Enum e, Variant v, CrateOriginOption crate, string path, string name +) { + hasExtendedCanonicalPath(e, crate, path) and + v = e.getVariantList().getAVariant() and + name = v.getName().getText() +} + +pragma[nomagic] +private predicate resolveExtendedCanonicalPath(Resolvable r, CrateOriginOption crate, string path) { + path = r.getResolvedPath() and + ( + crate.asSome() = r.getResolvedCrateOrigin() + or + crate.isNone() and + not r.hasResolvedCrateOrigin() + ) +} + +/** + * A reference contained in an object. For example a field in a struct. + */ +abstract class Content extends TContent { + /** Gets a textual representation of this content. */ + abstract string toString(); +} + +/** A canonical path pointing to an enum variant. */ +private class VariantCanonicalPath extends MkVariantCanonicalPath { + CrateOriginOption crate; + string path; + string name; + + VariantCanonicalPath() { this = MkVariantCanonicalPath(crate, path, name) } + + /** Gets the underlying variant. */ + Variant getVariant() { variantHasExtendedCanonicalPath(_, result, crate, path, name) } + + string toString() { result = name } + + Location getLocation() { result = this.getVariant().getLocation() } +} + +/** + * A variant of an `enum`. In addition to the variant itself, this also includes the + * position (for tuple variants) or the field name (for record variants). + */ +abstract class VariantContent extends Content { } + +/** A tuple variant. */ +private class TupleVariantContent extends VariantContent, TTupleVariantContent { + private VariantCanonicalPath v; + private int pos_; + + TupleVariantContent() { this = TTupleVariantContent(v, pos_) } + + VariantCanonicalPath getVariantCanonicalPath(int pos) { result = v and pos = pos_ } + + final override string toString() { + // only print indices when the arity is > 1 + if exists(TTupleVariantContent(v, 1)) + then result = v.toString() + "(" + pos_ + ")" + else result = v.toString() } } -private class DataFlowCallableAlias = DataFlowCallable; +/** A record variant. */ +private class RecordVariantContent extends VariantContent, TRecordVariantContent { + private VariantCanonicalPath v; + private string field_; + + RecordVariantContent() { this = TRecordVariantContent(v, field_) } -private class ReturnKindAlias = ReturnKind; + VariantCanonicalPath getVariantCanonicalPath(string field) { result = v and field = field_ } -private class DataFlowCallAlias = DataFlowCall; + final override string toString() { + // only print field when the arity is > 1 + if strictcount(string f | exists(TRecordVariantContent(v, f))) > 1 + then result = v.toString() + "{" + field_ + "}" + else result = v.toString() + } +} -private class ParameterPositionAlias = ParameterPosition; +/** A value that represents a set of `Content`s. */ +abstract class ContentSet extends TContentSet { + /** Gets a textual representation of this element. */ + abstract string toString(); + + /** Gets a content that may be stored into when storing into this set. */ + abstract Content getAStoreContent(); + + /** Gets a content that may be read from when reading from this set. */ + abstract Content getAReadContent(); +} + +private class SingletonContentSet extends ContentSet, TSingletonContentSet { + private Content c; + + SingletonContentSet() { this = TSingletonContentSet(c) } + + Content getContent() { result = c } + + override string toString() { result = c.toString() } + + override Content getAStoreContent() { result = c } + + override Content getAReadContent() { result = c } +} + +// Defines a set of aliases needed for the `RustDataFlow` module +private module Aliases { + class DataFlowCallableAlias = DataFlowCallable; + + class ReturnKindAlias = ReturnKind; + + class DataFlowCallAlias = DataFlowCall; + + class ParameterPositionAlias = ParameterPosition; + + class ContentAlias = Content; + + class ContentSetAlias = ContentSet; +} module RustDataFlow implements InputSig { + private import Aliases + /** * An element, viewed as a node in a data flow graph. Either an expression * (`ExprNode`) or a parameter (`ParameterNode`). @@ -388,55 +538,22 @@ module RustDataFlow implements InputSig { final class ReturnKind = ReturnKindAlias; - private import codeql.util.Option - - private class CrateOrigin extends string { - CrateOrigin() { - this = [any(Item i).getCrateOrigin(), any(Resolvable r).getResolvedCrateOrigin()] - } - } - - private class CrateOriginOption = Option::Option; - pragma[nomagic] - private predicate hasExtendedCanonicalPath( - DataFlowCallable c, CrateOriginOption crate, string path + private predicate callResolveExtendedCanonicalPath( + CallExprBase call, CrateOriginOption crate, string path ) { - exists(Item i | - i = c.asCfgScope() and - path = i.getExtendedCanonicalPath() - | - crate.asSome() = i.getCrateOrigin() + exists(Resolvable r | resolveExtendedCanonicalPath(r, crate, path) | + r = call.(MethodCallExpr) or - crate.isNone() and - not i.hasCrateOrigin() - ) - } - - pragma[nomagic] - private predicate resolvesExtendedCanonicalPath( - DataFlowCall c, CrateOriginOption crate, string path - ) { - exists(Resolvable r | - path = r.getResolvedPath() and - ( - r = c.asMethodCallExprCfgNode().getExpr() - or - r = c.asCallExprCfgNode().getExpr().(PathExprCfgNode).getPath() - ) - | - crate.asSome() = r.getResolvedCrateOrigin() - or - crate.isNone() and - not r.hasResolvedCrateOrigin() + r = call.(CallExpr).getExpr().(PathExpr).getPath() ) } /** Gets a viable implementation of the target of the given `Call`. */ DataFlowCallable viableCallable(DataFlowCall call) { exists(string path, CrateOriginOption crate | - hasExtendedCanonicalPath(result, crate, path) and - resolvesExtendedCanonicalPath(call, crate, path) + hasExtendedCanonicalPath(result.asCfgScope(), crate, path) and + callResolveExtendedCanonicalPath(call.asCallBaseExprCfgNode().getExpr(), crate, path) ) } @@ -458,24 +575,15 @@ module RustDataFlow implements InputSig { predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() } - final class Content = Void; + class Content = ContentAlias; - predicate forceHighPrecision(Content c) { none() } + class ContentSet = ContentSetAlias; - class ContentSet extends TContentSet { - /** Gets a textual representation of this element. */ - string toString() { result = "ContentSet" } - - /** Gets a content that may be stored into when storing into this set. */ - Content getAStoreContent() { none() } - - /** Gets a content that may be read from when reading from this set. */ - Content getAReadContent() { none() } - } + predicate forceHighPrecision(Content c) { none() } - final class ContentApprox = Void; + final class ContentApprox = Content; // todo - ContentApprox getContentApprox(Content c) { any() } + ContentApprox getContentApprox(Content c) { result = c } class ParameterPosition = ParameterPositionAlias; @@ -503,19 +611,94 @@ module RustDataFlow implements InputSig { */ predicate jumpStep(Node node1, Node node2) { none() } + /** Holds if path `p` resolves to variant `v`. */ + private predicate pathResolveToVariantCanonicalPath(Path p, VariantCanonicalPath v) { + exists(CrateOriginOption crate, string path | + resolveExtendedCanonicalPath(p.getQualifier(), crate, path) and + v = MkVariantCanonicalPath(crate, path, p.getPart().getNameRef().getText()) + ) + or + // TODO: Remove once library types are extracted + not p.hasQualifier() and + v = MkVariantCanonicalPath(_, "crate::std::option::Option", p.getPart().getNameRef().getText()) + } + + /** Holds if `p` destructs an enum variant `v`. */ + pragma[nomagic] + private predicate tupleVariantDestruction(TupleStructPat p, VariantCanonicalPath v) { + pathResolveToVariantCanonicalPath(p.getPath(), v) + } + + /** Holds if `p` destructs an enum variant `v`. */ + pragma[nomagic] + private predicate recordVariantDestruction(RecordPat p, VariantCanonicalPath v) { + pathResolveToVariantCanonicalPath(p.getPath(), v) + } + /** * Holds if data can flow from `node1` to `node2` via a read of `c`. Thus, * `node1` references an object with a content `c.getAReadContent()` whose * value ends up in `node2`. */ - predicate readStep(Node node1, ContentSet c, Node node2) { none() } + predicate readStep(Node node1, ContentSet cs, Node node2) { + exists(Content c | c = cs.(SingletonContentSet).getContent() | + node1.asPat() = + any(TupleStructPatCfgNode pat, int pos | + tupleVariantDestruction(pat.getPat(), c.(TupleVariantContent).getVariantCanonicalPath(pos)) and + node2.asPat() = pat.getField(pos) + | + pat + ) + or + node1.asPat() = + any(RecordPatCfgNode pat, string field | + recordVariantDestruction(pat.getPat(), + c.(RecordVariantContent).getVariantCanonicalPath(field)) and + node2.asPat() = pat.getFieldPat(field) + | + pat + ) + ) + } + + /** Holds if `ce` constructs an enum value of type `v`. */ + pragma[nomagic] + private predicate tupleVariantConstruction(CallExpr ce, VariantCanonicalPath v) { + pathResolveToVariantCanonicalPath(ce.getExpr().(PathExpr).getPath(), v) + } + + /** Holds if `re` constructs an enum value of type `v`. */ + pragma[nomagic] + private predicate recordVariantConstruction(RecordExpr re, VariantCanonicalPath v) { + pathResolveToVariantCanonicalPath(re.getPath(), v) + } /** * Holds if data can flow from `node1` to `node2` via a store into `c`. Thus, * `node2` references an object with a content `c.getAStoreContent()` that * contains the value of `node1`. */ - predicate storeStep(Node node1, ContentSet c, Node node2) { none() } + predicate storeStep(Node node1, ContentSet cs, Node node2) { + exists(Content c | c = cs.(SingletonContentSet).getContent() | + node2.asExpr() = + any(CallExprCfgNode call, int pos | + tupleVariantConstruction(call.getCallExpr(), + c.(TupleVariantContent).getVariantCanonicalPath(pos)) and + node1.asExpr() = call.getArgument(pos) + | + call + ) + or + node2.asExpr() = + any(RecordExprCfgNode re, string field | + recordVariantConstruction(re.getRecordExpr(), + c.(RecordVariantContent).getVariantCanonicalPath(field)) and + node1.asExpr() = re.getFieldExpr(field) + | + re + ) + ) + } /** * Holds if values stored inside content `c` are cleared at node `n`. For example, @@ -582,8 +765,6 @@ module RustDataFlow implements InputSig { class DataFlowSecondLevelScope = Void; } -final class ContentSet = RustDataFlow::ContentSet; - import MakeImpl /** A collection of cached types and predicates to be evaluated in the same stage. */ @@ -600,14 +781,6 @@ private module Cached { cached newtype TDataFlowCall = TCall(CallExprBaseCfgNode c) - cached - newtype TOptionalContentSet = - TAnyElementContent() or - TAnyContent() - - cached - class TContentSet = TAnyElementContent or TAnyContent; - cached newtype TDataFlowCallable = TCfgScope(CfgScope scope) @@ -623,6 +796,33 @@ private module Cached { i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1] } or TSelfParameterPosition() + + cached + newtype TVariantCanonicalPath = + MkVariantCanonicalPath(CrateOriginOption crate, string path, string name) { + variantHasExtendedCanonicalPath(_, _, crate, path, name) + or + // TODO: Remove once library types are extracted + crate.isNone() and + path = "crate::std::option::Option" and + name = "Some" + } + + cached + newtype TContent = + TTupleVariantContent(VariantCanonicalPath v, int pos) { + pos in [0 .. v.getVariant().getFieldList().(TupleFieldList).getNumberOfFields() - 1] + or + // TODO: Remove once library types are extracted + v = MkVariantCanonicalPath(_, "crate::std::option::Option", "Some") and + pos = 0 + } or + TRecordVariantContent(VariantCanonicalPath v, string field) { + field = v.getVariant().getFieldList().(RecordFieldList).getAField().getName().getText() + } + + cached + newtype TContentSet = TSingletonContentSet(Content c) } import Cached diff --git a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected index f16bdf9a844d..a49da7516790 100644 --- a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -34,6 +34,8 @@ localStep | main.rs:32:9:32:9 | [SSA] b | main.rs:36:10:36:10 | b | | main.rs:32:9:32:9 | b | main.rs:32:9:32:9 | [SSA] b | | main.rs:32:13:35:5 | match m { ... } | main.rs:32:9:32:9 | b | +| main.rs:32:19:32:19 | m | main.rs:33:9:33:15 | TupleStructPat | +| main.rs:32:19:32:19 | m | main.rs:34:9:34:12 | None | | main.rs:33:20:33:20 | a | main.rs:32:13:35:5 | match m { ... } | | main.rs:34:17:34:17 | 0 | main.rs:32:13:35:5 | match m { ... } | | main.rs:40:9:40:9 | [SSA] a | main.rs:43:10:43:10 | a | @@ -80,11 +82,15 @@ localStep | main.rs:105:9:105:10 | [SSA] s2 | main.rs:110:11:110:12 | s2 | | main.rs:105:9:105:10 | s2 | main.rs:105:9:105:10 | [SSA] s2 | | main.rs:105:14:105:28 | ...::Some(...) | main.rs:105:9:105:10 | s2 | +| main.rs:106:11:106:12 | s1 | main.rs:107:9:107:23 | TupleStructPat | +| main.rs:106:11:106:12 | s1 | main.rs:108:9:108:20 | ...::None | | main.rs:107:22:107:22 | [SSA] n | main.rs:107:33:107:33 | n | | main.rs:107:22:107:22 | n | main.rs:107:22:107:22 | [SSA] n | | main.rs:107:28:107:34 | sink(...) | main.rs:106:5:109:5 | match s1 { ... } | | main.rs:108:25:108:31 | sink(...) | main.rs:106:5:109:5 | match s1 { ... } | | main.rs:110:5:113:5 | match s2 { ... } | main.rs:103:37:114:1 | { ... } | +| main.rs:110:11:110:12 | s2 | main.rs:111:9:111:23 | TupleStructPat | +| main.rs:110:11:110:12 | s2 | main.rs:112:9:112:20 | ...::None | | main.rs:111:22:111:22 | [SSA] n | main.rs:111:33:111:33 | n | | main.rs:111:22:111:22 | n | main.rs:111:22:111:22 | [SSA] n | | main.rs:111:28:111:34 | sink(...) | main.rs:110:5:113:5 | match s2 { ... } | @@ -95,11 +101,15 @@ localStep | main.rs:118:9:118:10 | [SSA] s2 | main.rs:123:11:123:12 | s2 | | main.rs:118:9:118:10 | s2 | main.rs:118:9:118:10 | [SSA] s2 | | main.rs:118:14:118:20 | Some(...) | main.rs:118:9:118:10 | s2 | +| main.rs:119:11:119:12 | s1 | main.rs:120:9:120:15 | TupleStructPat | +| main.rs:119:11:119:12 | s1 | main.rs:121:9:121:12 | None | | main.rs:120:14:120:14 | [SSA] n | main.rs:120:25:120:25 | n | | main.rs:120:14:120:14 | n | main.rs:120:14:120:14 | [SSA] n | | main.rs:120:20:120:26 | sink(...) | main.rs:119:5:122:5 | match s1 { ... } | | main.rs:121:17:121:23 | sink(...) | main.rs:119:5:122:5 | match s1 { ... } | | main.rs:123:5:126:5 | match s2 { ... } | main.rs:116:39:127:1 | { ... } | +| main.rs:123:11:123:12 | s2 | main.rs:124:9:124:15 | TupleStructPat | +| main.rs:123:11:123:12 | s2 | main.rs:125:9:125:12 | None | | main.rs:124:14:124:14 | [SSA] n | main.rs:124:25:124:25 | n | | main.rs:124:14:124:14 | n | main.rs:124:14:124:14 | [SSA] n | | main.rs:124:20:124:26 | sink(...) | main.rs:123:5:126:5 | match s2 { ... } | @@ -110,6 +120,8 @@ localStep | main.rs:136:9:136:10 | [SSA] s2 | main.rs:144:11:144:12 | s2 | | main.rs:136:9:136:10 | s2 | main.rs:136:9:136:10 | [SSA] s2 | | main.rs:136:14:136:30 | ...::B(...) | main.rs:136:9:136:10 | s2 | +| main.rs:137:11:137:12 | s1 | main.rs:138:9:138:25 | TupleStructPat | +| main.rs:137:11:137:12 | s1 | main.rs:139:9:139:25 | TupleStructPat | | main.rs:137:11:137:12 | s1 | main.rs:141:11:141:12 | s1 | | main.rs:138:24:138:24 | [SSA] n | main.rs:138:35:138:35 | n | | main.rs:138:24:138:24 | n | main.rs:138:24:138:24 | [SSA] n | @@ -117,6 +129,9 @@ localStep | main.rs:139:24:139:24 | [SSA] n | main.rs:139:35:139:35 | n | | main.rs:139:24:139:24 | n | main.rs:139:24:139:24 | [SSA] n | | main.rs:139:30:139:36 | sink(...) | main.rs:137:5:140:5 | match s1 { ... } | +| main.rs:141:11:141:12 | s1 | main.rs:142:10:142:46 | ... \| ... | +| main.rs:142:10:142:46 | ... \| ... | main.rs:142:10:142:26 | TupleStructPat | +| main.rs:142:10:142:46 | ... \| ... | main.rs:142:30:142:46 | TupleStructPat | | main.rs:142:10:142:46 | [SSA] [match(true)] phi | main.rs:142:57:142:57 | n | | main.rs:142:25:142:25 | [SSA] [input] [match(true)] phi | main.rs:142:10:142:46 | [SSA] [match(true)] phi | | main.rs:142:25:142:25 | [SSA] n | main.rs:142:25:142:25 | [SSA] [input] [match(true)] phi | @@ -126,6 +141,8 @@ localStep | main.rs:142:45:142:45 | n | main.rs:142:45:142:45 | [SSA] n | | main.rs:142:52:142:58 | sink(...) | main.rs:141:5:143:5 | match s1 { ... } | | main.rs:144:5:147:5 | match s2 { ... } | main.rs:134:48:148:1 | { ... } | +| main.rs:144:11:144:12 | s2 | main.rs:145:9:145:25 | TupleStructPat | +| main.rs:144:11:144:12 | s2 | main.rs:146:9:146:25 | TupleStructPat | | main.rs:145:24:145:24 | [SSA] n | main.rs:145:35:145:35 | n | | main.rs:145:24:145:24 | n | main.rs:145:24:145:24 | [SSA] n | | main.rs:145:30:145:36 | sink(...) | main.rs:144:5:147:5 | match s2 { ... } | @@ -138,6 +155,8 @@ localStep | main.rs:154:9:154:10 | [SSA] s2 | main.rs:162:11:162:12 | s2 | | main.rs:154:9:154:10 | s2 | main.rs:154:9:154:10 | [SSA] s2 | | main.rs:154:14:154:17 | B(...) | main.rs:154:9:154:10 | s2 | +| main.rs:155:11:155:12 | s1 | main.rs:156:9:156:12 | TupleStructPat | +| main.rs:155:11:155:12 | s1 | main.rs:157:9:157:12 | TupleStructPat | | main.rs:155:11:155:12 | s1 | main.rs:159:11:159:12 | s1 | | main.rs:156:11:156:11 | [SSA] n | main.rs:156:22:156:22 | n | | main.rs:156:11:156:11 | n | main.rs:156:11:156:11 | [SSA] n | @@ -145,6 +164,9 @@ localStep | main.rs:157:11:157:11 | [SSA] n | main.rs:157:22:157:22 | n | | main.rs:157:11:157:11 | n | main.rs:157:11:157:11 | [SSA] n | | main.rs:157:17:157:23 | sink(...) | main.rs:155:5:158:5 | match s1 { ... } | +| main.rs:159:11:159:12 | s1 | main.rs:160:10:160:20 | ... \| ... | +| main.rs:160:10:160:20 | ... \| ... | main.rs:160:10:160:13 | TupleStructPat | +| main.rs:160:10:160:20 | ... \| ... | main.rs:160:17:160:20 | TupleStructPat | | main.rs:160:10:160:20 | [SSA] [match(true)] phi | main.rs:160:31:160:31 | n | | main.rs:160:12:160:12 | [SSA] [input] [match(true)] phi | main.rs:160:10:160:20 | [SSA] [match(true)] phi | | main.rs:160:12:160:12 | [SSA] n | main.rs:160:12:160:12 | [SSA] [input] [match(true)] phi | @@ -154,6 +176,8 @@ localStep | main.rs:160:19:160:19 | n | main.rs:160:19:160:19 | [SSA] n | | main.rs:160:26:160:32 | sink(...) | main.rs:159:5:161:5 | match s1 { ... } | | main.rs:162:5:165:5 | match s2 { ... } | main.rs:152:50:166:1 | { ... } | +| main.rs:162:11:162:12 | s2 | main.rs:163:9:163:12 | TupleStructPat | +| main.rs:162:11:162:12 | s2 | main.rs:164:9:164:12 | TupleStructPat | | main.rs:163:11:163:11 | [SSA] n | main.rs:163:22:163:22 | n | | main.rs:163:11:163:11 | n | main.rs:163:11:163:11 | [SSA] n | | main.rs:163:17:163:23 | sink(...) | main.rs:162:5:165:5 | match s2 { ... } | @@ -166,6 +190,8 @@ localStep | main.rs:177:9:177:10 | [SSA] s2 | main.rs:185:11:185:12 | s2 | | main.rs:177:9:177:10 | s2 | main.rs:177:9:177:10 | [SSA] s2 | | main.rs:177:14:177:43 | ...::D {...} | main.rs:177:9:177:10 | s2 | +| main.rs:178:11:178:12 | s1 | main.rs:179:9:179:38 | ...::C {...} | +| main.rs:178:11:178:12 | s1 | main.rs:180:9:180:38 | ...::D {...} | | main.rs:178:11:178:12 | s1 | main.rs:182:11:182:12 | s1 | | main.rs:179:36:179:36 | [SSA] n | main.rs:179:48:179:48 | n | | main.rs:179:36:179:36 | n | main.rs:179:36:179:36 | [SSA] n | @@ -173,6 +199,9 @@ localStep | main.rs:180:36:180:36 | [SSA] n | main.rs:180:48:180:48 | n | | main.rs:180:36:180:36 | n | main.rs:180:36:180:36 | [SSA] n | | main.rs:180:43:180:49 | sink(...) | main.rs:178:5:181:5 | match s1 { ... } | +| main.rs:182:11:182:12 | s1 | main.rs:183:10:183:72 | ... \| ... | +| main.rs:183:10:183:72 | ... \| ... | main.rs:183:10:183:39 | ...::C {...} | +| main.rs:183:10:183:72 | ... \| ... | main.rs:183:43:183:72 | ...::D {...} | | main.rs:183:10:183:72 | [SSA] [match(true)] phi | main.rs:183:83:183:83 | n | | main.rs:183:37:183:37 | [SSA] [input] [match(true)] phi | main.rs:183:10:183:72 | [SSA] [match(true)] phi | | main.rs:183:37:183:37 | [SSA] n | main.rs:183:37:183:37 | [SSA] [input] [match(true)] phi | @@ -182,6 +211,8 @@ localStep | main.rs:183:70:183:70 | n | main.rs:183:70:183:70 | [SSA] n | | main.rs:183:78:183:84 | sink(...) | main.rs:182:5:184:5 | match s1 { ... } | | main.rs:185:5:188:5 | match s2 { ... } | main.rs:173:49:189:1 | { ... } | +| main.rs:185:11:185:12 | s2 | main.rs:186:9:186:38 | ...::C {...} | +| main.rs:185:11:185:12 | s2 | main.rs:187:9:187:38 | ...::D {...} | | main.rs:186:36:186:36 | [SSA] n | main.rs:186:48:186:48 | n | | main.rs:186:36:186:36 | n | main.rs:186:36:186:36 | [SSA] n | | main.rs:186:43:186:49 | sink(...) | main.rs:185:5:188:5 | match s2 { ... } | @@ -194,6 +225,8 @@ localStep | main.rs:197:9:197:10 | [SSA] s2 | main.rs:205:11:205:12 | s2 | | main.rs:197:9:197:10 | s2 | main.rs:197:9:197:10 | [SSA] s2 | | main.rs:197:14:197:29 | D {...} | main.rs:197:9:197:10 | s2 | +| main.rs:198:11:198:12 | s1 | main.rs:199:9:199:24 | C {...} | +| main.rs:198:11:198:12 | s1 | main.rs:200:9:200:24 | D {...} | | main.rs:198:11:198:12 | s1 | main.rs:202:11:202:12 | s1 | | main.rs:199:22:199:22 | [SSA] n | main.rs:199:34:199:34 | n | | main.rs:199:22:199:22 | n | main.rs:199:22:199:22 | [SSA] n | @@ -201,6 +234,9 @@ localStep | main.rs:200:22:200:22 | [SSA] n | main.rs:200:34:200:34 | n | | main.rs:200:22:200:22 | n | main.rs:200:22:200:22 | [SSA] n | | main.rs:200:29:200:35 | sink(...) | main.rs:198:5:201:5 | match s1 { ... } | +| main.rs:202:11:202:12 | s1 | main.rs:203:10:203:44 | ... \| ... | +| main.rs:203:10:203:44 | ... \| ... | main.rs:203:10:203:25 | C {...} | +| main.rs:203:10:203:44 | ... \| ... | main.rs:203:29:203:44 | D {...} | | main.rs:203:10:203:44 | [SSA] [match(true)] phi | main.rs:203:55:203:55 | n | | main.rs:203:23:203:23 | [SSA] [input] [match(true)] phi | main.rs:203:10:203:44 | [SSA] [match(true)] phi | | main.rs:203:23:203:23 | [SSA] n | main.rs:203:23:203:23 | [SSA] [input] [match(true)] phi | @@ -210,6 +246,8 @@ localStep | main.rs:203:42:203:42 | n | main.rs:203:42:203:42 | [SSA] n | | main.rs:203:50:203:56 | sink(...) | main.rs:202:5:204:5 | match s1 { ... } | | main.rs:205:5:208:5 | match s2 { ... } | main.rs:193:51:209:1 | { ... } | +| main.rs:205:11:205:12 | s2 | main.rs:206:9:206:24 | C {...} | +| main.rs:205:11:205:12 | s2 | main.rs:207:9:207:24 | D {...} | | main.rs:206:22:206:22 | [SSA] n | main.rs:206:34:206:34 | n | | main.rs:206:22:206:22 | n | main.rs:206:22:206:22 | [SSA] n | | main.rs:206:29:206:35 | sink(...) | main.rs:205:5:208:5 | match s2 { ... } | @@ -243,4 +281,26 @@ localStep | main.rs:231:22:231:22 | 2 | main.rs:231:9:231:22 | break ''block 2 | | main.rs:233:5:233:5 | a | main.rs:226:38:234:1 | { ... } | storeStep +| main.rs:117:19:117:28 | source(...) | Some | main.rs:117:14:117:29 | Some(...) | +| main.rs:118:19:118:19 | 2 | Some | main.rs:118:14:118:20 | Some(...) | +| main.rs:135:29:135:38 | source(...) | A | main.rs:135:14:135:39 | ...::A(...) | +| main.rs:136:29:136:29 | 2 | B | main.rs:136:14:136:30 | ...::B(...) | +| main.rs:175:18:175:27 | source(...) | C | main.rs:174:14:176:5 | ...::C {...} | +| main.rs:177:41:177:41 | 2 | D | main.rs:177:14:177:43 | ...::D {...} | +| main.rs:240:27:240:27 | 0 | Some | main.rs:240:22:240:28 | Some(...) | readStep +| main.rs:33:9:33:15 | TupleStructPat | Some | main.rs:33:14:33:14 | _ | +| main.rs:120:9:120:15 | TupleStructPat | Some | main.rs:120:14:120:14 | n | +| main.rs:124:9:124:15 | TupleStructPat | Some | main.rs:124:14:124:14 | n | +| main.rs:138:9:138:25 | TupleStructPat | A | main.rs:138:24:138:24 | n | +| main.rs:139:9:139:25 | TupleStructPat | B | main.rs:139:24:139:24 | n | +| main.rs:142:10:142:26 | TupleStructPat | A | main.rs:142:25:142:25 | n | +| main.rs:142:30:142:46 | TupleStructPat | B | main.rs:142:45:142:45 | n | +| main.rs:145:9:145:25 | TupleStructPat | A | main.rs:145:24:145:24 | n | +| main.rs:146:9:146:25 | TupleStructPat | B | main.rs:146:24:146:24 | n | +| main.rs:179:9:179:38 | ...::C {...} | C | main.rs:179:36:179:36 | n | +| main.rs:180:9:180:38 | ...::D {...} | D | main.rs:180:36:180:36 | n | +| main.rs:183:10:183:39 | ...::C {...} | C | main.rs:183:37:183:37 | n | +| main.rs:183:43:183:72 | ...::D {...} | D | main.rs:183:70:183:70 | n | +| main.rs:186:9:186:38 | ...::C {...} | C | main.rs:186:36:186:36 | n | +| main.rs:187:9:187:38 | ...::D {...} | D | main.rs:187:36:187:36 | n | diff --git a/rust/ql/test/library-tests/dataflow/local/inline-flow.expected b/rust/ql/test/library-tests/dataflow/local/inline-flow.expected index f8585809e942..bfafa38c3ff3 100644 --- a/rust/ql/test/library-tests/dataflow/local/inline-flow.expected +++ b/rust/ql/test/library-tests/dataflow/local/inline-flow.expected @@ -5,6 +5,24 @@ edges | main.rs:31:13:31:21 | source(...) | main.rs:36:10:36:10 | b | provenance | | | main.rs:45:15:45:23 | source(...) | main.rs:47:10:47:10 | b | provenance | | | main.rs:53:9:53:17 | source(...) | main.rs:54:10:54:10 | i | provenance | | +| main.rs:117:14:117:29 | Some(...) [Some] | main.rs:120:9:120:15 | TupleStructPat [Some] | provenance | | +| main.rs:117:19:117:28 | source(...) | main.rs:117:14:117:29 | Some(...) [Some] | provenance | | +| main.rs:120:9:120:15 | TupleStructPat [Some] | main.rs:120:14:120:14 | n | provenance | | +| main.rs:120:14:120:14 | n | main.rs:120:25:120:25 | n | provenance | | +| main.rs:135:14:135:39 | ...::A(...) [A] | main.rs:138:9:138:25 | TupleStructPat [A] | provenance | | +| main.rs:135:14:135:39 | ...::A(...) [A] | main.rs:142:10:142:26 | TupleStructPat [A] | provenance | | +| main.rs:135:29:135:38 | source(...) | main.rs:135:14:135:39 | ...::A(...) [A] | provenance | | +| main.rs:138:9:138:25 | TupleStructPat [A] | main.rs:138:24:138:24 | n | provenance | | +| main.rs:138:24:138:24 | n | main.rs:138:35:138:35 | n | provenance | | +| main.rs:142:10:142:26 | TupleStructPat [A] | main.rs:142:25:142:25 | n | provenance | | +| main.rs:142:25:142:25 | n | main.rs:142:57:142:57 | n | provenance | | +| main.rs:174:14:176:5 | ...::C {...} [C] | main.rs:179:9:179:38 | ...::C {...} [C] | provenance | | +| main.rs:174:14:176:5 | ...::C {...} [C] | main.rs:183:10:183:39 | ...::C {...} [C] | provenance | | +| main.rs:175:18:175:27 | source(...) | main.rs:174:14:176:5 | ...::C {...} [C] | provenance | | +| main.rs:179:9:179:38 | ...::C {...} [C] | main.rs:179:36:179:36 | n | provenance | | +| main.rs:179:36:179:36 | n | main.rs:179:48:179:48 | n | provenance | | +| main.rs:183:10:183:39 | ...::C {...} [C] | main.rs:183:37:183:37 | n | provenance | | +| main.rs:183:37:183:37 | n | main.rs:183:83:183:83 | n | provenance | | nodes | main.rs:15:10:15:18 | source(...) | semmle.label | source(...) | | main.rs:19:13:19:21 | source(...) | semmle.label | source(...) | @@ -17,6 +35,27 @@ nodes | main.rs:47:10:47:10 | b | semmle.label | b | | main.rs:53:9:53:17 | source(...) | semmle.label | source(...) | | main.rs:54:10:54:10 | i | semmle.label | i | +| main.rs:117:14:117:29 | Some(...) [Some] | semmle.label | Some(...) [Some] | +| main.rs:117:19:117:28 | source(...) | semmle.label | source(...) | +| main.rs:120:9:120:15 | TupleStructPat [Some] | semmle.label | TupleStructPat [Some] | +| main.rs:120:14:120:14 | n | semmle.label | n | +| main.rs:120:25:120:25 | n | semmle.label | n | +| main.rs:135:14:135:39 | ...::A(...) [A] | semmle.label | ...::A(...) [A] | +| main.rs:135:29:135:38 | source(...) | semmle.label | source(...) | +| main.rs:138:9:138:25 | TupleStructPat [A] | semmle.label | TupleStructPat [A] | +| main.rs:138:24:138:24 | n | semmle.label | n | +| main.rs:138:35:138:35 | n | semmle.label | n | +| main.rs:142:10:142:26 | TupleStructPat [A] | semmle.label | TupleStructPat [A] | +| main.rs:142:25:142:25 | n | semmle.label | n | +| main.rs:142:57:142:57 | n | semmle.label | n | +| main.rs:174:14:176:5 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:175:18:175:27 | source(...) | semmle.label | source(...) | +| main.rs:179:9:179:38 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:179:36:179:36 | n | semmle.label | n | +| main.rs:179:48:179:48 | n | semmle.label | n | +| main.rs:183:10:183:39 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:183:37:183:37 | n | semmle.label | n | +| main.rs:183:83:183:83 | n | semmle.label | n | subpaths testFailures #select @@ -26,3 +65,8 @@ testFailures | main.rs:36:10:36:10 | b | main.rs:31:13:31:21 | source(...) | main.rs:36:10:36:10 | b | $@ | main.rs:31:13:31:21 | source(...) | source(...) | | main.rs:47:10:47:10 | b | main.rs:45:15:45:23 | source(...) | main.rs:47:10:47:10 | b | $@ | main.rs:45:15:45:23 | source(...) | source(...) | | main.rs:54:10:54:10 | i | main.rs:53:9:53:17 | source(...) | main.rs:54:10:54:10 | i | $@ | main.rs:53:9:53:17 | source(...) | source(...) | +| main.rs:120:25:120:25 | n | main.rs:117:19:117:28 | source(...) | main.rs:120:25:120:25 | n | $@ | main.rs:117:19:117:28 | source(...) | source(...) | +| main.rs:138:35:138:35 | n | main.rs:135:29:135:38 | source(...) | main.rs:138:35:138:35 | n | $@ | main.rs:135:29:135:38 | source(...) | source(...) | +| main.rs:142:57:142:57 | n | main.rs:135:29:135:38 | source(...) | main.rs:142:57:142:57 | n | $@ | main.rs:135:29:135:38 | source(...) | source(...) | +| main.rs:179:48:179:48 | n | main.rs:175:18:175:27 | source(...) | main.rs:179:48:179:48 | n | $@ | main.rs:175:18:175:27 | source(...) | source(...) | +| main.rs:183:83:183:83 | n | main.rs:175:18:175:27 | source(...) | main.rs:183:83:183:83 | n | $@ | main.rs:175:18:175:27 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/local/main.rs b/rust/ql/test/library-tests/dataflow/local/main.rs index b9dcc78c3a80..9a9e6b467e0e 100644 --- a/rust/ql/test/library-tests/dataflow/local/main.rs +++ b/rust/ql/test/library-tests/dataflow/local/main.rs @@ -117,7 +117,7 @@ fn option_pattern_match_unqualified() { let s1 = Some(source(14)); let s2 = Some(2); match s1 { - Some(n) => sink(n), // $ MISSING: hasValueFlow=14 + Some(n) => sink(n), // $ hasValueFlow=14 None => sink(0), } match s2 { @@ -135,11 +135,11 @@ fn custom_tuple_enum_pattern_match_qualified() { let s1 = MyTupleEnum::A(source(15)); let s2 = MyTupleEnum::B(2); match s1 { - MyTupleEnum::A(n) => sink(n), // $ MISSING: hasValueFlow=15 + MyTupleEnum::A(n) => sink(n), // $ hasValueFlow=15 MyTupleEnum::B(n) => sink(n), } match s1 { - (MyTupleEnum::A(n) | MyTupleEnum::B(n)) => sink(n), // $ MISSING: hasValueFlow=15 + (MyTupleEnum::A(n) | MyTupleEnum::B(n)) => sink(n), // $ hasValueFlow=15 } match s2 { MyTupleEnum::A(n) => sink(n), @@ -176,11 +176,11 @@ fn custom_record_enum_pattern_match_qualified() { }; let s2 = MyRecordEnum::D { field_d: 2 }; match s1 { - MyRecordEnum::C { field_c: n } => sink(n), // $ MISSING: hasValueFlow=17 + MyRecordEnum::C { field_c: n } => sink(n), // $ hasValueFlow=17 MyRecordEnum::D { field_d: n } => sink(n), } match s1 { - (MyRecordEnum::C { field_c: n } | MyRecordEnum::D { field_d: n }) => sink(n), // $ MISSING: hasValueFlow=17 + (MyRecordEnum::C { field_c: n } | MyRecordEnum::D { field_d: n }) => sink(n), // $ hasValueFlow=17 } match s2 { MyRecordEnum::C { field_c: n } => sink(n),