Skip to content

Commit b99d899

Browse files
committed
Rust: Model implicit Deref trait calls in data flow
1 parent fd5df58 commit b99d899

File tree

22 files changed

+747
-438
lines changed

22 files changed

+747
-438
lines changed

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

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class TupleFieldContent extends FieldContent, TTupleFieldContent {
3535
not field = any(TupleType tt).getATupleField()
3636
}
3737

38+
/** Gets the tuple field. */
39+
TupleField getField() { result = field }
40+
3841
/** Holds if this field belongs to an enum variant. */
3942
predicate isVariantField(Variant v, int pos) { field.isVariantField(v, pos) }
4043

@@ -68,6 +71,9 @@ class StructFieldContent extends FieldContent, TStructFieldContent {
6871

6972
StructFieldContent() { this = TStructFieldContent(field) }
7073

74+
/** Gets the struct field. */
75+
StructField getField() { result = field }
76+
7177
/** Holds if this field belongs to an enum variant. */
7278
predicate isVariantField(Variant v, string name) { field.isVariantField(v, name) }
7379

@@ -256,10 +262,32 @@ final class OptionalBarrier extends ContentSet, TOptionalBarrier {
256262

257263
private import codeql.rust.internal.CachedStages
258264

265+
string tupleFieldApprox(TupleField field) {
266+
exists(Name name |
267+
name = any(Variant v | field.isVariantField(v, _)).getName()
268+
or
269+
name = any(Struct s | field.isStructField(s, _)).getName()
270+
|
271+
result = name.getText().charAt(0)
272+
)
273+
}
274+
275+
string structFieldApprox(StructField field) {
276+
exists(string name |
277+
field.isVariantField(_, name) or
278+
field.isStructField(_, name)
279+
|
280+
result = name.charAt(0)
281+
)
282+
}
283+
259284
cached
260285
newtype TContent =
261-
TTupleFieldContent(TupleField field) { Stages::DataFlowStage::ref() } or
262-
TStructFieldContent(StructField field) or
286+
TTupleFieldContent(TupleField field) {
287+
Stages::DataFlowStage::ref() and
288+
exists(tupleFieldApprox(field))
289+
} or
290+
TStructFieldContent(StructField field) { exists(structFieldApprox(field)) } or
263291
TElementContent() or
264292
TFutureContent() or
265293
TTuplePositionContent(int pos) {
@@ -275,3 +303,41 @@ newtype TContent =
275303
} or
276304
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
277305
TReferenceContent()
306+
307+
cached
308+
newtype TContentApprox =
309+
TTupleFieldContentApprox(string s) { Stages::DataFlowStage::ref() and s = tupleFieldApprox(_) } or
310+
TStructFieldContentApprox(string s) { s = structFieldApprox(_) } or
311+
TElementContentApprox() or
312+
TFutureContentApprox() or
313+
TTuplePositionContentApprox() or
314+
TFunctionCallReturnContentApprox() or
315+
TFunctionCallArgumentContentApprox() or
316+
TCapturedVariableContentApprox() or
317+
TReferenceContentApprox()
318+
319+
final class ContentApprox extends TContentApprox {
320+
/** Gets a textual representation of this element. */
321+
string toString() {
322+
exists(string s |
323+
this = TTupleFieldContentApprox(s) or
324+
this = TStructFieldContentApprox(s)
325+
|
326+
result = s
327+
)
328+
or
329+
this = TElementContentApprox() and result = "element"
330+
or
331+
this = TFutureContentApprox() and result = "future"
332+
or
333+
this = TTuplePositionContentApprox() and result = "tuple.position"
334+
or
335+
this = TFunctionCallReturnContentApprox() and result = "function.return"
336+
or
337+
this = TFunctionCallArgumentContentApprox() and result = "function.argument"
338+
or
339+
this = TCapturedVariableContentApprox() and result = "captured.variable"
340+
or
341+
this = TReferenceContentApprox() and result = "&ref"
342+
}
343+
}

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

Lines changed: 60 additions & 12 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
/**
@@ -257,6 +274,8 @@ private module Aliases {
257274

258275
class ContentAlias = Content;
259276

277+
class ContentApproxAlias = ContentApprox;
278+
260279
class ContentSetAlias = ContentSet;
261280

262281
class LambdaCallKindAlias = LambdaCallKind;
@@ -383,7 +402,8 @@ module RustDataFlow implements InputSig<Location> {
383402
node.(FlowSummaryNode).getSummaryNode().isHidden() or
384403
node instanceof CaptureNode or
385404
node instanceof ClosureParameterNode or
386-
node instanceof DerefBorrowNode or
405+
node instanceof ImplicitDerefNode or
406+
node instanceof ImplicitBorrowNode or
387407
node instanceof DerefOutNode or
388408
node instanceof IndexOutNode or
389409
node.asExpr() instanceof ParenExpr or
@@ -445,6 +465,12 @@ module RustDataFlow implements InputSig<Location> {
445465
or
446466
result.asSummarizedCallable() = getStaticTargetExt(c)
447467
)
468+
or
469+
exists(Function f | call = TImplicitDerefCall(_, _, _, f) |
470+
result.asCfgScope() = f
471+
or
472+
result.asSummarizedCallable() = f
473+
)
448474
}
449475

450476
/**
@@ -471,9 +497,27 @@ module RustDataFlow implements InputSig<Location> {
471497

472498
predicate forceHighPrecision(Content c) { none() }
473499

474-
final class ContentApprox = Content; // TODO: Implement if needed
500+
class ContentApprox = ContentApproxAlias;
475501

476-
ContentApprox getContentApprox(Content c) { result = c }
502+
ContentApprox getContentApprox(Content c) {
503+
result = TTupleFieldContentApprox(tupleFieldApprox(c.(TupleFieldContent).getField()))
504+
or
505+
result = TStructFieldContentApprox(structFieldApprox(c.(StructFieldContent).getField()))
506+
or
507+
result = TElementContentApprox() and c instanceof ElementContent
508+
or
509+
result = TFutureContentApprox() and c instanceof FutureContent
510+
or
511+
result = TTuplePositionContentApprox() and c instanceof TuplePositionContent
512+
or
513+
result = TFunctionCallArgumentContentApprox() and c instanceof FunctionCallArgumentContent
514+
or
515+
result = TFunctionCallReturnContentApprox() and c instanceof FunctionCallReturnContent
516+
or
517+
result = TCapturedVariableContentApprox() and c instanceof CapturedVariableContent
518+
or
519+
result = TReferenceContentApprox() and c instanceof ReferenceContent
520+
}
477521

478522
/**
479523
* Holds if the parameter position `ppos` matches the argument position
@@ -499,6 +543,8 @@ module RustDataFlow implements InputSig<Location> {
499543
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
500544
)
501545
or
546+
nodeFrom = nodeTo.(ImplicitDerefNode).getLocalInputNode()
547+
or
502548
VariableCapture::localFlowStep(nodeFrom, nodeTo)
503549
) and
504550
model = ""
@@ -524,16 +570,14 @@ module RustDataFlow implements InputSig<Location> {
524570
}
525571

526572
pragma[nomagic]
527-
private predicate implicitDeref(Node node1, DerefBorrowNode node2, ReferenceContent c) {
528-
not node2.isBorrow() and
529-
node1.asExpr() = node2.getNode() and
573+
private predicate implicitDeref(ImplicitDerefNode node1, Node node2, ReferenceContent c) {
574+
node2 = node1.getDerefOutputNode() and
530575
exists(c)
531576
}
532577

533578
pragma[nomagic]
534-
private predicate implicitBorrow(Node node1, DerefBorrowNode node2, ReferenceContent c) {
535-
node2.isBorrow() and
536-
node1.asExpr() = node2.getNode() and
579+
private predicate implicitBorrow(Node node1, ImplicitDerefBorrowNode node2, ReferenceContent c) {
580+
node1 = node2.getBorrowInputNode() and
537581
exists(c)
538582
}
539583

@@ -545,10 +589,10 @@ module RustDataFlow implements InputSig<Location> {
545589

546590
private Node getFieldExprContainerNode(FieldExpr fe) {
547591
exists(Expr container | container = fe.getContainer() |
548-
not any(DerefBorrowNode n).getNode() = container and
592+
not TypeInference::implicitDerefChainBorrow(container, _, _) and
549593
result.asExpr() = container
550594
or
551-
result.(DerefBorrowNode).getNode() = container
595+
result.(ImplicitDerefNode).isLast(container)
552596
)
553597
}
554598

@@ -1037,6 +1081,10 @@ private module Cached {
10371081
Stages::DataFlowStage::ref() and
10381082
call.hasEnclosingCfgScope()
10391083
} or
1084+
TImplicitDerefCall(AstNode n, DerefChain derefChain, int i, Function target) {
1085+
TypeInference::implicitDerefChainBorrow(n, derefChain, _) and
1086+
target = derefChain.getElement(i).getDerefFunction()
1087+
} or
10401088
TSummaryCall(
10411089
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
10421090
) {

0 commit comments

Comments
 (0)