Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rust/ql/integration-tests/hello-project/summary.expected
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
| Macro calls - resolved | 2 |
| Macro calls - total | 2 |
| Macro calls - unresolved | 0 |
| Taint edges - number of edges | 1674 |
| Taint edges - number of edges | 1691 |
| Taint reach - nodes tainted | 0 |
| Taint reach - per million nodes | 0 |
| Taint sinks - cryptographic operations | 0 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
| Macro calls - resolved | 2 |
| Macro calls - total | 2 |
| Macro calls - unresolved | 0 |
| Taint edges - number of edges | 1674 |
| Taint edges - number of edges | 1691 |
| Taint reach - nodes tainted | 0 |
| Taint reach - per million nodes | 0 |
| Taint sinks - cryptographic operations | 0 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
| Macro calls - resolved | 2 |
| Macro calls - total | 2 |
| Macro calls - unresolved | 0 |
| Taint edges - number of edges | 1674 |
| Taint edges - number of edges | 1691 |
| Taint reach - nodes tainted | 0 |
| Taint reach - per million nodes | 0 |
| Taint sinks - cryptographic operations | 0 |
Expand Down
2 changes: 0 additions & 2 deletions rust/ql/lib/codeql/rust/dataflow/internal/Content.qll
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,6 @@ cached
newtype TContent =
TTupleFieldContent(TupleField field) { Stages::DataFlowStage::ref() } or
TStructFieldContent(StructField field) or
// TODO: Remove once library types are extracted
TVariantInLibTupleFieldContent(VariantInLib::VariantInLib v, int pos) { pos = v.getAPosition() } or
TElementContent() or
TFutureContent() or
TTuplePositionContent(int pos) {
Expand Down
134 changes: 5 additions & 129 deletions rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -296,125 +296,6 @@ module LocalFlow {
}
}

/**
* Provides temporary modeling of built-in variants, for which no source code
* `Item`s are available.
*
* TODO: Remove once library code is extracted.
*/
module VariantInLib {
private import codeql.util.Option

private class CrateOrigin extends string {
CrateOrigin() { this = any(Resolvable r).getResolvedCrateOrigin() }
}

private class CrateOriginOption = Option<CrateOrigin>::Option;

private CrateOriginOption langCoreCrate() { result.asSome() = "lang:core" }

private newtype TVariantInLib =
MkVariantInLib(CrateOriginOption crate, string path, string name) {
crate = langCoreCrate() and
(
path = "crate::option::Option" and
name = "Some"
or
path = "crate::result::Result" and
name = ["Ok", "Err"]
)
}

/** An enum variant from library code, represented by the enum's canonical path and the variant's name. */
class VariantInLib extends MkVariantInLib {
CrateOriginOption crate;
string path;
string name;

VariantInLib() { this = MkVariantInLib(crate, path, name) }

int getAPosition() {
this = MkVariantInLib(langCoreCrate(), "crate::option::Option", "Some") and
result = 0
or
this = MkVariantInLib(langCoreCrate(), "crate::result::Result", ["Ok", "Err"]) and
result = 0
}

string getExtendedCanonicalPath() { result = path + "::" + name }

string toString() { result = name }
}

/** A tuple variant from library code. */
class VariantInLibTupleFieldContent extends Content, TVariantInLibTupleFieldContent {
private VariantInLib::VariantInLib v;
private int pos_;

VariantInLibTupleFieldContent() { this = TVariantInLibTupleFieldContent(v, pos_) }

VariantInLib::VariantInLib getVariantInLib(int pos) { result = v and pos = pos_ }

string getExtendedCanonicalPath() { result = v.getExtendedCanonicalPath() }

int getPosition() { result = pos_ }

final override string toString() {
// only print indices when the arity is > 1
if exists(TVariantInLibTupleFieldContent(v, 1))
then result = v.toString() + "(" + pos_ + ")"
else result = v.toString()
}

final override Location getLocation() { result instanceof EmptyLocation }
}

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()
)
}

/** Holds if path `p` resolves to variant `v`. */
private predicate pathResolveToVariantInLib(PathAstNode p, VariantInLib v) {
exists(CrateOriginOption crate, string path, string name |
resolveExtendedCanonicalPath(p, pragma[only_bind_into](crate), path + "::" + name) and
v = MkVariantInLib(pragma[only_bind_into](crate), path, name)
)
}

/** Holds if `p` destructs an enum variant `v`. */
pragma[nomagic]
private predicate tupleVariantCanonicalDestruction(TupleStructPat p, VariantInLib v) {
pathResolveToVariantInLib(p, v)
}

bindingset[pos]
predicate tupleVariantCanonicalDestruction(
TupleStructPat pat, VariantInLibTupleFieldContent c, int pos
) {
tupleVariantCanonicalDestruction(pat, c.getVariantInLib(pos))
}

/** Holds if `ce` constructs an enum value of type `v`. */
pragma[nomagic]
private predicate tupleVariantCanonicalConstruction(CallExpr ce, VariantInLib v) {
pathResolveToVariantInLib(ce.getFunction().(PathExpr), v)
}

bindingset[pos]
predicate tupleVariantCanonicalConstruction(CallExpr ce, VariantInLibTupleFieldContent c, int pos) {
tupleVariantCanonicalConstruction(ce, c.getVariantInLib(pos))
}
}

class VariantInLibTupleFieldContent = VariantInLib::VariantInLibTupleFieldContent;

class LambdaCallKind = Unit;

/** Holds if `creation` is an expression that creates a lambda of kind `kind`. */
Expand Down Expand Up @@ -480,6 +361,7 @@ module RustDataFlow implements InputSig<Location> {
private import Aliases
private import codeql.rust.dataflow.DataFlow
private import Node as Node
private import codeql.rust.frameworks.stdlib.Stdlib

/**
* An element, viewed as a node in a data flow graph. Either an expression
Expand Down Expand Up @@ -665,11 +547,8 @@ module RustDataFlow implements InputSig<Location> {
exists(Content c | c = cs.(SingletonContentSet).getContent() |
exists(TupleStructPatCfgNode pat, int pos |
pat = node1.asPat() and
node2.asPat() = pat.getField(pos)
|
node2.asPat() = pat.getField(pos) and
c = TTupleFieldContent(pat.getTupleStructPat().getTupleField(pos))
or
VariantInLib::tupleVariantCanonicalDestruction(pat.getPat(), c, pos)
)
or
exists(TuplePatCfgNode pat, int pos |
Expand Down Expand Up @@ -714,8 +593,8 @@ module RustDataFlow implements InputSig<Location> {
exists(TryExprCfgNode try |
node1.asExpr() = try.getExpr() and
node2.asExpr() = try and
c.(VariantInLibTupleFieldContent).getVariantInLib(0).getExtendedCanonicalPath() =
["crate::option::Option::Some", "crate::result::Result::Ok"]
c.(TupleFieldContent)
.isVariantField([any(OptionEnum o).getSome(), any(ResultEnum r).getOk()], 0)
)
or
exists(PrefixExprCfgNode deref |
Expand Down Expand Up @@ -791,11 +670,8 @@ module RustDataFlow implements InputSig<Location> {
private predicate storeContentStep(Node node1, Content c, Node node2) {
exists(CallExprCfgNode call, int pos |
node1.asExpr() = call.getArgument(pragma[only_bind_into](pos)) and
node2.asExpr() = call
|
node2.asExpr() = call and
c = TTupleFieldContent(call.getCallExpr().getTupleField(pragma[only_bind_into](pos)))
or
VariantInLib::tupleVariantCanonicalConstruction(call.getCallExpr(), c, pos)
)
or
exists(StructExprCfgNode re, string field |
Expand Down
21 changes: 14 additions & 7 deletions rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ private import Content

module Input implements InputSig<Location, RustDataFlow> {
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
private import codeql.rust.frameworks.stdlib.Stdlib

class SummarizedCallableBase = string;

Expand Down Expand Up @@ -66,9 +67,20 @@ module Input implements InputSig<Location, RustDataFlow> {
exists(Content c | cs = TSingletonContentSet(c) |
result = "Field" and
(
exists(Addressable a, int pos |
exists(Addressable a, int pos, string prefix |
// TODO: calculate in QL
arg = a.getExtendedCanonicalPath() + "(" + pos + ")"
arg = prefix + "(" + pos + ")" and
(
prefix = a.getExtendedCanonicalPath()
or
a = any(OptionEnum o).getSome() and
prefix = "crate::option::Option::Some"
or
exists(string name |
a = any(ResultEnum r).getVariant(name) and
prefix = "crate::result::Result::" + name
)
)
|
c.(TupleFieldContent).isStructField(a, pos)
or
Expand All @@ -84,11 +96,6 @@ module Input implements InputSig<Location, RustDataFlow> {
c.(StructFieldContent).isVariantField(a, field)
)
or
c =
any(VariantInLibTupleFieldContent v |
arg = v.getExtendedCanonicalPath() + "(" + v.getPosition() + ")"
)
or
exists(int pos |
c = TTuplePositionContent(pos) and
arg = pos.toString()
Expand Down
9 changes: 9 additions & 0 deletions rust/ql/lib/codeql/rust/elements/internal/EnumImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ private import codeql.rust.elements.internal.generated.Enum
* be referenced directly.
*/
module Impl {
private import rust

// the following QLdoc is generated: if you need to edit it, do it in the schema file
/**
* A Enum. For example:
Expand All @@ -20,5 +22,12 @@ module Impl {
*/
class Enum extends Generated::Enum {
override string toStringImpl() { result = "enum " + this.getName().getText() }

/** Gets the variant named `name`, if any. */
pragma[nomagic]
Variant getVariant(string name) {
result = this.getVariantList().getAVariant() and
result.getName().getText() = name
}
}
}
12 changes: 12 additions & 0 deletions rust/ql/lib/codeql/rust/elements/internal/PathImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ module Impl {
*/
pragma[nomagic]
string getText() { result = this.getSegment().getIdentifier().getText() }

/**
* Gets the full text of this path, including the qualifier.
*
* Should only be used for debugging purposes.
*/
string toStringDebug() {
not this.hasQualifier() and
result = this.getText()
or
result = this.getQualifier().toStringDebug() + "::" + this.getText()
}
}

/** A simple identifier path. */
Expand Down
45 changes: 45 additions & 0 deletions rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,48 @@ private class StartswithCall extends Path::SafeAccessCheck::Range, CfgNodes::Met
branch = true
}
}

/**
* The [`Option` enum][1].
*
* [1]: https://doc.rust-lang.org/std/option/enum.Option.html
*/
class OptionEnum extends Enum {
OptionEnum() {
// todo: replace with canonical path, once calculated in QL
exists(Crate core, Module m |
core.getName() = "core" and
m = core.getModule().getItemList().getAnItem() and
m.getName().getText() = "option" and
this = m.getItemList().getAnItem() and
this.getName().getText() = "Option"
)
}

/** Gets the `Some` variant. */
Variant getSome() { result = this.getVariant("Some") }
}

/**
* The [`Result` enum][1].
*
* [1]: https://doc.rust-lang.org/stable/std/result/enum.Result.html
*/
class ResultEnum extends Enum {
ResultEnum() {
// todo: replace with canonical path, once calculated in QL
exists(Crate core, Module m |
core.getName() = "core" and
m = core.getModule().getItemList().getAnItem() and
m.getName().getText() = "result" and
this = m.getItemList().getAnItem() and
this.getName().getText() = "Result"
)
}

/** Gets the `Ok` variant. */
Variant getOk() { result = this.getVariant("Ok") }

/** Gets the `Err` variant. */
Variant getErr() { result = this.getVariant("Err") }
}
35 changes: 33 additions & 2 deletions rust/ql/lib/codeql/rust/internal/CachedStages.qll
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,48 @@ module Stages {
}
}

/**
* The path resolution stage.
*/
cached
module PathResolutionStage {
private import codeql.rust.internal.PathResolution

/**
* Always holds.
* Ensures that a predicate is evaluated as part of the path resolution stage.
*/
cached
predicate ref() { 1 = 1 }

/**
* DO NOT USE!
*
* Contains references to each predicate that use the above `ref` predicate.
*/
cached
predicate backref() {
1 = 1
or
exists(resolvePath(_))
or
exists(any(ItemNode i).getASuccessor(_))
or
exists(any(ItemNode i).getASuccessorRec(_))
}
}

/**
* The type inference stage.
*/
cached
module TypeInference {
module TypeInferenceStage {
private import codeql.rust.internal.Type
private import codeql.rust.internal.TypeInference

/**
* Always holds.
* Ensures that a predicate is evaluated as part of the CFG stage.
* Ensures that a predicate is evaluated as part of the type inference stage.
*/
cached
predicate ref() { 1 = 1 }
Expand Down
Loading