mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #19004 from paldepind/rust-data-flow-split
Rust: Extract data flow node and content into separate files
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
private import rust
|
||||
private import codeql.dataflow.DataFlow
|
||||
private import internal.DataFlowImpl as DataFlowImpl
|
||||
private import DataFlowImpl::Node as Node
|
||||
private import internal.Node as Node
|
||||
private import internal.Content as Content
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and global
|
||||
@@ -23,9 +24,23 @@ module DataFlow {
|
||||
|
||||
final class PostUpdateNode = Node::PostUpdateNodePublic;
|
||||
|
||||
final class Content = DataFlowImpl::Content;
|
||||
final class Content = Content::Content;
|
||||
|
||||
final class ContentSet = DataFlowImpl::ContentSet;
|
||||
final class FieldContent = Content::FieldContent;
|
||||
|
||||
final class TuplePositionContent = Content::TuplePositionContent;
|
||||
|
||||
final class TupleFieldContent = Content::TupleFieldContent;
|
||||
|
||||
final class RecordFieldContent = Content::RecordFieldContent;
|
||||
|
||||
final class ReferenceContent = Content::ReferenceContent;
|
||||
|
||||
final class ElementContent = Content::ElementContent;
|
||||
|
||||
final class FutureContent = Content::FutureContent;
|
||||
|
||||
final class ContentSet = Content::ContentSet;
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
|
||||
239
rust/ql/lib/codeql/rust/dataflow/internal/Content.qll
Normal file
239
rust/ql/lib/codeql/rust/dataflow/internal/Content.qll
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Provides the `Content` class and subclasses thereof.
|
||||
*/
|
||||
|
||||
private import rust
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import DataFlowImpl
|
||||
|
||||
/**
|
||||
* A path to a value contained in an object. For example a field name of a struct.
|
||||
*/
|
||||
abstract class Content extends TContent {
|
||||
/** Gets a textual representation of this content. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the location of this content. */
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
/** A field belonging to either a variant or a struct. */
|
||||
abstract class FieldContent extends Content {
|
||||
/** Gets an access to this field. */
|
||||
pragma[nomagic]
|
||||
abstract FieldExprCfgNode getAnAccess();
|
||||
}
|
||||
|
||||
/** A tuple field belonging to either a variant or a struct. */
|
||||
class TupleFieldContent extends FieldContent, TTupleFieldContent {
|
||||
private TupleField field;
|
||||
|
||||
TupleFieldContent() { this = TTupleFieldContent(field) }
|
||||
|
||||
/** Holds if this field belongs to an enum variant. */
|
||||
predicate isVariantField(Variant v, int pos) { field.isVariantField(v, pos) }
|
||||
|
||||
/** Holds if this field belongs to a struct. */
|
||||
predicate isStructField(Struct s, int pos) { field.isStructField(s, pos) }
|
||||
|
||||
override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getTupleField() }
|
||||
|
||||
final override string toString() {
|
||||
exists(Variant v, int pos, string vname |
|
||||
this.isVariantField(v, pos) and
|
||||
vname = v.getName().getText() and
|
||||
// only print indices when the arity is > 1
|
||||
if exists(v.getTupleField(1)) then result = vname + "(" + pos + ")" else result = vname
|
||||
)
|
||||
or
|
||||
exists(Struct s, int pos, string sname |
|
||||
this.isStructField(s, pos) and
|
||||
sname = s.getName().getText() and
|
||||
// only print indices when the arity is > 1
|
||||
if exists(s.getTupleField(1)) then result = sname + "(" + pos + ")" else result = sname
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocation() { result = field.getLocation() }
|
||||
}
|
||||
|
||||
/** A record field belonging to either a variant or a struct. */
|
||||
class RecordFieldContent extends FieldContent, TRecordFieldContent {
|
||||
private RecordField field;
|
||||
|
||||
RecordFieldContent() { this = TRecordFieldContent(field) }
|
||||
|
||||
/** Holds if this field belongs to an enum variant. */
|
||||
predicate isVariantField(Variant v, string name) { field.isVariantField(v, name) }
|
||||
|
||||
/** Holds if this field belongs to a struct. */
|
||||
predicate isStructField(Struct s, string name) { field.isStructField(s, name) }
|
||||
|
||||
override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getRecordField() }
|
||||
|
||||
final override string toString() {
|
||||
exists(Variant v, string name, string vname |
|
||||
this.isVariantField(v, name) and
|
||||
vname = v.getName().getText() and
|
||||
// only print field when the arity is > 1
|
||||
if strictcount(v.getRecordField(_)) > 1 then result = vname + "." + name else result = vname
|
||||
)
|
||||
or
|
||||
exists(Struct s, string name, string sname |
|
||||
this.isStructField(s, name) and
|
||||
sname = s.getName().getText() and
|
||||
// only print field when the arity is > 1
|
||||
if strictcount(s.getRecordField(_)) > 1 then result = sname + "." + name else result = sname
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocation() { result = field.getLocation() }
|
||||
}
|
||||
|
||||
/** A captured variable. */
|
||||
final class CapturedVariableContent extends Content, TCapturedVariableContent {
|
||||
private Variable v;
|
||||
|
||||
CapturedVariableContent() { this = TCapturedVariableContent(v) }
|
||||
|
||||
/** Gets the captured variable. */
|
||||
Variable getVariable() { result = v }
|
||||
|
||||
override string toString() { result = "captured " + v }
|
||||
|
||||
override Location getLocation() { result = v.getLocation() }
|
||||
}
|
||||
|
||||
/** A value referred to by a reference. */
|
||||
final class ReferenceContent extends Content, TReferenceContent {
|
||||
override string toString() { result = "&ref" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* An element in a collection where we do not track the specific collection
|
||||
* type nor the placement of the element in the collection. Therefore the
|
||||
* collection should be one where the elements are reasonably homogeneous,
|
||||
* i.e., if one is tainted all elements are considered tainted.
|
||||
*
|
||||
* Examples include the elements of a set, array, vector, or stack.
|
||||
*/
|
||||
final class ElementContent extends Content, TElementContent {
|
||||
override string toString() { result = "element" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A value that a future resolves to.
|
||||
*/
|
||||
final class FutureContent extends Content, TFutureContent {
|
||||
override string toString() { result = "future" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* Content stored at a position in a tuple.
|
||||
*
|
||||
* NOTE: Unlike `struct`s and `enum`s tuples are structural and not nominal,
|
||||
* hence we don't store a canonical path for them.
|
||||
*/
|
||||
final class TuplePositionContent extends FieldContent, TTuplePositionContent {
|
||||
private int pos;
|
||||
|
||||
TuplePositionContent() { this = TTuplePositionContent(pos) }
|
||||
|
||||
/** Gets the index of this tuple position. */
|
||||
int getPosition() { result = pos }
|
||||
|
||||
override FieldExprCfgNode getAnAccess() {
|
||||
// TODO: limit to tuple types
|
||||
result.getNameRef().getText().toInt() = pos
|
||||
}
|
||||
|
||||
override string toString() { result = "tuple." + pos.toString() }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A content for the index of an argument to at function call.
|
||||
*
|
||||
* Used by the model generator to create flow summaries for higher-order
|
||||
* functions.
|
||||
*/
|
||||
final class FunctionCallArgumentContent extends Content, TFunctionCallArgumentContent {
|
||||
private int pos;
|
||||
|
||||
FunctionCallArgumentContent() { this = TFunctionCallArgumentContent(pos) }
|
||||
|
||||
int getPosition() { result = pos }
|
||||
|
||||
override string toString() { result = "function argument at " + pos }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A content for the return value of function call.
|
||||
*
|
||||
* Used by the model generator to create flow summaries for higher-order
|
||||
* functions.
|
||||
*/
|
||||
final class FunctionCallReturnContent extends Content, TFunctionCallReturnContent {
|
||||
override string toString() { result = "function return" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
/** A value that represents a set of `Content`s. */
|
||||
abstract class ContentSet extends TContentSet {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets a content that may be stored into when storing into this set. */
|
||||
abstract Content getAStoreContent();
|
||||
|
||||
/** Gets a content that may be read from when reading from this set. */
|
||||
abstract Content getAReadContent();
|
||||
}
|
||||
|
||||
final class SingletonContentSet extends ContentSet, TSingletonContentSet {
|
||||
private Content c;
|
||||
|
||||
SingletonContentSet() { this = TSingletonContentSet(c) }
|
||||
|
||||
Content getContent() { result = c }
|
||||
|
||||
override string toString() { result = c.toString() }
|
||||
|
||||
override Content getAStoreContent() { result = c }
|
||||
|
||||
override Content getAReadContent() { result = c }
|
||||
}
|
||||
|
||||
private import codeql.rust.internal.CachedStages
|
||||
|
||||
cached
|
||||
newtype TContent =
|
||||
TTupleFieldContent(TupleField field) { Stages::DataFlowStage::ref() } or
|
||||
TRecordFieldContent(RecordField field) or
|
||||
// TODO: Remove once library types are extracted
|
||||
TVariantInLibTupleFieldContent(VariantInLib::VariantInLib v, int pos) { pos = v.getAPosition() } or
|
||||
TElementContent() or
|
||||
TFutureContent() or
|
||||
TTuplePositionContent(int pos) {
|
||||
pos in [0 .. max([
|
||||
any(TuplePat pat).getNumberOfFields(),
|
||||
any(FieldExpr access).getNameRef().getText().toInt()
|
||||
]
|
||||
)]
|
||||
} or
|
||||
TFunctionCallReturnContent() or
|
||||
TFunctionCallArgumentContent(int pos) {
|
||||
pos in [0 .. any(CallExpr c).getArgList().getNumberOfArgs() - 1]
|
||||
} or
|
||||
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
|
||||
TReferenceContent()
|
||||
@@ -1,6 +1,7 @@
|
||||
import codeql.rust.dataflow.DataFlow::DataFlow as DataFlow
|
||||
private import rust
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.dataflow.internal.Node as Node
|
||||
private import codeql.rust.dataflow.internal.TaintTrackingImpl
|
||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ private import codeql.dataflow.internal.FlowSummaryImpl
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.dataflow.FlowSummary
|
||||
private import Content
|
||||
|
||||
module Input implements InputSig<Location, RustDataFlow> {
|
||||
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
|
||||
|
||||
480
rust/ql/lib/codeql/rust/dataflow/internal/Node.qll
Normal file
480
rust/ql/lib/codeql/rust/dataflow/internal/Node.qll
Normal file
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
* Provides the `Node` class and subclasses thereof.
|
||||
*
|
||||
* Classes with names ending in `Public` are exposed as `final` aliases in the
|
||||
* public `DataFlow` API, so they should not expose internal implementation details.
|
||||
*/
|
||||
|
||||
private import codeql.util.Boolean
|
||||
private import codeql.dataflow.DataFlow
|
||||
private import codeql.dataflow.internal.DataFlowImpl
|
||||
private import rust
|
||||
private import SsaImpl as SsaImpl
|
||||
private import codeql.rust.controlflow.ControlFlowGraph
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.dataflow.Ssa
|
||||
private import codeql.rust.dataflow.FlowSummary
|
||||
private import Node as Node
|
||||
private import DataFlowImpl
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
/** An element, viewed as a node in a data flow graph. */
|
||||
abstract class NodePublic extends TNode {
|
||||
/** Gets the location of this node. */
|
||||
abstract Location getLocation();
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Gets the expression that corresponds to this node, if any.
|
||||
*/
|
||||
ExprCfgNode asExpr() { none() }
|
||||
|
||||
/**
|
||||
* Gets the parameter that corresponds to this node, if any.
|
||||
*/
|
||||
ParamBase asParameter() { result = this.(SourceParameterNode).getParameter().getParamBase() }
|
||||
|
||||
/**
|
||||
* Gets the pattern that corresponds to this node, if any.
|
||||
*/
|
||||
PatCfgNode asPat() { none() }
|
||||
}
|
||||
|
||||
abstract class Node extends NodePublic {
|
||||
/** Gets the enclosing callable. */
|
||||
DataFlowCallable getEnclosingCallable() { result = TCfgScope(this.getCfgScope()) }
|
||||
|
||||
/** Do not call: use `getEnclosingCallable()` instead. */
|
||||
abstract CfgScope getCfgScope();
|
||||
|
||||
/**
|
||||
* Gets the control flow node that corresponds to this data flow node.
|
||||
*/
|
||||
CfgNode getCfgNode() { none() }
|
||||
}
|
||||
|
||||
/** A node type that is not implemented. */
|
||||
final class NaNode extends Node {
|
||||
NaNode() { none() }
|
||||
|
||||
override CfgScope getCfgScope() { none() }
|
||||
|
||||
override string toString() { result = "N/A" }
|
||||
|
||||
override Location getLocation() { none() }
|
||||
}
|
||||
|
||||
/** A data flow node used to model flow summaries. */
|
||||
class FlowSummaryNode extends Node, TFlowSummaryNode {
|
||||
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
|
||||
|
||||
/** Gets the summarized callable that this node belongs to, if any. */
|
||||
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
|
||||
result = this.getSummaryNode().getSummarizedCallable()
|
||||
}
|
||||
|
||||
/** Gets the AST source node that this node belongs to, if any */
|
||||
FlowSummaryImpl::Public::SourceElement getSourceElement() {
|
||||
result = this.getSummaryNode().getSourceElement()
|
||||
}
|
||||
|
||||
/** Gets the AST sink node that this node belongs to, if any */
|
||||
FlowSummaryImpl::Public::SinkElement getSinkElement() {
|
||||
result = this.getSummaryNode().getSinkElement()
|
||||
}
|
||||
|
||||
/** Holds is this node is a source node of kind `kind`. */
|
||||
predicate isSource(string kind, string model) {
|
||||
this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind, model)
|
||||
}
|
||||
|
||||
/** Holds is this node is a sink node of kind `kind`. */
|
||||
predicate isSink(string kind, string model) {
|
||||
this.getSummaryNode().(FlowSummaryImpl::Private::SinkInputNode).isExit(kind, model)
|
||||
}
|
||||
|
||||
override CfgScope getCfgScope() {
|
||||
result = this.getSummaryNode().getSourceElement().getEnclosingCfgScope()
|
||||
or
|
||||
result = this.getSummaryNode().getSinkElement().getEnclosingCfgScope()
|
||||
}
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.asLibraryCallable() = this.getSummarizedCallable()
|
||||
or
|
||||
result.asCfgScope() = this.getCfgScope()
|
||||
}
|
||||
|
||||
override Location getLocation() {
|
||||
exists(this.getSummarizedCallable()) and
|
||||
result instanceof EmptyLocation
|
||||
or
|
||||
result = this.getSourceElement().getLocation()
|
||||
or
|
||||
result = this.getSinkElement().getLocation()
|
||||
}
|
||||
|
||||
override string toString() { result = this.getSummaryNode().toString() }
|
||||
}
|
||||
|
||||
/** A data flow node that corresponds directly to a CFG node for an AST node. */
|
||||
abstract class AstCfgFlowNode extends Node {
|
||||
AstCfgNode n;
|
||||
|
||||
final override CfgNode getCfgNode() { result = n }
|
||||
|
||||
final override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
|
||||
|
||||
final override Location getLocation() { result = n.getAstNode().getLocation() }
|
||||
|
||||
final override string toString() { result = n.getAstNode().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in the data flow graph that corresponds to an expression in the
|
||||
* AST.
|
||||
*
|
||||
* Note that because of control flow splitting, one `Expr` may correspond
|
||||
* to multiple `ExprNode`s, just like it may correspond to multiple
|
||||
* `ControlFlow::Node`s.
|
||||
*/
|
||||
class ExprNode extends AstCfgFlowNode, TExprNode {
|
||||
override ExprCfgNode n;
|
||||
|
||||
ExprNode() { this = TExprNode(n) }
|
||||
|
||||
override ExprCfgNode asExpr() { result = n }
|
||||
}
|
||||
|
||||
final class PatNode extends AstCfgFlowNode, TPatNode {
|
||||
override PatCfgNode n;
|
||||
|
||||
PatNode() { this = TPatNode(n) }
|
||||
|
||||
override PatCfgNode asPat() { result = n }
|
||||
}
|
||||
|
||||
/** A data flow node that corresponds to a name node in the CFG. */
|
||||
final class NameNode extends AstCfgFlowNode, TNameNode {
|
||||
override NameCfgNode n;
|
||||
|
||||
NameNode() { this = TNameNode(n) }
|
||||
|
||||
NameCfgNode asName() { result = n }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
abstract class ParameterNode extends Node {
|
||||
/** Holds if this node is a parameter of `c` at position `pos`. */
|
||||
abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos);
|
||||
}
|
||||
|
||||
final class SourceParameterNode extends AstCfgFlowNode, ParameterNode, TSourceParameterNode {
|
||||
override ParamBaseCfgNode n;
|
||||
|
||||
SourceParameterNode() { this = TSourceParameterNode(n) }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
n.getAstNode() = pos.getParameterIn(c.asCfgScope().(Callable).getParamList())
|
||||
}
|
||||
|
||||
/** Get the parameter position of this parameter. */
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
/** Gets the parameter in the CFG that this node corresponds to. */
|
||||
ParamBaseCfgNode getParameter() { result = n }
|
||||
}
|
||||
|
||||
/** A parameter for a library callable with a flow summary. */
|
||||
final class SummaryParameterNode extends ParameterNode, FlowSummaryNode {
|
||||
private ParameterPosition pos_;
|
||||
|
||||
SummaryParameterNode() {
|
||||
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), pos_)
|
||||
}
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
this.getSummarizedCallable() = c.asLibraryCallable() and pos = pos_
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The run-time representation of a closure itself at function entry, viewed
|
||||
* as a node in a data flow graph.
|
||||
*/
|
||||
final class ClosureParameterNode extends ParameterNode, TClosureSelfReferenceNode {
|
||||
private CfgScope cfgScope;
|
||||
|
||||
ClosureParameterNode() { this = TClosureSelfReferenceNode(cfgScope) }
|
||||
|
||||
final override CfgScope getCfgScope() { result = cfgScope }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
cfgScope = c.asCfgScope() and pos.isClosureSelf()
|
||||
}
|
||||
|
||||
override Location getLocation() { result = cfgScope.getLocation() }
|
||||
|
||||
override string toString() { result = "closure self in " + cfgScope }
|
||||
}
|
||||
|
||||
abstract class ArgumentNode extends Node {
|
||||
abstract predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos);
|
||||
}
|
||||
|
||||
final class ExprArgumentNode extends ArgumentNode, ExprNode {
|
||||
private CallExprBaseCfgNode call_;
|
||||
private RustDataFlow::ArgumentPosition pos_;
|
||||
|
||||
ExprArgumentNode() { isArgumentForCall(n, call_, pos_) }
|
||||
|
||||
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
|
||||
call.asCallBaseExprCfgNode() = call_ and pos = pos_
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The receiver of a method call _after_ any implicit borrow or dereferencing
|
||||
* has taken place.
|
||||
*/
|
||||
final class ReceiverNode extends ArgumentNode, TReceiverNode {
|
||||
private MethodCallExprCfgNode n;
|
||||
|
||||
ReceiverNode() { this = TReceiverNode(n, false) }
|
||||
|
||||
ExprCfgNode getReceiver() { result = n.getReceiver() }
|
||||
|
||||
MethodCallExprCfgNode getMethodCall() { result = n }
|
||||
|
||||
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
|
||||
call.asMethodCallExprCfgNode() = n and pos = TSelfParameterPosition()
|
||||
}
|
||||
|
||||
override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
|
||||
|
||||
override Location getLocation() { result = this.getReceiver().getLocation() }
|
||||
|
||||
override string toString() { result = "receiver for " + this.getReceiver() }
|
||||
}
|
||||
|
||||
final class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
||||
private FlowSummaryImpl::Private::SummaryNode receiver;
|
||||
private RustDataFlow::ArgumentPosition pos_;
|
||||
|
||||
SummaryArgumentNode() {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(receiver, this.getSummaryNode(), pos_)
|
||||
}
|
||||
|
||||
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
|
||||
call.isSummaryCall(_, receiver) and pos = pos_
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that represents the run-time representation of a closure
|
||||
* passed into the closure body at an invocation.
|
||||
*/
|
||||
final class ClosureArgumentNode extends ArgumentNode, ExprNode {
|
||||
private CallExprCfgNode call_;
|
||||
|
||||
ClosureArgumentNode() { lambdaCallExpr(call_, _, this.asExpr()) }
|
||||
|
||||
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
|
||||
call.asCallExprCfgNode() = call_ and
|
||||
pos.isClosureSelf()
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA node. */
|
||||
class SsaNode extends Node, TSsaNode {
|
||||
SsaImpl::DataFlowIntegration::SsaNode node;
|
||||
|
||||
SsaNode() { this = TSsaNode(node) }
|
||||
|
||||
override CfgScope getCfgScope() { result = node.getBasicBlock().getScope() }
|
||||
|
||||
/** Gets the definition this node corresponds to, if any. */
|
||||
SsaImpl::Definition asDefinition() {
|
||||
result = node.(SsaImpl::DataFlowIntegration::SsaDefinitionNode).getDefinition()
|
||||
}
|
||||
|
||||
override Location getLocation() { result = node.getLocation() }
|
||||
|
||||
override string toString() { result = "[SSA] " + node.toString() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents a value returned by a callable. */
|
||||
abstract class ReturnNode extends Node {
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
|
||||
final class ExprReturnNode extends ExprNode, ReturnNode {
|
||||
ExprReturnNode() { this.getCfgNode().getASuccessor() instanceof AnnotatedExitCfgNode }
|
||||
|
||||
override ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
final class SummaryReturnNode extends FlowSummaryNode, ReturnNode {
|
||||
private ReturnKind rk;
|
||||
|
||||
SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), rk) }
|
||||
|
||||
override ReturnKind getKind() { result = rk }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call for this node. */
|
||||
abstract DataFlowCall getCall(ReturnKind kind);
|
||||
}
|
||||
|
||||
final private class ExprOutNode extends ExprNode, OutNode {
|
||||
ExprOutNode() { this.asExpr() instanceof CallExprBaseCfgNode }
|
||||
|
||||
/** Gets the underlying call CFG node that includes this out node. */
|
||||
override DataFlowCall getCall(ReturnKind kind) {
|
||||
result.asCallBaseExprCfgNode() = this.getCfgNode() and
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
}
|
||||
|
||||
final class SummaryOutNode extends FlowSummaryNode, OutNode {
|
||||
private DataFlowCall call;
|
||||
private ReturnKind kind_;
|
||||
|
||||
SummaryOutNode() {
|
||||
exists(FlowSummaryImpl::Private::SummaryNode receiver |
|
||||
call.isSummaryCall(_, receiver) and
|
||||
FlowSummaryImpl::Private::summaryOutNode(receiver, this.getSummaryNode(), kind_)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ }
|
||||
}
|
||||
|
||||
/**
|
||||
* A synthesized data flow node representing a closure object that tracks
|
||||
* captured variables.
|
||||
*/
|
||||
class CaptureNode extends Node, TCaptureNode {
|
||||
private VariableCapture::Flow::SynthesizedCaptureNode cn;
|
||||
|
||||
CaptureNode() { this = TCaptureNode(cn) }
|
||||
|
||||
VariableCapture::Flow::SynthesizedCaptureNode getSynthesizedCaptureNode() { result = cn }
|
||||
|
||||
override CfgScope getCfgScope() { result = cn.getEnclosingCallable() }
|
||||
|
||||
override Location getLocation() { result = cn.getLocation() }
|
||||
|
||||
override string toString() { result = cn.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update.
|
||||
*/
|
||||
abstract class PostUpdateNodePublic extends NodePublic {
|
||||
/** Gets the node before the state update. */
|
||||
abstract NodePublic getPreUpdateNode();
|
||||
}
|
||||
|
||||
abstract class PostUpdateNode extends PostUpdateNodePublic, Node {
|
||||
override string toString() { result = "[post] " + this.getPreUpdateNode().toString() }
|
||||
}
|
||||
|
||||
final class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode {
|
||||
private ExprCfgNode n;
|
||||
|
||||
ExprPostUpdateNode() { this = TExprPostUpdateNode(n) }
|
||||
|
||||
override Node getPreUpdateNode() { result = TExprNode(n) }
|
||||
|
||||
override CfgScope getCfgScope() { result = n.getScope() }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
}
|
||||
|
||||
final class ReceiverPostUpdateNode extends PostUpdateNode, TReceiverNode {
|
||||
private MethodCallExprCfgNode n;
|
||||
|
||||
ReceiverPostUpdateNode() { this = TReceiverNode(n, true) }
|
||||
|
||||
override Node getPreUpdateNode() { result = TReceiverNode(n, false) }
|
||||
|
||||
override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
|
||||
|
||||
override Location getLocation() { result = n.getReceiver().getLocation() }
|
||||
}
|
||||
|
||||
final class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
|
||||
private FlowSummaryNode pre;
|
||||
|
||||
SummaryPostUpdateNode() {
|
||||
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(), pre.getSummaryNode())
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { result = pre }
|
||||
|
||||
final override string toString() { result = PostUpdateNode.super.toString() }
|
||||
}
|
||||
|
||||
private class CapturePostUpdateNode extends PostUpdateNode, CaptureNode {
|
||||
private CaptureNode pre;
|
||||
|
||||
CapturePostUpdateNode() {
|
||||
VariableCapture::Flow::capturePostUpdateNode(this.getSynthesizedCaptureNode(),
|
||||
pre.getSynthesizedCaptureNode())
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { result = pre }
|
||||
|
||||
final override string toString() { result = PostUpdateNode.super.toString() }
|
||||
}
|
||||
|
||||
final class CastNode = NaNode;
|
||||
|
||||
private import codeql.rust.internal.CachedStages
|
||||
|
||||
cached
|
||||
newtype TNode =
|
||||
TExprNode(ExprCfgNode n) { Stages::DataFlowStage::ref() } or
|
||||
TSourceParameterNode(ParamBaseCfgNode p) or
|
||||
TPatNode(PatCfgNode p) or
|
||||
TNameNode(NameCfgNode n) { n.getName() = any(Variable v).getName() } or
|
||||
TExprPostUpdateNode(ExprCfgNode e) {
|
||||
isArgumentForCall(e, _, _)
|
||||
or
|
||||
lambdaCallExpr(_, _, e)
|
||||
or
|
||||
lambdaCreationExpr(e.getExpr(), _)
|
||||
or
|
||||
// Whenever `&mut e` has a post-update node we also create one for `e`.
|
||||
// E.g., for `e` in `f(..., &mut e, ...)` or `*(&mut e) = ...`.
|
||||
e = any(RefExprCfgNode ref | ref.isMut() and exists(TExprPostUpdateNode(ref))).getExpr()
|
||||
or
|
||||
e =
|
||||
[
|
||||
any(IndexExprCfgNode i).getBase(), any(FieldExprCfgNode access).getExpr(),
|
||||
any(TryExprCfgNode try).getExpr(),
|
||||
any(PrefixExprCfgNode pe | pe.getOperatorName() = "*").getExpr(),
|
||||
any(AwaitExprCfgNode a).getExpr(), any(MethodCallExprCfgNode mc).getReceiver()
|
||||
]
|
||||
} or
|
||||
TReceiverNode(MethodCallExprCfgNode mc, Boolean isPost) or
|
||||
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
|
||||
TClosureSelfReferenceNode(CfgScope c) { lambdaCreationExpr(c, _) } or
|
||||
TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn)
|
||||
@@ -4,6 +4,8 @@ private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.FlowSummary
|
||||
private import DataFlowImpl
|
||||
private import Node as Node
|
||||
private import Content
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import codeql.rust.internal.CachedStages
|
||||
|
||||
|
||||
@@ -131,6 +131,8 @@ module Stages {
|
||||
cached
|
||||
module DataFlowStage {
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.dataflow.internal.Node
|
||||
private import codeql.rust.dataflow.internal.Content
|
||||
private import codeql.rust.dataflow.internal.TaintTrackingImpl
|
||||
|
||||
/**
|
||||
@@ -152,6 +154,10 @@ module Stages {
|
||||
exists(Node n)
|
||||
or
|
||||
RustTaintTracking::defaultAdditionalTaintStep(_, _, _)
|
||||
or
|
||||
exists(DataFlowCall call)
|
||||
or
|
||||
exists(Content content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ private import codeql.rust.security.SensitiveData
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.FlowSource
|
||||
private import codeql.rust.dataflow.FlowSink
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.dataflow.internal.Node as Node
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting "use of a broken or weak
|
||||
|
||||
@@ -8,6 +8,7 @@ private import codeql.dataflow.test.InlineFlowTest
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.dataflow.internal.Node as Node
|
||||
private import codeql.rust.dataflow.internal.TaintTrackingImpl
|
||||
private import codeql.rust.dataflow.internal.ModelsAsData as MaD
|
||||
private import internal.InlineExpectationsTestImpl as InlineExpectationsTestImpl
|
||||
|
||||
@@ -17,7 +17,6 @@ import rust
|
||||
import codeql.rust.security.CleartextLoggingExtensions
|
||||
import codeql.rust.dataflow.DataFlow
|
||||
import codeql.rust.dataflow.TaintTracking
|
||||
import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for cleartext logging vulnerabilities.
|
||||
@@ -44,7 +43,7 @@ module CleartextLoggingConfig implements DataFlow::ConfigSig {
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
// flow out from tuple content at sinks.
|
||||
isSink(node) and
|
||||
c.getAReadContent() instanceof TuplePositionContent
|
||||
c.getAReadContent() instanceof DataFlow::TuplePositionContent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ private import rust
|
||||
private import rust as R
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.dataflow.internal.Node as Node
|
||||
private import codeql.rust.dataflow.internal.Content
|
||||
private import codeql.rust.dataflow.FlowSource as FlowSource
|
||||
private import codeql.rust.dataflow.FlowSink as FlowSink
|
||||
private import codeql.rust.dataflow.internal.TaintTrackingImpl
|
||||
|
||||
@@ -8,7 +8,7 @@ private module Tm = TranslateModels<provenance/1>;
|
||||
|
||||
query predicate models = Tm::models/2;
|
||||
|
||||
query predicate localStep(Node nodeFrom, Node nodeTo) {
|
||||
query predicate localStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Local flow steps that don't originate from a flow summary.
|
||||
RustDataFlow::simpleLocalFlowStep(nodeFrom, nodeTo, "")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import codeql.rust.dataflow.DataFlow
|
||||
import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
import codeql.rust.dataflow.internal.Node as Node
|
||||
import codeql.rust.dataflow.internal.TaintTrackingImpl
|
||||
import utils.test.TranslateModels
|
||||
|
||||
|
||||
Reference in New Issue
Block a user