Skip to content

Commit f3bad27

Browse files
committed
Rust: Model implicit Deref trait calls in data flow
1 parent 864a4b3 commit f3bad27

File tree

22 files changed

+733
-558
lines changed

22 files changed

+733
-558
lines changed

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

Lines changed: 42 additions & 10 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
@@ -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
@@ -445,6 +463,12 @@ module RustDataFlow implements InputSig<Location> {
445463
or
446464
result.asSummarizedCallable() = getStaticTargetExt(c)
447465
)
466+
or
467+
exists(Function f | call = TImplicitDerefCall(_, _, _, f) |
468+
result.asCfgScope() = f
469+
or
470+
result.asSummarizedCallable() = f
471+
)
448472
}
449473

450474
/**
@@ -524,16 +548,18 @@ module RustDataFlow implements InputSig<Location> {
524548
}
525549

526550
pragma[nomagic]
527-
private predicate implicitDeref(Node node1, DerefBorrowNode node2, ReferenceContent c) {
528-
not node2.isBorrow() and
529-
node1.asExpr() = node2.getNode() and
551+
private predicate implicitDeref(ImplicitDerefNode node1, Node node2, ReferenceContent c) {
552+
node2 = node1.getDerefOutputNode() and
530553
exists(c)
531554
}
532555

533556
pragma[nomagic]
534-
private predicate implicitBorrow(Node node1, DerefBorrowNode node2, ReferenceContent c) {
535-
node2.isBorrow() and
536-
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
537563
exists(c)
538564
}
539565

@@ -545,10 +571,12 @@ module RustDataFlow implements InputSig<Location> {
545571

546572
private Node getFieldExprContainerNode(FieldExpr fe) {
547573
exists(Expr container | container = fe.getContainer() |
548-
not any(DerefBorrowNode n).getNode() = container and
574+
not TypeInference::implicitDerefChainBorrow(container, _, _) and
549575
result.asExpr() = container
550576
or
551-
result.(DerefBorrowNode).getNode() = container
577+
result.(ImplicitBorrowNode).getNode() = container
578+
or
579+
result.(ImplicitDerefNode).isLast(container)
552580
)
553581
}
554582

@@ -1037,6 +1065,10 @@ private module Cached {
10371065
Stages::DataFlowStage::ref() and
10381066
call.hasEnclosingCfgScope()
10391067
} 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
10401072
TSummaryCall(
10411073
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
10421074
) {

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

Lines changed: 155 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ private import codeql.rust.controlflow.CfgNodes
1515
private import codeql.rust.dataflow.Ssa
1616
private import codeql.rust.dataflow.FlowSummary
1717
private import codeql.rust.internal.TypeInference as TypeInference
18+
private import codeql.rust.internal.typeinference.DerefChain
1819
private import Node as Node
1920
private import DataFlowImpl
2021
private import FlowSummaryImpl as FlowSummaryImpl
@@ -229,47 +230,152 @@ final class ExprArgumentNode extends ArgumentNode, ExprNode {
229230

230231
ExprArgumentNode() {
231232
isArgumentForCall(n, call_, pos_) and
232-
not TypeInference::implicitDeref(n) and
233-
not TypeInference::implicitBorrow(n, _)
233+
not TypeInference::implicitDerefChainBorrow(n, _, _)
234234
}
235235

236236
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
237237
call.asCall() = call_ and pos = pos_
238238
}
239239
}
240240

241+
private newtype TImplicitDerefNodeState =
242+
TImplicitDerefNodeBorrowState() or
243+
TImplicitDerefNodeBeforeDerefState() or
244+
TImplicitDerefNodeAfterDerefState()
245+
246+
/**
247+
* A state used to represent the flow steps involved in implicit dereferencing.
248+
*
249+
* For example, if there is an implicit dereference in a call like `x.m()`,
250+
* then that desugars into `(*Deref::deref(&x)).m()`, and
251+
*
252+
* - `TImplicitDerefNodeBorrowState` represents the `&x` part,
253+
* - `TImplicitDerefNodeBeforeDerefState` represents the `Deref::deref(&x)` part, and
254+
* - `TImplicitDerefNodeAfterDerefState` represents the entire `*Deref::deref(&x)` part.
255+
*/
256+
class ImplicitDerefNodeState extends TImplicitDerefNodeState {
257+
string toString() {
258+
this = TImplicitDerefNodeBorrowState() and result = "borrow"
259+
or
260+
this = TImplicitDerefNodeBeforeDerefState() and result = "before deref"
261+
or
262+
this = TImplicitDerefNodeAfterDerefState() and result = "after deref"
263+
}
264+
}
265+
241266
/**
242-
* A node that represents the value of an expression _after_ implicit dereferencing
243-
* or borrowing.
267+
* A node used to represent implicit dereferencing.
244268
*/
245-
class DerefBorrowNode extends Node, TDerefBorrowNode {
269+
class ImplicitDerefNode extends Node, TImplicitDerefNode {
246270
AstNode n;
247-
boolean isBorrow;
271+
DerefChain derefChain;
272+
ImplicitDerefNodeState state;
273+
int i;
248274

249-
DerefBorrowNode() { this = TDerefBorrowNode(n, isBorrow, false) }
275+
ImplicitDerefNode() { this = TImplicitDerefNode(n, derefChain, state, i, false) }
250276

251-
AstNode getNode() { result = n }
277+
/**
278+
* Gets the node that should the predecessor in a reference store-step into this
279+
* node, if any.
280+
*/
281+
Node getBorrowInputNode() {
282+
state = TImplicitDerefNodeBorrowState() and
283+
(
284+
i = 0 and
285+
result.(AstNodeNode).getAstNode() = n
286+
or
287+
result = TImplicitDerefNode(n, derefChain, TImplicitDerefNodeAfterDerefState(), i - 1, false)
288+
)
289+
}
252290

253-
predicate isBorrow() { isBorrow = true }
291+
/**
292+
* Gets the node that should the successor in a reference read-step out of this
293+
* node, if any.
294+
*/
295+
Node getDerefOutputNode() {
296+
state = TImplicitDerefNodeBeforeDerefState() and
297+
result = TImplicitDerefNode(n, derefChain, TImplicitDerefNodeAfterDerefState(), i, false)
298+
}
299+
300+
predicate isLast(AstNode node) {
301+
node = n and
302+
state = TImplicitDerefNodeAfterDerefState() and
303+
i = derefChain.length() - 1
304+
}
254305

255306
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
256307

257308
override Location getLocation() { result = n.getLocation() }
258309

259-
override string toString() {
260-
if isBorrow = true then result = n + " [borrowed]" else result = n + " [dereferenced]"
310+
override string toString() { result = n + " [implicit deref " + i + " in state " + state + "]" }
311+
}
312+
313+
final class ImplicitDerefArgNode extends ImplicitDerefNode, ArgumentNode {
314+
private DataFlowCall call_;
315+
private RustDataFlow::ArgumentPosition pos_;
316+
317+
ImplicitDerefArgNode() {
318+
state = TImplicitDerefNodeBorrowState() and
319+
call_ = TImplicitDerefCall(n, derefChain, i, _) and
320+
pos_.isSelf()
321+
or
322+
this.isLast(_) and
323+
TypeInference::implicitDerefChainBorrow(n, derefChain, false) and
324+
isArgumentForCall(n, call_.asCall(), pos_)
325+
}
326+
327+
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
328+
call = call_ and pos = pos_
329+
}
330+
}
331+
332+
private class ImplicitDerefOutNode extends ImplicitDerefNode, OutNode {
333+
private DataFlowCall call;
334+
335+
ImplicitDerefOutNode() { state = TImplicitDerefNodeBeforeDerefState() }
336+
337+
override DataFlowCall getCall(ReturnKind kind) {
338+
result = TImplicitDerefCall(n, derefChain, i, _) and
339+
kind = TNormalReturnKind()
261340
}
262341
}
263342

264343
/**
265-
* A node that represents the value of an argument of a call _after_ implicit
266-
* dereferencing or borrowing.
344+
* A node that represents the value of an expression _after_ implicit borrowing.
267345
*/
268-
final class DerefBorrowArgNode extends DerefBorrowNode, ArgumentNode {
346+
class ImplicitBorrowNode extends Node, TImplicitBorrowNode {
347+
AstNode n;
348+
DerefChain derefChain;
349+
350+
ImplicitBorrowNode() { this = TImplicitBorrowNode(n, derefChain, false) }
351+
352+
AstNode getNode() { result = n }
353+
354+
/**
355+
* Gets the node that should the predecessor in a reference store-step into this
356+
* node.
357+
*/
358+
Node getBorrowInputNode() {
359+
result =
360+
TImplicitDerefNode(n, derefChain, TImplicitDerefNodeAfterDerefState(),
361+
derefChain.length() - 1, false)
362+
or
363+
derefChain.isEmpty() and
364+
result.(AstNodeNode).getAstNode() = n
365+
}
366+
367+
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
368+
369+
override Location getLocation() { result = n.getLocation() }
370+
371+
override string toString() { result = n + " [implicit borrow]" }
372+
}
373+
374+
final class ImplicitBorrowArgNode extends ImplicitBorrowNode, ArgumentNode {
269375
private DataFlowCall call_;
270376
private RustDataFlow::ArgumentPosition pos_;
271377

272-
DerefBorrowArgNode() { isArgumentForCall(n, call_.asCall(), pos_) }
378+
ImplicitBorrowArgNode() { isArgumentForCall(n, call_.asCall(), pos_) }
273379

274380
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
275381
call = call_ and pos = pos_
@@ -478,17 +584,36 @@ final class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode {
478584
override Location getLocation() { result = e.getLocation() }
479585
}
480586

481-
final class DerefBorrowPostUpdateNode extends PostUpdateNode, TDerefBorrowNode {
482-
private Expr arg;
483-
private boolean isBorrow;
587+
final class ImplicitDerefPostUpdateNode extends PostUpdateNode, TImplicitDerefNode {
588+
AstNode n;
589+
DerefChain derefChain;
590+
ImplicitDerefNodeState state;
591+
int i;
592+
593+
ImplicitDerefPostUpdateNode() { this = TImplicitDerefNode(n, derefChain, state, i, true) }
594+
595+
override ImplicitDerefNode getPreUpdateNode() {
596+
result = TImplicitDerefNode(n, derefChain, state, i, false)
597+
}
598+
599+
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
600+
601+
override Location getLocation() { result = n.getLocation() }
602+
}
603+
604+
final class ImplicitBorrowPostUpdateNode extends PostUpdateNode, TImplicitBorrowNode {
605+
AstNode n;
606+
DerefChain derefChain;
484607

485-
DerefBorrowPostUpdateNode() { this = TDerefBorrowNode(arg, isBorrow, true) }
608+
ImplicitBorrowPostUpdateNode() { this = TImplicitBorrowNode(n, derefChain, true) }
486609

487-
override DerefBorrowNode getPreUpdateNode() { result = TDerefBorrowNode(arg, isBorrow, false) }
610+
override ImplicitBorrowNode getPreUpdateNode() {
611+
result = TImplicitBorrowNode(n, derefChain, false)
612+
}
488613

489-
override CfgScope getCfgScope() { result = arg.getEnclosingCfgScope() }
614+
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
490615

491-
override Location getLocation() { result = arg.getLocation() }
616+
override Location getLocation() { result = n.getLocation() }
492617
}
493618

494619
class DerefOutPostUpdateNode extends PostUpdateNode, TDerefOutNode {
@@ -575,12 +700,14 @@ newtype TNode =
575700
]
576701
)
577702
} or
578-
TDerefBorrowNode(AstNode n, boolean borrow, Boolean isPost) {
579-
TypeInference::implicitDeref(n) and
580-
borrow = false
581-
or
582-
TypeInference::implicitBorrow(n, _) and
583-
borrow = true
703+
TImplicitDerefNode(
704+
AstNode n, DerefChain derefChain, ImplicitDerefNodeState state, int i, Boolean isPost
705+
) {
706+
TypeInference::implicitDerefChainBorrow(n, derefChain, _) and
707+
i in [0 .. derefChain.length() - 1]
708+
} or
709+
TImplicitBorrowNode(AstNode n, DerefChain derefChain, Boolean isPost) {
710+
TypeInference::implicitDerefChainBorrow(n, derefChain, true)
584711
} or
585712
TDerefOutNode(DerefExpr de, Boolean isPost) or
586713
TIndexOutNode(IndexExpr ie, Boolean isPost) or

rust/ql/lib/codeql/rust/frameworks/rustls.model.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ extensions:
99
extensible: summaryModel
1010
data:
1111
- ["<futures_rustls::TlsConnector>::connect", "Argument[1]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
12-
- ["<rustls::conn::ConnectionCommon>::reader", "Argument[self]", "ReturnValue", "taint", "manual"]
12+
- ["<rustls::conn::ConnectionCommon>::reader", "Argument[self].Reference", "ReturnValue", "taint", "manual"]

rust/ql/lib/codeql/rust/frameworks/stdlib/core.model.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extensions:
66
# Builtin deref
77
- ["<& as core::ops::deref::Deref>::deref", "Argument[self].Reference", "ReturnValue", "value", "manual"]
88
- ["<&mut as core::ops::deref::Deref>::deref", "Argument[self].Reference", "ReturnValue", "value", "manual"]
9+
- ["<_ as core::ops::deref::Deref>::deref", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
910
# Index
1011
- ["<_ as core::ops::index::Index>::index", "Argument[self].Reference.Element", "ReturnValue.Reference", "value", "manual"]
1112
- ["<_ as core::ops::index::IndexMut>::index_mut", "Argument[self].Reference.Element", "ReturnValue.Reference", "value", "manual"]
@@ -114,10 +115,10 @@ extensions:
114115
- ["<core::pin::Pin as core::ops::deref::Deref>::deref", "Argument[self].Reference.Field[core::pin::Pin::pointer].Field[alloc::boxed::Box(0)]", "ReturnValue.Reference", "value", "manual"]
115116
# Str
116117
- ["<core::str>::as_str", "Argument[self]", "ReturnValue", "value", "manual"]
117-
- ["<core::str>::as_bytes", "Argument[self]", "ReturnValue", "value", "manual"]
118-
- ["<core::str>::parse", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
119-
- ["<core::str>::trim", "Argument[self]", "ReturnValue.Reference", "taint", "manual"]
120-
- ["<core::str>::to_string", "Argument[self]", "ReturnValue", "taint", "manual"]
118+
- ["<core::str>::as_bytes", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
119+
- ["<core::str>::parse", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
120+
- ["<core::str>::trim", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
121+
- ["<core::str>::to_string", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
121122
# Ord
122123
- ["<_ as core::cmp::Ord>::min", "Argument[self,0]", "ReturnValue", "value", "manual"]
123124
- ["<_ as core::cmp::Ord>::max", "Argument[self,0]", "ReturnValue", "value", "manual"]

0 commit comments

Comments
 (0)