Skip to content

Commit 20ebba3

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

File tree

22 files changed

+753
-436
lines changed

22 files changed

+753
-436
lines changed

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

Lines changed: 73 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,37 @@ final class OptionalBarrier extends ContentSet, TOptionalBarrier {
256262

257263
private import codeql.rust.internal.CachedStages
258264

265+
string tupleFieldApprox(TupleField field) {
266+
exists(Variant v, int pos |
267+
field.isVariantField(v, pos) and
268+
result = v.getName().getText().charAt(0)
269+
)
270+
or
271+
exists(Struct s, int pos |
272+
field.isStructField(s, pos) and
273+
result = s.getName().getText().charAt(0)
274+
)
275+
}
276+
277+
string structFieldApprox(StructField field) {
278+
exists(Variant v, string name |
279+
field.isVariantField(v, name) and
280+
result = v.getName().getText().charAt(0) + "." + name
281+
)
282+
or
283+
exists(Struct s, string name |
284+
field.isStructField(s, name) and
285+
result = s.getName().getText().charAt(0) + "." + name
286+
)
287+
}
288+
259289
cached
260290
newtype TContent =
261-
TTupleFieldContent(TupleField field) { Stages::DataFlowStage::ref() } or
262-
TStructFieldContent(StructField field) or
291+
TTupleFieldContent(TupleField field) {
292+
Stages::DataFlowStage::ref() and
293+
exists(tupleFieldApprox(field))
294+
} or
295+
TStructFieldContent(StructField field) { exists(structFieldApprox(field)) } or
263296
TElementContent() or
264297
TFutureContent() or
265298
TTuplePositionContent(int pos) {
@@ -275,3 +308,41 @@ newtype TContent =
275308
} or
276309
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
277310
TReferenceContent()
311+
312+
cached
313+
newtype TContentApprox =
314+
TTupleFieldContentApprox(string s) { Stages::DataFlowStage::ref() and s = tupleFieldApprox(_) } or
315+
TStructFieldContentApprox(string s) { s = structFieldApprox(_) } or
316+
TElementContentApprox() or
317+
TFutureContentApprox() or
318+
TTuplePositionContentApprox() or
319+
TFunctionCallReturnContentApprox() or
320+
TFunctionCallArgumentContentApprox() or
321+
TCapturedVariableContentApprox() or
322+
TReferenceContentApprox()
323+
324+
final class ContentApprox extends TContentApprox {
325+
/** Gets a textual representation of this element. */
326+
string toString() {
327+
exists(string s |
328+
this = TTupleFieldContentApprox(s) or
329+
this = TStructFieldContentApprox(s)
330+
|
331+
result = s
332+
)
333+
or
334+
this = TElementContentApprox() and result = "element"
335+
or
336+
this = TFutureContentApprox() and result = "future"
337+
or
338+
this = TTuplePositionContentApprox() and result = "tuple.position"
339+
or
340+
this = TFunctionCallReturnContentApprox() and result = "function.return"
341+
or
342+
this = TFunctionCallArgumentContentApprox() and result = "function.argument"
343+
or
344+
this = TCapturedVariableContentApprox() and result = "captured.variable"
345+
or
346+
this = TReferenceContentApprox() and result = "&ref"
347+
}
348+
}

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

Lines changed: 66 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,18 @@ 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, Node node2, ReferenceContent c) {
580+
(
581+
node1 = node2.(ImplicitDerefNode).getBorrowInputNode()
582+
or
583+
node1 = node2.(ImplicitBorrowNode).getBorrowInputNode()
584+
) and
537585
exists(c)
538586
}
539587

@@ -545,10 +593,12 @@ module RustDataFlow implements InputSig<Location> {
545593

546594
private Node getFieldExprContainerNode(FieldExpr fe) {
547595
exists(Expr container | container = fe.getContainer() |
548-
not any(DerefBorrowNode n).getNode() = container and
596+
not TypeInference::implicitDerefChainBorrow(container, _, _) and
549597
result.asExpr() = container
550598
or
551-
result.(DerefBorrowNode).getNode() = container
599+
result.(ImplicitBorrowNode).getNode() = container
600+
or
601+
result.(ImplicitDerefNode).isLast(container)
552602
)
553603
}
554604

@@ -1037,6 +1087,10 @@ private module Cached {
10371087
Stages::DataFlowStage::ref() and
10381088
call.hasEnclosingCfgScope()
10391089
} or
1090+
TImplicitDerefCall(AstNode n, DerefChain derefChain, int i, Function target) {
1091+
TypeInference::implicitDerefChainBorrow(n, derefChain, _) and
1092+
target = derefChain.getElement(i).getDerefFunction()
1093+
} or
10401094
TSummaryCall(
10411095
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
10421096
) {

0 commit comments

Comments
 (0)