Skip to content

Commit 042bc16

Browse files
committed
Rust: Model implicit derefs in data flow
1 parent f99ee9c commit 042bc16

27 files changed

+733
-576
lines changed

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

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ private import codeql.rust.internal.PathResolution
1515
private import codeql.rust.controlflow.ControlFlowGraph
1616
private import codeql.rust.dataflow.Ssa
1717
private import codeql.rust.dataflow.FlowSummary
18+
private import codeql.rust.internal.TypeInference as TypeInference
19+
private import codeql.rust.internal.typeinference.DerefChain
1820
private import Node
1921
private import Content
2022
private import FlowSummaryImpl as FlowSummaryImpl
@@ -47,7 +49,7 @@ final class DataFlowCallable extends TDataFlowCallable {
4749

4850
/** Gets a textual representation of this callable. */
4951
string toString() {
50-
result = [this.asCfgScope().toString(), this.asSummarizedCallable().toString()]
52+
result = [this.asCfgScope().toString(), "[summarized] " + this.asSummarizedCallable()]
5153
}
5254

5355
/** Gets the location of this callable. */
@@ -60,6 +62,10 @@ final class DataFlowCall extends TDataFlowCall {
6062
/** Gets the underlying call, if any. */
6163
Call asCall() { this = TCall(result) }
6264

65+
predicate isImplicitDeref(AstNode n, DerefChain derefChain, int i, Function target) {
66+
this = TImplicitDerefCall(n, derefChain, i, target)
67+
}
68+
6369
predicate isSummaryCall(
6470
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
6571
) {
@@ -69,12 +75,19 @@ final class DataFlowCall extends TDataFlowCall {
6975
DataFlowCallable getEnclosingCallable() {
7076
result.asCfgScope() = this.asCall().getEnclosingCfgScope()
7177
or
78+
result.asCfgScope() = any(AstNode n | this.isImplicitDeref(n, _, _, _)).getEnclosingCfgScope()
79+
or
7280
this.isSummaryCall(result.asSummarizedCallable(), _)
7381
}
7482

7583
string toString() {
7684
result = this.asCall().toString()
7785
or
86+
exists(AstNode n, DerefChain derefChain, int i, Function target |
87+
this.isImplicitDeref(n, derefChain, i, target) and
88+
result = "[implicit deref call " + i + " in " + derefChain.toString() + "] " + n
89+
)
90+
or
7891
exists(
7992
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
8093
|
@@ -83,7 +96,11 @@ final class DataFlowCall extends TDataFlowCall {
8396
)
8497
}
8598

86-
Location getLocation() { result = this.asCall().getLocation() }
99+
Location getLocation() {
100+
result = this.asCall().getLocation()
101+
or
102+
result = any(AstNode n | this.isImplicitDeref(n, _, _, _)).getLocation()
103+
}
87104
}
88105

89106
/**
@@ -383,7 +400,8 @@ module RustDataFlow implements InputSig<Location> {
383400
node.(FlowSummaryNode).getSummaryNode().isHidden() or
384401
node instanceof CaptureNode or
385402
node instanceof ClosureParameterNode or
386-
node instanceof DerefBorrowNode or
403+
node instanceof ImplicitDerefNode or
404+
node instanceof ImplicitBorrowNode or
387405
node instanceof DerefOutNode or
388406
node instanceof IndexOutNode or
389407
node.asExpr() instanceof ParenExpr or
@@ -443,25 +461,13 @@ module RustDataFlow implements InputSig<Location> {
443461
exists(Call c | c = call.asCall() |
444462
result.asCfgScope() = c.getARuntimeTarget()
445463
or
446-
exists(SummarizedCallable sc, Function staticTarget |
447-
staticTarget = getStaticTargetExt(c) and
448-
sc = result.asSummarizedCallable() and
449-
// Only use summarized callables with generated summaries in case
450-
// the static call target is not in the source code.
451-
// Note that if `applyGeneratedModel` holds it implies that there doesn't
452-
// exist a manual model.
453-
not (
454-
staticTarget.fromSource() and
455-
sc.applyGeneratedModel()
456-
)
457-
|
458-
sc = staticTarget
459-
or
460-
// only apply trait models to concrete implementations when they are not
461-
// defined in source code
462-
staticTarget.implements(sc) and
463-
not staticTarget.fromSource()
464-
)
464+
result.asSummarizedCallable() = getStaticTargetExt(c)
465+
)
466+
or
467+
exists(Function f | call = TImplicitDerefCall(_, _, _, f) |
468+
result.asCfgScope() = f
469+
or
470+
result.asSummarizedCallable() = f
465471
)
466472
}
467473

@@ -542,16 +548,18 @@ module RustDataFlow implements InputSig<Location> {
542548
}
543549

544550
pragma[nomagic]
545-
private predicate implicitDeref(Node node1, DerefBorrowNode node2, ReferenceContent c) {
546-
not node2.isBorrow() and
547-
node1.asExpr() = node2.getNode() and
551+
private predicate implicitDeref(ImplicitDerefNode node1, Node node2, ReferenceContent c) {
552+
node2 = node1.getDerefOutputNode() and
548553
exists(c)
549554
}
550555

551556
pragma[nomagic]
552-
private predicate implicitBorrow(Node node1, DerefBorrowNode node2, ReferenceContent c) {
553-
node2.isBorrow() and
554-
node1.asExpr() = node2.getNode() and
557+
private predicate implicitBorrow(Node node1, Node node2, ReferenceContent c) {
558+
(
559+
node1 = node2.(ImplicitDerefNode).getBorrowInputNode()
560+
or
561+
node1 = node2.(ImplicitBorrowNode).getBorrowInputNode()
562+
) and
555563
exists(c)
556564
}
557565

@@ -563,10 +571,12 @@ module RustDataFlow implements InputSig<Location> {
563571

564572
private Node getFieldExprContainerNode(FieldExpr fe) {
565573
exists(Expr container | container = fe.getContainer() |
566-
not any(DerefBorrowNode n).getNode() = container and
574+
not TypeInference::implicitDerefChainBorrow(container, _, _) and
567575
result.asExpr() = container
568576
or
569-
result.(DerefBorrowNode).getNode() = container
577+
result.(ImplicitBorrowNode).getNode() = container
578+
or
579+
result.(ImplicitDerefNode).isLast(container)
570580
)
571581
}
572582

@@ -1055,6 +1065,10 @@ private module Cached {
10551065
Stages::DataFlowStage::ref() and
10561066
call.hasEnclosingCfgScope()
10571067
} or
1068+
TImplicitDerefCall(AstNode n, DerefChain derefChain, int i, Function target) {
1069+
TypeInference::implicitDerefChainBorrow(n, derefChain, _) and
1070+
target = derefChain.getElement(i).getDerefFunction()
1071+
} or
10581072
TSummaryCall(
10591073
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
10601074
) {

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

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,25 +111,56 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
111111
)
112112
}
113113

114-
private class SummarizedCallableFromModel extends SummarizedCallable::Range {
115-
private string path;
114+
private predicate summaryModel(
115+
Function f, string input, string output, string kind, Provenance provenance, boolean isExact,
116+
QlBuiltins::ExtensionId madId
117+
) {
118+
exists(string path, Function f0 |
119+
summaryModel(path, input, output, kind, provenance, madId) and
120+
f0.getCanonicalPath() = path
121+
|
122+
f = f0 and
123+
isExact = true
124+
or
125+
f.implements(f0) and
126+
isExact = false
127+
)
128+
}
116129

117-
SummarizedCallableFromModel() {
118-
summaryModel(path, _, _, _, _, _) and
119-
this.getCanonicalPath() = path
120-
}
130+
private predicate summaryModelRelevant(
131+
Function f, string input, string output, string kind, Provenance provenance,
132+
QlBuiltins::ExtensionId madId
133+
) {
134+
exists(boolean isExact | summaryModel(f, input, output, kind, provenance, isExact, madId) |
135+
(
136+
provenance.isManual()
137+
or
138+
provenance.isGenerated() and
139+
not any(Provenance manual | summaryModel(f, _, _, _, manual, _, _)).isManual() and
140+
not f.fromSource()
141+
) and
142+
(
143+
isExact = true
144+
or
145+
isExact = false and
146+
not summaryModel(f, _, _, _, provenance, true, _) and
147+
not f.fromSource()
148+
)
149+
)
150+
}
151+
152+
private class SummarizedCallableFromModel extends SummarizedCallable::Range {
153+
SummarizedCallableFromModel() { summaryModelRelevant(this, _, _, _, _, _) }
121154

122155
override predicate hasProvenance(Provenance provenance) {
123-
summaryModel(path, _, _, _, provenance, _)
156+
summaryModelRelevant(this, _, _, _, provenance, _)
124157
}
125158

126-
private predicate hasManualModel() { summaryModel(path, _, _, _, "manual", _) }
127-
128159
override predicate propagatesFlow(
129160
string input, string output, boolean preservesValue, string model
130161
) {
131162
exists(string kind, string provenance, QlBuiltins::ExtensionId madId |
132-
summaryModel(path, input, output, kind, provenance, madId) and
163+
summaryModelRelevant(this, input, output, kind, provenance, madId) and
133164
model = "MaD:" + madId.toString() and
134165
(provenance = "manual" or not this.hasManualModel())
135166
|

0 commit comments

Comments
 (0)