Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
74 changes: 44 additions & 30 deletions rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
private import codeql.rust.controlflow.ControlFlowGraph
private import codeql.rust.dataflow.Ssa
private import codeql.rust.dataflow.FlowSummary
private import codeql.rust.internal.TypeInference as TypeInference
private import codeql.rust.internal.typeinference.DerefChain
private import Node
private import Content
private import FlowSummaryImpl as FlowSummaryImpl
Expand Down Expand Up @@ -47,7 +49,7 @@

/** Gets a textual representation of this callable. */
string toString() {
result = [this.asCfgScope().toString(), this.asSummarizedCallable().toString()]
result = [this.asCfgScope().toString(), "[summarized] " + this.asSummarizedCallable()]
}

/** Gets the location of this callable. */
Expand All @@ -60,6 +62,10 @@
/** Gets the underlying call, if any. */
Call asCall() { this = TCall(result) }

predicate isImplicitDeref(AstNode n, DerefChain derefChain, int i, Function target) {
this = TImplicitDerefCall(n, derefChain, i, target)
}

predicate isSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {
Expand All @@ -69,12 +75,19 @@
DataFlowCallable getEnclosingCallable() {
result.asCfgScope() = this.asCall().getEnclosingCfgScope()
or
result.asCfgScope() = any(AstNode n | this.isImplicitDeref(n, _, _, _)).getEnclosingCfgScope()
or
this.isSummaryCall(result.asSummarizedCallable(), _)
}

string toString() {
result = this.asCall().toString()
or
exists(AstNode n, DerefChain derefChain, int i, Function target |

Check warning

Code scanning / CodeQL

Omittable 'exists' variable Warning

This exists variable can be omitted by using a don't-care expression
in this argument
.
this.isImplicitDeref(n, derefChain, i, target) and
result = "[implicit deref call " + i + " in " + derefChain.toString() + "] " + n
)
or
exists(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
Expand All @@ -83,7 +96,11 @@
)
}

Location getLocation() { result = this.asCall().getLocation() }
Location getLocation() {
result = this.asCall().getLocation()
or
result = any(AstNode n | this.isImplicitDeref(n, _, _, _)).getLocation()
}
}

/**
Expand Down Expand Up @@ -383,7 +400,8 @@
node.(FlowSummaryNode).getSummaryNode().isHidden() or
node instanceof CaptureNode or
node instanceof ClosureParameterNode or
node instanceof DerefBorrowNode or
node instanceof ImplicitDerefNode or
node instanceof ImplicitBorrowNode or
node instanceof DerefOutNode or
node instanceof IndexOutNode or
node.asExpr() instanceof ParenExpr or
Expand Down Expand Up @@ -443,25 +461,13 @@
exists(Call c | c = call.asCall() |
result.asCfgScope() = c.getARuntimeTarget()
or
exists(SummarizedCallable sc, Function staticTarget |
staticTarget = getStaticTargetExt(c) and
sc = result.asSummarizedCallable() and
// Only use summarized callables with generated summaries in case
// the static call target is not in the source code.
// Note that if `applyGeneratedModel` holds it implies that there doesn't
// exist a manual model.
not (
staticTarget.fromSource() and
sc.applyGeneratedModel()
)
|
sc = staticTarget
or
// only apply trait models to concrete implementations when they are not
// defined in source code
staticTarget.implements(sc) and
not staticTarget.fromSource()
)
result.asSummarizedCallable() = getStaticTargetExt(c)
)
or
exists(Function f | call = TImplicitDerefCall(_, _, _, f) |
result.asCfgScope() = f
or
result.asSummarizedCallable() = f
)
}

Expand Down Expand Up @@ -542,16 +548,18 @@
}

pragma[nomagic]
private predicate implicitDeref(Node node1, DerefBorrowNode node2, ReferenceContent c) {
not node2.isBorrow() and
node1.asExpr() = node2.getNode() and
private predicate implicitDeref(ImplicitDerefNode node1, Node node2, ReferenceContent c) {
node2 = node1.getDerefOutputNode() and
exists(c)
}

pragma[nomagic]
private predicate implicitBorrow(Node node1, DerefBorrowNode node2, ReferenceContent c) {
node2.isBorrow() and
node1.asExpr() = node2.getNode() and
private predicate implicitBorrow(Node node1, Node node2, ReferenceContent c) {
(
node1 = node2.(ImplicitDerefNode).getBorrowInputNode()
or
node1 = node2.(ImplicitBorrowNode).getBorrowInputNode()
) and
exists(c)
}

Expand All @@ -563,10 +571,12 @@

private Node getFieldExprContainerNode(FieldExpr fe) {
exists(Expr container | container = fe.getContainer() |
not any(DerefBorrowNode n).getNode() = container and
not TypeInference::implicitDerefChainBorrow(container, _, _) and
result.asExpr() = container
or
result.(DerefBorrowNode).getNode() = container
result.(ImplicitBorrowNode).getNode() = container
or
result.(ImplicitDerefNode).isLast(container)
)
}

Expand Down Expand Up @@ -1055,6 +1065,10 @@
Stages::DataFlowStage::ref() and
call.hasEnclosingCfgScope()
} or
TImplicitDerefCall(AstNode n, DerefChain derefChain, int i, Function target) {
TypeInference::implicitDerefChainBorrow(n, derefChain, _) and
target = derefChain.getElement(i).getDerefFunction()
} or
TSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {
Expand Down
60 changes: 47 additions & 13 deletions rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll
Original file line number Diff line number Diff line change
Expand Up @@ -111,27 +111,61 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
)
}

private class SummarizedCallableFromModel extends SummarizedCallable::Range {
private string path;
private predicate summaryModel(
Function f, string input, string output, string kind, Provenance provenance, boolean isExact,
QlBuiltins::ExtensionId madId
) {
exists(string path, Function f0 |
summaryModel(path, input, output, kind, provenance, madId) and
f0.getCanonicalPath() = path
|
f = f0 and
isExact = true
or
f.implements(f0) and
isExact = false
)
}

SummarizedCallableFromModel() {
summaryModel(path, _, _, _, _, _) and
this.getCanonicalPath() = path
}
private predicate summaryModelRelevant(
Function f, string input, string output, string kind, Provenance provenance,
QlBuiltins::ExtensionId madId
) {
exists(boolean isExact | summaryModel(f, input, output, kind, provenance, isExact, madId) |
(
provenance.isManual()
or
// only apply generated models to functions not defined in source code, and
// when there are no exact manual models for the functions
provenance.isGenerated() and
not any(Provenance manual | summaryModel(f, _, _, _, manual, true, _)).isManual() and
not f.fromSource()
) and
(
isExact = true
or
// only apply trait models to concrete implementations when they are not
// defined in source code, and when there are no exact model for the functions
isExact = false and
not summaryModel(f, _, _, _, provenance, true, _) and
not f.fromSource()
)
)
}

private class SummarizedCallableFromModel extends SummarizedCallable::Range {
SummarizedCallableFromModel() { summaryModelRelevant(this, _, _, _, _, _) }

override predicate hasProvenance(Provenance provenance) {
summaryModel(path, _, _, _, provenance, _)
summaryModelRelevant(this, _, _, _, provenance, _)
}

private predicate hasManualModel() { summaryModel(path, _, _, _, "manual", _) }

override predicate propagatesFlow(
string input, string output, boolean preservesValue, string model
) {
exists(string kind, string provenance, QlBuiltins::ExtensionId madId |
summaryModel(path, input, output, kind, provenance, madId) and
model = "MaD:" + madId.toString() and
(provenance = "manual" or not this.hasManualModel())
exists(string kind, QlBuiltins::ExtensionId madId |
summaryModelRelevant(this, input, output, kind, _, madId) and
model = "MaD:" + madId.toString()
|
kind = "value" and
preservesValue = true
Expand Down
Loading
Loading