diff --git a/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll b/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll index a384f9e5c37..3cb4dd9a982 100644 --- a/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll +++ b/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll @@ -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 diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/Content.qll b/rust/ql/lib/codeql/rust/dataflow/internal/Content.qll new file mode 100644 index 00000000000..f9ad0a02cb7 --- /dev/null +++ b/rust/ql/lib/codeql/rust/dataflow/internal/Content.qll @@ -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() diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll index 0cabde4ba1f..9202985a852 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll @@ -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 diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index ea24d4bfe37..b589fe4ad6f 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -15,6 +15,8 @@ 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 +private import Content private import FlowSummaryImpl as FlowSummaryImpl /** @@ -139,7 +141,7 @@ private predicate callToMethod(CallExpr call) { * Note that this does not hold for the receiever expression of a method call * as the synthetic `ReceiverNode` is the argument for the `self` parameter. */ -private predicate isArgumentForCall(ExprCfgNode arg, CallExprBaseCfgNode call, ParameterPosition pos) { +predicate isArgumentForCall(ExprCfgNode arg, CallExprBaseCfgNode call, ParameterPosition pos) { if callToMethod(call.(CallExprCfgNode).getCallExpr()) then // The first argument is for the `self` parameter @@ -150,450 +152,12 @@ private predicate isArgumentForCall(ExprCfgNode arg, CallExprBaseCfgNode call, P else arg = call.getArgument(pos.getPosition()) } -/** - * 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. - */ -module Node { - /** 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; -} - -final class Node = Node::Node; - /** Provides logic related to SSA. */ module SsaFlow { private module SsaFlow = SsaImpl::DataFlowIntegration; - private Node::ParameterNode toParameterNode(ParamCfgNode p) { - result.(Node::SourceParameterNode).getParameter() = p + private ParameterNode toParameterNode(ParamCfgNode p) { + result.(SourceParameterNode).getParameter() = p } /** Converts a control flow node into an SSA control flow node. */ @@ -602,8 +166,7 @@ module SsaFlow { or result.(SsaFlow::ExprNode).getExpr() = n.asExpr() or - result.(SsaFlow::ExprPostUpdateNode).getExpr() = - n.(Node::PostUpdateNode).getPreUpdateNode().asExpr() + result.(SsaFlow::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr() or n = toParameterNode(result.(SsaFlow::ParameterNode).getParameter()) } @@ -637,18 +200,16 @@ private ExprCfgNode getALastEvalNode(ExprCfgNode e) { module LocalFlow { predicate flowSummaryLocalStep(Node nodeFrom, Node nodeTo, string model) { exists(FlowSummaryImpl::Public::SummarizedCallable c | - FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom - .(Node::FlowSummaryNode) - .getSummaryNode(), nodeTo.(Node::FlowSummaryNode).getSummaryNode(), true, model) and - c = nodeFrom.(Node::FlowSummaryNode).getSummarizedCallable() + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), + nodeTo.(FlowSummaryNode).getSummaryNode(), true, model) and + c = nodeFrom.(FlowSummaryNode).getSummarizedCallable() ) or - FlowSummaryImpl::Private::Steps::sourceLocalStep(nodeFrom - .(Node::FlowSummaryNode) - .getSummaryNode(), nodeTo, model) + FlowSummaryImpl::Private::Steps::sourceLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), + nodeTo, model) or FlowSummaryImpl::Private::Steps::sinkLocalStep(nodeFrom, - nodeTo.(Node::FlowSummaryNode).getSummaryNode(), model) + nodeTo.(FlowSummaryNode).getSummaryNode(), model) } pragma[nomagic] @@ -673,10 +234,10 @@ module LocalFlow { ) or // An edge from a pattern/expression to its corresponding SSA definition. - nodeFrom.(Node::AstCfgFlowNode).getCfgNode() = - nodeTo.(Node::SsaNode).asDefinition().(Ssa::WriteDefinition).getControlFlowNode() + nodeFrom.(AstCfgFlowNode).getCfgNode() = + nodeTo.(SsaNode).asDefinition().(Ssa::WriteDefinition).getControlFlowNode() or - nodeFrom.(Node::SourceParameterNode).getParameter().(ParamCfgNode).getPat() = nodeTo.asPat() + nodeFrom.(SourceParameterNode).getParameter().(ParamCfgNode).getPat() = nodeTo.asPat() or exists(AssignmentExprCfgNode a | a.getRhs() = nodeFrom.getCfgNode() and @@ -692,11 +253,11 @@ module LocalFlow { or // Simple value step from receiver expression to receiver node, in case // there is no implicit deref or borrow operation. - nodeFrom.asExpr() = nodeTo.(Node::ReceiverNode).getReceiver() + nodeFrom.asExpr() = nodeTo.(ReceiverNode).getReceiver() or // The dual step of the above, for the post-update nodes. - nodeFrom.(Node::PostUpdateNode).getPreUpdateNode().(Node::ReceiverNode).getReceiver() = - nodeTo.(Node::PostUpdateNode).getPreUpdateNode().asExpr() + nodeFrom.(PostUpdateNode).getPreUpdateNode().(ReceiverNode).getReceiver() = + nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() } } @@ -706,7 +267,7 @@ module LocalFlow { * * TODO: Remove once library code is extracted. */ -private module VariantInLib { +module VariantInLib { private import codeql.util.Option private class CrateOrigin extends string { @@ -819,213 +380,10 @@ private module VariantInLib { class VariantInLibTupleFieldContent = VariantInLib::VariantInLibTupleFieldContent; -/** - * 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) } - - predicate isVariantField(Variant v, int pos) { field.isVariantField(v, pos) } - - 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) } - - predicate isVariantField(Variant v, string name) { field.isVariantField(v, name) } - - 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. */ -private 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) } - - 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 } -} - class LambdaCallKind = Unit; /** Holds if `creation` is an expression that creates a lambda of kind `kind`. */ -private predicate lambdaCreationExpr(Expr creation, LambdaCallKind kind) { +predicate lambdaCreationExpr(Expr creation, LambdaCallKind kind) { ( creation instanceof ClosureExpr or @@ -1084,6 +442,7 @@ private module Aliases { module RustDataFlow implements InputSig { private import Aliases private import codeql.rust.dataflow.DataFlow + private import Node as Node /** * An element, viewed as a node in a data flow graph. Either an expression @@ -1101,7 +460,7 @@ module RustDataFlow implements InputSig { class PostUpdateNode = DataFlow::PostUpdateNode; - final class CastNode = Node::NaNode; + final class CastNode = Node::CastNode; /** Holds if `p` is a parameter of `c` at the position `pos`. */ predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { @@ -1120,12 +479,12 @@ module RustDataFlow implements InputSig { DataFlowType getNodeType(Node node) { any() } predicate nodeIsHidden(Node node) { - node instanceof Node::SsaNode or - node.(Node::FlowSummaryNode).getSummaryNode().isHidden() or - node instanceof Node::CaptureNode or - node instanceof Node::ClosureParameterNode or - node instanceof Node::ReceiverNode or - node instanceof Node::ReceiverPostUpdateNode + node instanceof SsaNode or + node.(FlowSummaryNode).getSummaryNode().isHidden() or + node instanceof CaptureNode or + node instanceof ClosureParameterNode or + node instanceof ReceiverNode or + node instanceof ReceiverPostUpdateNode } predicate neverSkipInPathGraph(Node node) { @@ -1230,21 +589,19 @@ module RustDataFlow implements InputSig { * variable. */ predicate jumpStep(Node node1, Node node2) { - FlowSummaryImpl::Private::Steps::summaryJumpStep(node1.(Node::FlowSummaryNode).getSummaryNode(), - node2.(Node::FlowSummaryNode).getSummaryNode()) + FlowSummaryImpl::Private::Steps::summaryJumpStep(node1.(FlowSummaryNode).getSummaryNode(), + node2.(FlowSummaryNode).getSummaryNode()) } pragma[nomagic] - private predicate implicitDerefToReceiver(Node node1, Node::ReceiverNode node2, ReferenceContent c) { + private predicate implicitDerefToReceiver(Node node1, ReceiverNode node2, ReferenceContent c) { node1.asExpr() = node2.getReceiver() and implicitDeref(node2.getMethodCall().getMethodCallExpr()) and exists(c) } pragma[nomagic] - private predicate implicitBorrowToReceiver( - Node node1, Node::ReceiverNode node2, ReferenceContent c - ) { + private predicate implicitBorrowToReceiver(Node node1, ReceiverNode node2, ReferenceContent c) { node1.asExpr() = node2.getReceiver() and implicitBorrow(node2.getMethodCall().getMethodCallExpr()) and exists(c) @@ -1352,8 +709,8 @@ module RustDataFlow implements InputSig { VariableCapture::readStep(node1, c, node2) ) or - FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(Node::FlowSummaryNode).getSummaryNode(), - cs, node2.(Node::FlowSummaryNode).getSummaryNode()) + FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), cs, + node2.(FlowSummaryNode).getSummaryNode()) } pragma[nomagic] @@ -1411,7 +768,7 @@ module RustDataFlow implements InputSig { c instanceof ReferenceContent and p.isRef() and node1.asPat() = p and - node2.(Node::NameNode).asName() = p.getName() + node2.(NameNode).asName() = p.getName() ) or fieldAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c) @@ -1449,8 +806,8 @@ module RustDataFlow implements InputSig { predicate storeStep(Node node1, ContentSet cs, Node node2) { storeContentStep(node1, cs.(SingletonContentSet).getContent(), node2) or - FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(Node::FlowSummaryNode).getSummaryNode(), - cs, node2.(Node::FlowSummaryNode).getSummaryNode()) + FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), cs, + node2.(FlowSummaryNode).getSummaryNode()) } /** @@ -1463,8 +820,7 @@ module RustDataFlow implements InputSig { or referenceAssignment(_, n, cs.(SingletonContentSet).getContent()) or - FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(Node::FlowSummaryNode).getSummaryNode(), - cs) + FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), cs) or VariableCapture::clearsContent(n, cs.(SingletonContentSet).getContent()) } @@ -1474,8 +830,7 @@ module RustDataFlow implements InputSig { * at node `n`. */ predicate expectsContent(Node n, ContentSet cs) { - FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(Node::FlowSummaryNode) - .getSummaryNode(), cs) + FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), cs) } class NodeRegion instanceof Void { @@ -1502,7 +857,7 @@ module RustDataFlow implements InputSig { FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asLibraryCallable(), pos) ) or - VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(Node::ClosureParameterNode) + VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(ClosureParameterNode) .getCfgScope()) } @@ -1519,8 +874,8 @@ module RustDataFlow implements InputSig { SsaFlow::localMustFlowStep(node1, node2) or FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1 - .(Node::FlowSummaryNode) - .getSummaryNode(), node2.(Node::FlowSummaryNode).getSummaryNode()) + .(FlowSummaryNode) + .getSummaryNode(), node2.(FlowSummaryNode).getSummaryNode()) } /** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ @@ -1542,7 +897,7 @@ module RustDataFlow implements InputSig { f instanceof PathExpr implies f = any(Variable v).getAnAccess() ) or - call.isSummaryCall(_, receiver.(Node::FlowSummaryNode).getSummaryNode()) + call.isSummaryCall(_, receiver.(FlowSummaryNode).getSummaryNode()) ) and exists(kind) } @@ -1551,12 +906,10 @@ module RustDataFlow implements InputSig { predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } predicate knownSourceModel(Node source, string model) { - source.(Node::FlowSummaryNode).isSource(_, model) + source.(FlowSummaryNode).isSource(_, model) } - predicate knownSinkModel(Node sink, string model) { - sink.(Node::FlowSummaryNode).isSink(_, model) - } + predicate knownSinkModel(Node sink, string model) { sink.(FlowSummaryNode).isSink(_, model) } class DataFlowSecondLevelScope = Void; } @@ -1602,7 +955,7 @@ module VariableCapture { CapturedParameter() { p = this.getParameter() } - Node::SourceParameterNode getParameterNode() { result.getParameter().getParamBase() = p } + SourceParameterNode getParameterNode() { result.getParameter().getParamBase() = p } } class Expr extends CfgNode { @@ -1661,18 +1014,17 @@ module VariableCapture { module Flow = SharedVariableCapture::Flow; private Flow::ClosureNode asClosureNode(Node n) { - result = n.(Node::CaptureNode).getSynthesizedCaptureNode() + result = n.(CaptureNode).getSynthesizedCaptureNode() or result.(Flow::ExprNode).getExpr() = n.asExpr() or result.(Flow::VariableWriteSourceNode).getVariableWrite().getSource() = n.asExpr() or - result.(Flow::ExprPostUpdateNode).getExpr() = - n.(Node::PostUpdateNode).getPreUpdateNode().asExpr() + result.(Flow::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr() or result.(Flow::ParameterNode).getParameter().getParameterNode() = n or - result.(Flow::ThisParameterNode).getCallable() = n.(Node::ClosureParameterNode).getCfgScope() + result.(Flow::ThisParameterNode).getCallable() = n.(ClosureParameterNode).getCfgScope() } predicate storeStep(Node node1, CapturedVariableContent c, Node node2) { @@ -1699,40 +1051,9 @@ cached private module Cached { 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) - cached newtype TDataFlowCall = - TCall(CallExprBaseCfgNode c) or + TCall(CallExprBaseCfgNode c) { Stages::DataFlowStage::ref() } or TSummaryCall( FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver ) { @@ -1746,7 +1067,7 @@ private module Cached { /** This is the local flow predicate that is exposed. */ cached - predicate localFlowStepImpl(Node::Node nodeFrom, Node::Node nodeTo) { + predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _) @@ -1771,38 +1092,16 @@ private module Cached { cached newtype TReturnKind = TNormalReturnKind() - cached - newtype TContent = - TTupleFieldContent(TupleField field) 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() - cached newtype TContentSet = TSingletonContentSet(Content c) /** Holds if `n` is a flow source of kind `kind`. */ cached - predicate sourceNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSource(kind, _) } + predicate sourceNode(Node n, string kind) { n.(FlowSummaryNode).isSource(kind, _) } /** Holds if `n` is a flow sink of kind `kind`. */ cached - predicate sinkNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSink(kind, _) } + predicate sinkNode(Node n, string kind) { n.(FlowSummaryNode).isSink(kind, _) } } import Cached diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll index 0fd457a80d5..31c5b5b01aa 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll @@ -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 { private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/Node.qll b/rust/ql/lib/codeql/rust/dataflow/internal/Node.qll new file mode 100644 index 00000000000..c84da6712bb --- /dev/null +++ b/rust/ql/lib/codeql/rust/dataflow/internal/Node.qll @@ -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) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/TaintTrackingImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/TaintTrackingImpl.qll index 4d71297660a..7cef3b58f55 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/TaintTrackingImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/TaintTrackingImpl.qll @@ -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 diff --git a/rust/ql/lib/codeql/rust/internal/CachedStages.qll b/rust/ql/lib/codeql/rust/internal/CachedStages.qll index e59efb110b1..d9ad8e1fbc1 100644 --- a/rust/ql/lib/codeql/rust/internal/CachedStages.qll +++ b/rust/ql/lib/codeql/rust/internal/CachedStages.qll @@ -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) } } } diff --git a/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll b/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll index d49e932fa4b..b22e5153ce3 100644 --- a/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/WeakSensitiveDataHashingExtensions.qll @@ -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 diff --git a/rust/ql/lib/utils/test/InlineFlowTest.qll b/rust/ql/lib/utils/test/InlineFlowTest.qll index 3fe1e5f2e62..80abf21e1f5 100644 --- a/rust/ql/lib/utils/test/InlineFlowTest.qll +++ b/rust/ql/lib/utils/test/InlineFlowTest.qll @@ -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 diff --git a/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql b/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql index 223ea141c13..fd6d538f13f 100644 --- a/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql +++ b/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql @@ -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 } } diff --git a/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll index 99cc2407358..3a14f6a4a48 100644 --- a/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll +++ b/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -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 diff --git a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.ql b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.ql index b082800864c..6d81896d229 100644 --- a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.ql +++ b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.ql @@ -8,7 +8,7 @@ private module Tm = TranslateModels; 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, "") } diff --git a/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.ql b/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.ql index f049d77fe07..62a77b5b66b 100644 --- a/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.ql +++ b/rust/ql/test/library-tests/dataflow/taint/TaintFlowStep.ql @@ -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