Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `Deref` trait is now considered during method resolution. This means that method calls on receivers implementing the `Deref` trait will correctly resolve to methods defined on the target type. This may result in additional query results, especially for data flow queries.
70 changes: 68 additions & 2 deletions rust/ql/lib/codeql/rust/dataflow/internal/Content.qll
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class TupleFieldContent extends FieldContent, TTupleFieldContent {
not field = any(TupleType tt).getATupleField()
}

/** Gets the tuple field. */
TupleField getField() { result = field }

/** Holds if this field belongs to an enum variant. */
predicate isVariantField(Variant v, int pos) { field.isVariantField(v, pos) }

Expand Down Expand Up @@ -68,6 +71,9 @@ class StructFieldContent extends FieldContent, TStructFieldContent {

StructFieldContent() { this = TStructFieldContent(field) }

/** Gets the struct field. */
StructField getField() { result = field }

/** Holds if this field belongs to an enum variant. */
predicate isVariantField(Variant v, string name) { field.isVariantField(v, name) }

Expand Down Expand Up @@ -253,10 +259,32 @@ final class OptionalBarrier extends ContentSet, TOptionalBarrier {

private import codeql.rust.internal.CachedStages

string tupleFieldApprox(TupleField field) {
exists(Name name |
name = any(Variant v | field.isVariantField(v, _)).getName()
or
name = any(Struct s | field.isStructField(s, _)).getName()
|
result = name.getText().charAt(0)
)
}

string structFieldApprox(StructField field) {
exists(string name |
field.isVariantField(_, name) or
field.isStructField(_, name)
|
result = name.charAt(0)
)
}

cached
newtype TContent =
TTupleFieldContent(TupleField field) { Stages::DataFlowStage::ref() } or
TStructFieldContent(StructField field) or
TTupleFieldContent(TupleField field) {
Stages::DataFlowStage::ref() and
exists(tupleFieldApprox(field))
} or
TStructFieldContent(StructField field) { exists(structFieldApprox(field)) } or
TElementContent() or
TFutureContent() or
TTuplePositionContent(int pos) {
Expand All @@ -272,3 +300,41 @@ newtype TContent =
} or
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
TReferenceContent()

cached
newtype TContentApprox =
TTupleFieldContentApprox(string s) { Stages::DataFlowStage::ref() and s = tupleFieldApprox(_) } or
TStructFieldContentApprox(string s) { s = structFieldApprox(_) } or
TElementContentApprox() or
TFutureContentApprox() or
TTuplePositionContentApprox() or
TFunctionCallReturnContentApprox() or
TFunctionCallArgumentContentApprox() or
TCapturedVariableContentApprox() or
TReferenceContentApprox()

final class ContentApprox extends TContentApprox {
/** Gets a textual representation of this element. */
string toString() {
exists(string s |
this = TTupleFieldContentApprox(s) or
this = TStructFieldContentApprox(s)
|
result = s
)
or
this = TElementContentApprox() and result = "element"
or
this = TFutureContentApprox() and result = "future"
or
this = TTuplePositionContentApprox() and result = "tuple.position"
or
this = TFunctionCallReturnContentApprox() and result = "function.return"
or
this = TFunctionCallArgumentContentApprox() and result = "function.argument"
or
this = TCapturedVariableContentApprox() and result = "captured.variable"
or
this = TReferenceContentApprox() and result = "&ref"
}
}
73 changes: 61 additions & 12 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.internal.PathResolution
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 @@ -60,6 +62,10 @@ final class DataFlowCall extends TDataFlowCall {
/** Gets the underlying call, if any. */
Call asCall() { this = TCall(result) }

predicate isImplicitDerefCall(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,20 @@ final class DataFlowCall extends TDataFlowCall {
DataFlowCallable getEnclosingCallable() {
result.asCfgScope() = this.asCall().getEnclosingCfgScope()
or
result.asCfgScope() =
any(AstNode n | this.isImplicitDerefCall(n, _, _, _)).getEnclosingCfgScope()
or
this.isSummaryCall(result.asSummarizedCallable(), _)
}

string toString() {
result = this.asCall().toString()
or
exists(AstNode n, DerefChain derefChain, int i |
this.isImplicitDerefCall(n, derefChain, i, _) and
result = "[implicit deref call " + i + " in " + derefChain.toString() + "] " + n
)
or
exists(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
Expand All @@ -83,7 +97,11 @@ final class DataFlowCall extends TDataFlowCall {
)
}

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

/**
Expand Down Expand Up @@ -257,6 +275,8 @@ private module Aliases {

class ContentAlias = Content;

class ContentApproxAlias = ContentApprox;

class ContentSetAlias = ContentSet;

class LambdaCallKindAlias = LambdaCallKind;
Expand Down Expand Up @@ -383,7 +403,8 @@ module RustDataFlow implements InputSig<Location> {
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 @@ -445,6 +466,12 @@ module RustDataFlow implements InputSig<Location> {
or
result.asSummarizedCallable() = getStaticTargetExt(c)
)
or
exists(Function f | call = TImplicitDerefCall(_, _, _, f) |
result.asCfgScope() = f
or
result.asSummarizedCallable() = f
)
}

/**
Expand All @@ -471,9 +498,27 @@ module RustDataFlow implements InputSig<Location> {

predicate forceHighPrecision(Content c) { none() }

final class ContentApprox = Content; // TODO: Implement if needed
class ContentApprox = ContentApproxAlias;

ContentApprox getContentApprox(Content c) { result = c }
ContentApprox getContentApprox(Content c) {
result = TTupleFieldContentApprox(tupleFieldApprox(c.(TupleFieldContent).getField()))
or
result = TStructFieldContentApprox(structFieldApprox(c.(StructFieldContent).getField()))
or
result = TElementContentApprox() and c instanceof ElementContent
or
result = TFutureContentApprox() and c instanceof FutureContent
or
result = TTuplePositionContentApprox() and c instanceof TuplePositionContent
or
result = TFunctionCallArgumentContentApprox() and c instanceof FunctionCallArgumentContent
or
result = TFunctionCallReturnContentApprox() and c instanceof FunctionCallReturnContent
or
result = TCapturedVariableContentApprox() and c instanceof CapturedVariableContent
or
result = TReferenceContentApprox() and c instanceof ReferenceContent
}

/**
* Holds if the parameter position `ppos` matches the argument position
Expand All @@ -499,6 +544,8 @@ module RustDataFlow implements InputSig<Location> {
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
)
or
nodeFrom = nodeTo.(ImplicitDerefNode).getLocalInputNode()
or
VariableCapture::localFlowStep(nodeFrom, nodeTo)
) and
model = ""
Expand All @@ -524,16 +571,14 @@ module RustDataFlow implements InputSig<Location> {
}

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, ImplicitDerefBorrowNode node2, ReferenceContent c) {
node1 = node2.getBorrowInputNode() and
exists(c)
}

Expand All @@ -545,10 +590,10 @@ module RustDataFlow implements InputSig<Location> {

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.(ImplicitDerefNode).isLast(container)
)
}

Expand Down Expand Up @@ -1037,6 +1082,10 @@ private module Cached {
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
Loading
Loading