Skip to content
Draft
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
54 changes: 43 additions & 11 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.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 @@ -47,7 +49,7 @@

/** Gets a textual representation of this callable. */
string toString() {
result = [this.asCfgScope().toString(), this.asSummarizedCallable().toString()]
result = [this.asCfgScope().toString(), "[summarized] " + this.asSummarizedCallable()]
}

/** Gets the location of this callable. */
Expand All @@ -60,6 +62,10 @@
/** Gets the underlying call, if any. */
Call asCall() { this = TCall(result) }

predicate isImplicitDeref(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,19 @@
DataFlowCallable getEnclosingCallable() {
result.asCfgScope() = this.asCall().getEnclosingCfgScope()
or
result.asCfgScope() = any(AstNode n | this.isImplicitDeref(n, _, _, _)).getEnclosingCfgScope()
or
this.isSummaryCall(result.asSummarizedCallable(), _)
}

string toString() {
result = this.asCall().toString()
or
exists(AstNode n, DerefChain derefChain, int i, Function target |

Check warning

Code scanning / CodeQL

Omittable 'exists' variable Warning

This exists variable can be omitted by using a don't-care expression
in this argument
.
this.isImplicitDeref(n, derefChain, i, target) and
result = "[implicit deref call " + i + " in " + derefChain.toString() + "] " + n
)
or
exists(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
Expand All @@ -83,7 +96,11 @@
)
}

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

/**
Expand Down Expand Up @@ -383,7 +400,8 @@
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 @@ -463,6 +481,12 @@
not staticTarget.fromSource()
)
)
or
exists(Function f | call = TImplicitDerefCall(_, _, _, f) |
result.asCfgScope() = f
or
result.asSummarizedCallable() = f
)
}

/**
Expand Down Expand Up @@ -542,16 +566,18 @@
}

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

Expand All @@ -563,10 +589,12 @@

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.(ImplicitBorrowNode).getNode() = container
or
result.(ImplicitDerefNode).isLast(container)
)
}

Expand Down Expand Up @@ -1055,6 +1083,10 @@
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
165 changes: 141 additions & 24 deletions rust/ql/lib/codeql/rust/dataflow/internal/Node.qll
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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 as Node
private import DataFlowImpl
private import FlowSummaryImpl as FlowSummaryImpl
Expand Down Expand Up @@ -229,8 +230,7 @@

ExprArgumentNode() {
isArgumentForCall(n, call_, pos_) and
not TypeInference::implicitDeref(n) and
not TypeInference::implicitBorrow(n, _)
not TypeInference::implicitDerefChainBorrow(n, _, _)
}

override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
Expand All @@ -242,34 +242,115 @@
* A node that represents the value of an expression _after_ implicit dereferencing
* or borrowing.
*/
class DerefBorrowNode extends Node, TDerefBorrowNode {
class ImplicitDerefNode extends Node, TImplicitDerefNode {
AstNode n;
boolean isBorrow;
DerefChain derefChain;
ImplicitDerefNodeState state;
int i;

DerefBorrowNode() { this = TDerefBorrowNode(n, isBorrow, false) }
ImplicitDerefNode() { this = TImplicitDerefNode(n, derefChain, state, i, false) }

AstNode getNode() { result = n }
Node getBorrowInputNode() {
state = TImplicitDerefNodeBorrowState() and
(
i = 0 and
result.(AstNodeNode).getAstNode() = n
or
result = TImplicitDerefNode(n, derefChain, TImplicitDerefNodeAfterDerefState(), i - 1, false)
)
}

predicate isBorrow() { isBorrow = true }
Node getDerefOutputNode() {
state = TImplicitDerefNodeBeforeDerefState() and
result = TImplicitDerefNode(n, derefChain, TImplicitDerefNodeAfterDerefState(), i, false)
}

predicate isLast(AstNode node) {
this =
TImplicitDerefNode(node, derefChain, TImplicitDerefNodeAfterDerefState(),
derefChain.length() - 1, false)
}

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

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

override string toString() {
if isBorrow = true then result = n + " [borrowed]" else result = n + " [dereferenced]"
override string toString() { result = n + " [implicit deref " + i + " in state " + state + "]" }
}

/**
* A node that represents the value of an argument of a call _after_ implicit
* dereferencing or borrowing.
*/
final class ImplicitDerefArgNode extends ImplicitDerefNode, ArgumentNode {
private DataFlowCall call_;
private RustDataFlow::ArgumentPosition pos_;

ImplicitDerefArgNode() {
state = TImplicitDerefNodeBorrowState() and
call_ = TImplicitDerefCall(n, derefChain, i, _) and
pos_.isSelf()
or
state = TImplicitDerefNodeAfterDerefState() and
i = derefChain.length() - 1 and
TypeInference::implicitDerefChainBorrow(n, derefChain, false) and
isArgumentForCall(n, call_.asCall(), pos_)
}

override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call = call_ and pos = pos_
}
}

private class ImplicitDerefOutNode extends ImplicitDerefNode, OutNode {
private DataFlowCall call;

Check warning

Code scanning / CodeQL

Dead code Warning

This code is never used, and it's not publicly exported.

ImplicitDerefOutNode() { state = TImplicitDerefNodeBeforeDerefState() }

override DataFlowCall getCall(ReturnKind kind) {
result = TImplicitDerefCall(n, derefChain, i, _) and
kind = TNormalReturnKind()
}
}

/**
* A node that represents the value of an expression _after_ implicit dereferencing
* or borrowing.
*/
class ImplicitBorrowNode extends Node, TImplicitBorrowNode {
AstNode n;
DerefChain derefChain;

ImplicitBorrowNode() { this = TImplicitBorrowNode(n, derefChain, false) }

AstNode getNode() { result = n }

Node getBorrowInputNode() {
result =
TImplicitDerefNode(n, derefChain, TImplicitDerefNodeAfterDerefState(),
derefChain.length() - 1, false)
or
derefChain.isEmpty() and
result.(AstNodeNode).getAstNode() = n
}

// AstNode getNode() { result = n }
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }

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

override string toString() { result = n + " [implicit borrow]" }
}

/**
* A node that represents the value of an argument of a call _after_ implicit
* dereferencing or borrowing.
*/
final class DerefBorrowArgNode extends DerefBorrowNode, ArgumentNode {
final class ImplicitBorrowArgNode extends ImplicitBorrowNode, ArgumentNode {
private DataFlowCall call_;
private RustDataFlow::ArgumentPosition pos_;

DerefBorrowArgNode() { isArgumentForCall(n, call_.asCall(), pos_) }
ImplicitBorrowArgNode() { isArgumentForCall(n, call_.asCall(), pos_) }

override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call = call_ and pos = pos_
Expand Down Expand Up @@ -478,17 +559,36 @@
override Location getLocation() { result = e.getLocation() }
}

final class DerefBorrowPostUpdateNode extends PostUpdateNode, TDerefBorrowNode {
private Expr arg;
private boolean isBorrow;
final class ImplicitDerefPostUpdateNode extends PostUpdateNode, TImplicitDerefNode {
AstNode n;
DerefChain derefChain;
ImplicitDerefNodeState state;
int i;

DerefBorrowPostUpdateNode() { this = TDerefBorrowNode(arg, isBorrow, true) }
ImplicitDerefPostUpdateNode() { this = TImplicitDerefNode(n, derefChain, state, i, true) }

override DerefBorrowNode getPreUpdateNode() { result = TDerefBorrowNode(arg, isBorrow, false) }
override ImplicitDerefNode getPreUpdateNode() {
result = TImplicitDerefNode(n, derefChain, state, i, false)
}

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

override Location getLocation() { result = arg.getLocation() }
override Location getLocation() { result = n.getLocation() }
}

final class ImplicitBorrowPostUpdateNode extends PostUpdateNode, TImplicitBorrowNode {
AstNode n;
DerefChain derefChain;

ImplicitBorrowPostUpdateNode() { this = TImplicitBorrowNode(n, derefChain, true) }

override ImplicitBorrowNode getPreUpdateNode() {
result = TImplicitBorrowNode(n, derefChain, false)
}

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

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

class DerefOutPostUpdateNode extends PostUpdateNode, TDerefOutNode {
Expand Down Expand Up @@ -544,6 +644,21 @@
CastNode() { none() }
}

newtype TImplicitDerefNodeState =
TImplicitDerefNodeBorrowState() or
TImplicitDerefNodeBeforeDerefState() or
TImplicitDerefNodeAfterDerefState()

class ImplicitDerefNodeState extends TImplicitDerefNodeState {
string toString() {
this = TImplicitDerefNodeBorrowState() and result = "borrow"
or
this = TImplicitDerefNodeBeforeDerefState() and result = "before deref"
or
this = TImplicitDerefNodeAfterDerefState() and result = "after deref"
}
}

cached
newtype TNode =
TExprNode(Expr e) { e.hasEnclosingCfgScope() and Stages::DataFlowStage::ref() } or
Expand Down Expand Up @@ -575,12 +690,14 @@
]
)
} or
TDerefBorrowNode(AstNode n, boolean borrow, Boolean isPost) {
TypeInference::implicitDeref(n) and
borrow = false
or
TypeInference::implicitBorrow(n, _) and
borrow = true
TImplicitDerefNode(
AstNode n, DerefChain derefChain, ImplicitDerefNodeState state, int i, Boolean isPost
) {
TypeInference::implicitDerefChainBorrow(n, derefChain, _) and
i in [0 .. derefChain.length() - 1]
} or
TImplicitBorrowNode(AstNode n, DerefChain derefChain, Boolean isPost) {
TypeInference::implicitDerefChainBorrow(n, derefChain, true)
} or
TDerefOutNode(DerefExpr de, Boolean isPost) or
TIndexOutNode(IndexExpr ie, Boolean isPost) or
Expand Down
1 change: 1 addition & 0 deletions rust/ql/lib/codeql/rust/frameworks/asyncstd/fs.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ extensions:
pack: codeql/rust-all
extensible: summaryModel
data:
- ["<async_std::path::pathbuf::PathBuf as core::ops::deref::Deref>::deref", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
- ["async_std::fs::canonicalize::canonicalize", "Argument[0].OptionalStep[normalize-path]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
- ["async_std::fs::canonicalize::canonicalize", "Argument[0].OptionalBarrier[normalize-path]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ extensions:
- ["<core::alloc::layout::Layout>::pad_to_align", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
- ["<core::alloc::layout::Layout>::size", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
# String
- ["<alloc::string::String as core::ops::deref::Deref>::deref", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
- ["<alloc::string::String>::as_str", "Argument[self]", "ReturnValue", "value", "manual"]
- ["<alloc::string::String>::as_bytes", "Argument[self]", "ReturnValue", "value", "manual"]
- ["<_ as alloc::string::ToString>::to_string", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
Expand Down
8 changes: 4 additions & 4 deletions rust/ql/lib/codeql/rust/frameworks/stdlib/core.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ extensions:
- ["<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"]
# Str
- ["<core::str>::as_str", "Argument[self]", "ReturnValue", "value", "manual"]
- ["<core::str>::as_bytes", "Argument[self]", "ReturnValue", "value", "manual"]
- ["<core::str>::parse", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
- ["<core::str>::trim", "Argument[self]", "ReturnValue.Reference", "taint", "manual"]
- ["<core::str>::to_string", "Argument[self]", "ReturnValue", "taint", "manual"]
- ["<core::str>::as_bytes", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
- ["<core::str>::parse", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
- ["<core::str>::trim", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
- ["<core::str>::to_string", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
# Ord
- ["<_ as core::cmp::Ord>::min", "Argument[self,0]", "ReturnValue", "value", "manual"]
- ["<_ as core::cmp::Ord>::max", "Argument[self,0]", "ReturnValue", "value", "manual"]
Expand Down
Loading
Loading