Rust: Exclude flow summary nodes from DataFlowStep.ql

This commit is contained in:
Tom Hvitved
2025-05-21 11:04:53 +02:00
parent f093c496d5
commit 9ee0d2e6cf
4 changed files with 318 additions and 4146 deletions

View File

@@ -10,9 +10,7 @@ private import codeql.rust.elements.Locatable
private import codeql.Locations
import PathResolutionConsistency
class SourceLocatable instanceof Locatable {
string toString() { result = super.toString() }
class SourceLocatable extends Locatable {
Location getLocation() {
if super.getLocation().fromSource()
then result = super.getLocation()

View File

@@ -523,97 +523,103 @@ module RustDataFlow implements InputSig<Location> {
exists(c)
}
pragma[nomagic]
additional predicate readContentStep(Node node1, Content c, Node node2) {
exists(TupleStructPatCfgNode pat, int pos |
pat = node1.asPat() and
node2.asPat() = pat.getField(pos) and
c = TTupleFieldContent(pat.getTupleStructPat().getTupleField(pos))
)
or
exists(TuplePatCfgNode pat, int pos |
pos = c.(TuplePositionContent).getPosition() and
node1.asPat() = pat and
node2.asPat() = pat.getField(pos)
)
or
exists(StructPatCfgNode pat, string field |
pat = node1.asPat() and
c = TStructFieldContent(pat.getStructPat().getStructField(field)) and
node2.asPat() = pat.getFieldPat(field)
)
or
c instanceof ReferenceContent and
node1.asPat().(RefPatCfgNode).getPat() = node2.asPat()
or
exists(FieldExprCfgNode access |
node1.asExpr() = access.getContainer() and
node2.asExpr() = access and
access = c.(FieldContent).getAnAccess()
)
or
exists(IndexExprCfgNode arr |
c instanceof ElementContent and
node1.asExpr() = arr.getBase() and
node2.asExpr() = arr
)
or
exists(ForExprCfgNode for |
c instanceof ElementContent and
node1.asExpr() = for.getIterable() and
node2.asPat() = for.getPat()
)
or
exists(SlicePatCfgNode pat |
c instanceof ElementContent and
node1.asPat() = pat and
node2.asPat() = pat.getAPat()
)
or
exists(TryExprCfgNode try |
node1.asExpr() = try.getExpr() and
node2.asExpr() = try and
c.(TupleFieldContent)
.isVariantField([any(OptionEnum o).getSome(), any(ResultEnum r).getOk()], 0)
)
or
exists(PrefixExprCfgNode deref |
c instanceof ReferenceContent and
deref.getOperatorName() = "*" and
node1.asExpr() = deref.getExpr() and
node2.asExpr() = deref
)
or
// Read from function return
exists(DataFlowCall call |
lambdaCall(call, _, node1) and
call = node2.(OutNode).getCall(TNormalReturnKind()) and
c instanceof FunctionCallReturnContent
)
or
exists(AwaitExprCfgNode await |
c instanceof FutureContent and
node1.asExpr() = await.getExpr() and
node2.asExpr() = await
)
or
referenceExprToExpr(node2.(PostUpdateNode).getPreUpdateNode(),
node1.(PostUpdateNode).getPreUpdateNode(), c)
or
// Step from receiver expression to receiver node, in case of an implicit
// dereference.
implicitDerefToReceiver(node1, node2, c)
or
// A read step dual to the store step for implicit borrows.
implicitBorrowToReceiver(node2.(PostUpdateNode).getPreUpdateNode(),
node1.(PostUpdateNode).getPreUpdateNode(), c)
or
VariableCapture::readStep(node1, c, node2)
}
/**
* 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 cs, Node node2) {
exists(Content c | c = cs.(SingletonContentSet).getContent() |
exists(TupleStructPatCfgNode pat, int pos |
pat = node1.asPat() and
node2.asPat() = pat.getField(pos) and
c = TTupleFieldContent(pat.getTupleStructPat().getTupleField(pos))
)
or
exists(TuplePatCfgNode pat, int pos |
pos = c.(TuplePositionContent).getPosition() and
node1.asPat() = pat and
node2.asPat() = pat.getField(pos)
)
or
exists(StructPatCfgNode pat, string field |
pat = node1.asPat() and
c = TStructFieldContent(pat.getStructPat().getStructField(field)) and
node2.asPat() = pat.getFieldPat(field)
)
or
c instanceof ReferenceContent and
node1.asPat().(RefPatCfgNode).getPat() = node2.asPat()
or
exists(FieldExprCfgNode access |
node1.asExpr() = access.getContainer() and
node2.asExpr() = access and
access = c.(FieldContent).getAnAccess()
)
or
exists(IndexExprCfgNode arr |
c instanceof ElementContent and
node1.asExpr() = arr.getBase() and
node2.asExpr() = arr
)
or
exists(ForExprCfgNode for |
c instanceof ElementContent and
node1.asExpr() = for.getIterable() and
node2.asPat() = for.getPat()
)
or
exists(SlicePatCfgNode pat |
c instanceof ElementContent and
node1.asPat() = pat and
node2.asPat() = pat.getAPat()
)
or
exists(TryExprCfgNode try |
node1.asExpr() = try.getExpr() and
node2.asExpr() = try and
c.(TupleFieldContent)
.isVariantField([any(OptionEnum o).getSome(), any(ResultEnum r).getOk()], 0)
)
or
exists(PrefixExprCfgNode deref |
c instanceof ReferenceContent and
deref.getOperatorName() = "*" and
node1.asExpr() = deref.getExpr() and
node2.asExpr() = deref
)
or
// Read from function return
exists(DataFlowCall call |
lambdaCall(call, _, node1) and
call = node2.(OutNode).getCall(TNormalReturnKind()) and
c instanceof FunctionCallReturnContent
)
or
exists(AwaitExprCfgNode await |
c instanceof FutureContent and
node1.asExpr() = await.getExpr() and
node2.asExpr() = await
)
or
referenceExprToExpr(node2.(PostUpdateNode).getPreUpdateNode(),
node1.(PostUpdateNode).getPreUpdateNode(), c)
or
// Step from receiver expression to receiver node, in case of an implicit
// dereference.
implicitDerefToReceiver(node1, node2, c)
or
// A read step dual to the store step for implicit borrows.
implicitBorrowToReceiver(node2.(PostUpdateNode).getPreUpdateNode(),
node1.(PostUpdateNode).getPreUpdateNode(), c)
or
VariableCapture::readStep(node1, c, node2)
exists(Content c |
c = cs.(SingletonContentSet).getContent() and
readContentStep(node1, c, node2)
)
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), cs,
@@ -652,7 +658,7 @@ module RustDataFlow implements InputSig<Location> {
}
pragma[nomagic]
private predicate storeContentStep(Node node1, Content c, Node node2) {
additional 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 and

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
import codeql.rust.dataflow.DataFlow
import codeql.rust.dataflow.internal.DataFlowImpl
import codeql.rust.dataflow.internal.Node
import utils.test.TranslateModels
query predicate localStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
@@ -7,6 +8,27 @@ query predicate localStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
RustDataFlow::simpleLocalFlowStep(nodeFrom, nodeTo, "")
}
query predicate storeStep = RustDataFlow::storeStep/3;
class Content extends DataFlow::Content {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(string file |
this.getLocation().hasLocationInfo(file, startline, startcolumn, endline, endcolumn) and
filepath =
file.regexpReplaceAll("^/.*/tools/builtins/", "/BUILTINS/")
.regexpReplaceAll("^/.*/.rustup/toolchains/[^/]+/", "/RUSTUP_HOME/toolchain/")
)
}
}
query predicate readStep = RustDataFlow::readStep/3;
class Node extends DataFlow::Node {
Node() { not this instanceof FlowSummaryNode }
}
query predicate storeStep(Node node1, Content c, Node node2) {
RustDataFlow::storeContentStep(node1, c, node2)
}
query predicate readStep(Node node1, Content c, Node node2) {
RustDataFlow::readContentStep(node1, c, node2)
}