mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +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 rust
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
private import internal.DataFlowImpl as DataFlowImpl
|
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
|
* Provides classes for performing local (intra-procedural) and global
|
||||||
@@ -23,9 +24,23 @@ module DataFlow {
|
|||||||
|
|
||||||
final class PostUpdateNode = Node::PostUpdateNodePublic;
|
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
|
* 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
|
import codeql.rust.dataflow.DataFlow::DataFlow as DataFlow
|
||||||
private import rust
|
private import rust
|
||||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
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.TaintTrackingImpl
|
||||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
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.dataflow.internal.AccessPathSyntax as AccessPath
|
||||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||||
private import codeql.rust.dataflow.FlowSummary
|
private import codeql.rust.dataflow.FlowSummary
|
||||||
|
private import Content
|
||||||
|
|
||||||
module Input implements InputSig<Location, RustDataFlow> {
|
module Input implements InputSig<Location, RustDataFlow> {
|
||||||
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
|
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.DataFlow
|
||||||
private import codeql.rust.dataflow.FlowSummary
|
private import codeql.rust.dataflow.FlowSummary
|
||||||
private import DataFlowImpl
|
private import DataFlowImpl
|
||||||
|
private import Node as Node
|
||||||
|
private import Content
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import codeql.rust.internal.CachedStages
|
private import codeql.rust.internal.CachedStages
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ module Stages {
|
|||||||
cached
|
cached
|
||||||
module DataFlowStage {
|
module DataFlowStage {
|
||||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
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
|
private import codeql.rust.dataflow.internal.TaintTrackingImpl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,6 +154,10 @@ module Stages {
|
|||||||
exists(Node n)
|
exists(Node n)
|
||||||
or
|
or
|
||||||
RustTaintTracking::defaultAdditionalTaintStep(_, _, _)
|
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.DataFlow
|
||||||
private import codeql.rust.dataflow.FlowSource
|
private import codeql.rust.dataflow.FlowSource
|
||||||
private import codeql.rust.dataflow.FlowSink
|
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
|
* 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.controlflow.CfgNodes
|
||||||
private import codeql.rust.dataflow.DataFlow
|
private import codeql.rust.dataflow.DataFlow
|
||||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
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.TaintTrackingImpl
|
||||||
private import codeql.rust.dataflow.internal.ModelsAsData as MaD
|
private import codeql.rust.dataflow.internal.ModelsAsData as MaD
|
||||||
private import internal.InlineExpectationsTestImpl as InlineExpectationsTestImpl
|
private import internal.InlineExpectationsTestImpl as InlineExpectationsTestImpl
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import rust
|
|||||||
import codeql.rust.security.CleartextLoggingExtensions
|
import codeql.rust.security.CleartextLoggingExtensions
|
||||||
import codeql.rust.dataflow.DataFlow
|
import codeql.rust.dataflow.DataFlow
|
||||||
import codeql.rust.dataflow.TaintTracking
|
import codeql.rust.dataflow.TaintTracking
|
||||||
import codeql.rust.dataflow.internal.DataFlowImpl
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A taint-tracking configuration for cleartext logging vulnerabilities.
|
* 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) {
|
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||||
// flow out from tuple content at sinks.
|
// flow out from tuple content at sinks.
|
||||||
isSink(node) and
|
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 rust as R
|
||||||
private import codeql.rust.dataflow.DataFlow
|
private import codeql.rust.dataflow.DataFlow
|
||||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
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.FlowSource as FlowSource
|
||||||
private import codeql.rust.dataflow.FlowSink as FlowSink
|
private import codeql.rust.dataflow.FlowSink as FlowSink
|
||||||
private import codeql.rust.dataflow.internal.TaintTrackingImpl
|
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 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.
|
// Local flow steps that don't originate from a flow summary.
|
||||||
RustDataFlow::simpleLocalFlowStep(nodeFrom, nodeTo, "")
|
RustDataFlow::simpleLocalFlowStep(nodeFrom, nodeTo, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import codeql.rust.dataflow.DataFlow
|
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 codeql.rust.dataflow.internal.TaintTrackingImpl
|
||||||
import utils.test.TranslateModels
|
import utils.test.TranslateModels
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user