Rust: Re-factor implementation to use the new model generator interface.

This commit is contained in:
Michael Nebel
2025-04-25 13:09:13 +02:00
parent c16d913f8a
commit a6b5645b13
12 changed files with 124 additions and 99 deletions

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = ContentSensitive::captureFlow(api, _)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string noflow
where noflow = Heuristic::captureNoFlow(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SinkModels
from DataFlowSinkTargetApi api, string sink
where sink = Heuristic::captureSink(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SourceModels
from DataFlowSourceTargetApi api, string source
where source = Heuristic::captureSource(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api, _)

View File

@@ -10,6 +10,7 @@
private import codeql.rust.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import PartialFlow::PartialPathGraph
int explorationLimit() { result = 3 }

View File

@@ -10,6 +10,7 @@
private import codeql.rust.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import Heuristic
import PropagateFlow::PathGraph

View File

@@ -2,7 +2,7 @@ private import codeql.util.Unit
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.DataFlowImpl as 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
@@ -11,7 +11,25 @@ private import codeql.rust.dataflow.internal.TaintTrackingImpl
private import codeql.mad.modelgenerator.internal.ModelGeneratorImpl
private import codeql.rust.dataflow.internal.FlowSummaryImpl as FlowSummary
module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataFlow> {
private predicate relevant(Function api) {
// Only include functions that have a resolved path.
api.hasCrateOrigin() and
api.hasExtendedCanonicalPath() and
(
// This excludes closures (these are not exported API endpoints) and
// functions without a `pub` visiblity. A function can be `pub` without
// ultimately being exported by a crate, so this is an overapproximation.
api.hasVisibility()
or
// If a method implements a public trait it is exposed through the trait.
// We overapproximate this by including all trait method implementations.
exists(Impl impl | impl.hasTrait() and impl.getAssocItemList().getAssocItem(_) = api)
)
}
module ModelGeneratorCommonInput implements
ModelGeneratorCommonInputSig<Location, DataFlowImpl::RustDataFlow>
{
// NOTE: We are not using type information for now.
class Type = Unit;
@@ -23,42 +41,71 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
Type getType() { any() }
}
Callable getAsExprEnclosingCallable(NodeExtended node) { result = node.asExpr().getScope() }
Callable getEnclosingCallable(NodeExtended node) {
result = node.(Node::Node).getEnclosingCallable().asCfgScope()
}
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
predicate isRelevantType(Type t) { any() }
private predicate relevant(Function api) {
// Only include functions that have a resolved path.
api.hasCrateOrigin() and
api.hasExtendedCanonicalPath() and
(
// This excludes closures (these are not exported API endpoints) and
// functions without a `pub` visiblity. A function can be `pub` without
// ultimately being exported by a crate, so this is an overapproximation.
api.hasVisibility()
or
// If a method implements a public trait it is exposed through the trait.
// We overapproximate this by including all trait method implementations.
exists(Impl impl | impl.hasTrait() and impl.getAssocItemList().getAssocItem(_) = api)
)
/**
* Gets the underlying type of the content `c`.
*/
Type getUnderlyingContentType(DataFlow::ContentSet c) { result = any(Type t) and exists(c) }
string qualifierString() { result = "Argument[self]" }
string parameterAccess(R::ParamBase p) {
result =
"Argument[" + any(DataFlowImpl::ParameterPosition pos | p = pos.getParameterIn(_)).toString() +
"]"
}
predicate isUninterestingForDataFlowModels(Callable api) { none() }
string parameterContentAccess(R::ParamBase p) { result = parameterAccess(p) }
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }
class SourceOrSinkTargetApi extends Callable {
SourceOrSinkTargetApi() { relevant(this) }
class InstanceParameterNode extends DataFlow::ParameterNode {
InstanceParameterNode() { this.asParameter() instanceof SelfParam }
}
class SinkTargetApi extends SourceOrSinkTargetApi { }
bindingset[c]
string paramReturnNodeAsOutput(Callable c, DataFlowImpl::ParameterPosition pos) {
result = paramReturnNodeAsContentOutput(c, pos)
}
class SourceTargetApi extends SourceOrSinkTargetApi { }
bindingset[c]
string paramReturnNodeAsContentOutput(Callable c, DataFlowImpl::ParameterPosition pos) {
result = parameterContentAccess(c.getParamList().getParam(pos.getPosition()))
or
pos.isSelf() and result = qualifierString()
}
Callable returnNodeEnclosingCallable(DataFlow::Node ret) {
result = ret.(Node::Node).getEnclosingCallable().asCfgScope()
}
predicate isOwnInstanceAccessNode(DataFlowImpl::RustDataFlow::ReturnNode node) {
// This is probably not relevant to implement for Rust, as we only use
// `captureMixedFlow` which doesn't explicitly distinguish between
// functions that return `self` and those that don't.
none()
}
predicate containerContent(DataFlow::ContentSet c) {
c.(SingletonContentSet).getContent() instanceof ElementContent
}
string partialModelRow(Callable api, int i) {
i = 0 and result = api.(Function).getCrateOrigin() // crate
or
i = 1 and result = api.(Function).getExtendedCanonicalPath() // name
}
string partialNeutralModelRow(Callable api, int i) { result = partialModelRow(api, i) }
}
private import ModelGeneratorCommonInput
private import MakeModelGeneratorFactory<Location, DataFlowImpl::RustDataFlow, RustTaintTracking, ModelGeneratorCommonInput>
private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputSig {
class SummaryTargetApi extends Callable {
private Callable lift;
@@ -72,74 +119,13 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
predicate isRelevant() { relevant(this) }
}
predicate isRelevantType(Type t) { any() }
Callable getAsExprEnclosingCallable(NodeExtended node) { result = node.asExpr().getScope() }
/**
* Gets the underlying type of the content `c`.
*/
Type getUnderlyingContentType(DataFlow::ContentSet c) { result = any(Type t) and exists(c) }
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
string qualifierString() { result = "Argument[self]" }
predicate isUninterestingForDataFlowModels(Callable api) { none() }
string parameterAccess(R::ParamBase p) {
result = "Argument[" + any(ParameterPosition pos | p = pos.getParameterIn(_)).toString() + "]"
}
string parameterContentAccess(R::ParamBase p) { result = parameterAccess(p) }
class InstanceParameterNode extends DataFlow::ParameterNode {
InstanceParameterNode() { this.asParameter() instanceof SelfParam }
}
bindingset[c]
string paramReturnNodeAsOutput(Callable c, ParameterPosition pos) {
result = paramReturnNodeAsContentOutput(c, pos)
}
bindingset[c]
string paramReturnNodeAsContentOutput(Callable c, ParameterPosition pos) {
result = parameterContentAccess(c.getParamList().getParam(pos.getPosition()))
or
pos.isSelf() and result = qualifierString()
}
Callable returnNodeEnclosingCallable(DataFlow::Node ret) {
result = ret.(Node::Node).getEnclosingCallable().asCfgScope()
}
predicate isOwnInstanceAccessNode(RustDataFlow::ReturnNode node) {
// This is probably not relevant to implement for Rust, as we only use
// `captureMixedFlow` which doesn't explicitly distinguish between
// functions that return `self` and those that don't.
none()
}
predicate sinkModelSanitizer(DataFlow::Node node) { none() }
/**
* Holds if `source` is an API entrypoint, i.e., a source of input where data
* can flow in to a library. This is used for creating sink models, as we
* only want to mark functions as sinks if input to the function can reach
* (from an input source) a known sink.
*/
predicate apiSource(DataFlow::Node source) { source instanceof DataFlow::ParameterNode }
bindingset[sourceEnclosing, api]
predicate irrelevantSourceSinkApi(Callable sourceEnclosing, SourceTargetApi api) { none() }
string getInputArgument(DataFlow::Node source) {
result = "Argument[" + source.(Node::SourceParameterNode).getPosition().toString() + "]"
}
bindingset[kind]
predicate isRelevantSinkKind(string kind) { any() }
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
predicate containerContent(DataFlow::ContentSet c) {
c.(SingletonContentSet).getContent() instanceof ElementContent
}
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }
predicate isAdditionalContentFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { none() }
@@ -159,7 +145,7 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
private string encodeContent(ContentSet cs, string arg) {
result = FlowSummary::Input::encodeContent(cs, arg)
or
exists(Content c | cs = TSingletonContentSet(c) |
exists(Content c | cs = DataFlowImpl::TSingletonContentSet(c) |
exists(int pos |
pos = c.(FunctionCallArgumentContent).getPosition() and
result = "Parameter" and
@@ -176,18 +162,47 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
if arg = "" then result = name else result = name + "[" + arg + "]"
)
}
}
string partialModelRow(Callable api, int i) {
i = 0 and result = api.(Function).getCrateOrigin() // crate
or
i = 1 and result = api.(Function).getExtendedCanonicalPath() // name
private module SourceModelGeneratorInput implements SourceModelGeneratorInputSig {
class SourceTargetApi extends Callable {
SourceTargetApi() { relevant(this) }
}
string partialNeutralModelRow(Callable api, int i) { result = partialModelRow(api, i) }
bindingset[sourceEnclosing, api]
predicate irrelevantSourceSinkApi(Callable sourceEnclosing, SourceTargetApi api) { none() }
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
predicate sourceNode(DataFlow::Node node, string kind) { FlowSource::sourceNode(node, kind) }
}
private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
class SinkTargetApi extends Callable {
SinkTargetApi() { relevant(this) }
}
predicate sinkModelSanitizer(DataFlow::Node node) { none() }
/**
* Holds if `source` is an API entrypoint, i.e., a source of input where data
* can flow in to a library. This is used for creating sink models, as we
* only want to mark functions as sinks if input to the function can reach
* (from an input source) a known sink.
*/
predicate apiSource(DataFlow::Node source) { source instanceof DataFlow::ParameterNode }
string getInputArgument(DataFlow::Node source) {
result = "Argument[" + source.(Node::SourceParameterNode).getPosition().toString() + "]"
}
bindingset[kind]
predicate isRelevantSinkKind(string kind) { any() }
predicate sinkNode(DataFlow::Node node, string kind) { FlowSink::sinkNode(node, kind) }
}
import MakeModelGenerator<Location, RustDataFlow, RustTaintTracking, ModelGeneratorInput>
import MakeSummaryModelGenerator<SummaryModelGeneratorInput> as SummaryModels
import MakeSourceModelGenerator<SourceModelGeneratorInput> as SourceModels
import MakeSinkModelGenerator<SinkModelGeneratorInput> as SinkModels

View File

@@ -1,6 +1,6 @@
private import rust as R
private import codeql.mad.modelgenerator.internal.ModelPrinting
private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput
private import CaptureModels::ModelGeneratorCommonInput as ModelGeneratorInput
private module ModelPrintingLang implements ModelPrintingLangSig {
class Callable = R::Callable;

View File

@@ -1,5 +1,6 @@
import rust
import utils.modelgenerator.internal.CaptureModels
import SinkModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import rust
import utils.modelgenerator.internal.CaptureModels
import SourceModels
import utils.test.InlineMadTest
import codeql.rust.dataflow.internal.ModelsAsData

View File

@@ -1,5 +1,6 @@
import rust
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {