Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions rust/ql/lib/codeql/rust/dataflow/DataFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ module DataFlow {
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
final class ParameterNode extends Node instanceof Node::SourceParameterNode {
/** Gets the parameter that this node corresponds to. */
ParamBase getParameter() { result = super.getParameter().getParamBase() }
}
final class ParameterNode extends Node instanceof Node::SourceParameterNode { }

final class PostUpdateNode = Node::PostUpdateNodePublic;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ module Node {
*/
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.
*/
Expand Down Expand Up @@ -274,6 +279,7 @@ module Node {
* 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);
}

Expand Down Expand Up @@ -794,7 +800,7 @@ private class VariantRecordFieldContent extends VariantContent, TVariantRecordFi
}

/** Content stored in a field on a struct. */
private class StructFieldContent extends Content, TStructFieldContent {
class StructFieldContent extends Content, TStructFieldContent {
private Struct s;
private string field_;

Expand Down
21 changes: 21 additions & 0 deletions rust/ql/lib/utils/test/InlineMadTest.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
private import rust as R

Check warning on line 1 in rust/ql/lib/utils/test/InlineMadTest.qll

View workflow job for this annotation

GitHub Actions / qldoc

Missing QLdoc for file InlineMadTest
private import codeql.mad.test.InlineMadTest

private module InlineMadTestLang implements InlineMadTestLangSig {
class Callable = R::Function;

string getComment(R::Function callable) {
exists(R::Comment comment |
result = comment.getCommentText() and
comment.getLocation().getFile() = callable.getLocation().getFile() and
// When a function is preceded by comments its start line is the line of
// the first comment. Hence all relevant comments are found by including
// comments from the start line and up to the line with the function
// name.
callable.getLocation().getStartLine() <= comment.getLocation().getStartLine() and
comment.getLocation().getStartLine() <= callable.getName().getLocation().getStartLine()
)
}
}

import InlineMadTestImpl<InlineMadTestLang>
13 changes: 13 additions & 0 deletions rust/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @name Capture content based summary models.
* @description Finds applicable content based summary models to be used by other queries.
* @kind diagnostic
* @id rust/utils/modelgenerator/contentbased-summary-models
* @tags modelgenerator
*/

import internal.CaptureModels

from DataFlowSummaryTargetApi api, string flow
where flow = ContentSensitive::captureFlow(api, _)
select flow order by flow
13 changes: 13 additions & 0 deletions rust/ql/src/utils/modelgenerator/CaptureMixedNeutralModels.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @name Capture mixed neutral models.
* @description Finds neutral models to be used by other queries.
* @kind diagnostic
* @id rust/utils/modelgenerator/mixed-neutral-models
* @tags modelgenerator
*/

import internal.CaptureModels

from DataFlowSummaryTargetApi api, string noflow
where noflow = captureMixedNeutral(api)
select noflow order by noflow
13 changes: 13 additions & 0 deletions rust/ql/src/utils/modelgenerator/CaptureMixedSummaryModels.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we'll want this query, I hope that CaptureContentSummaryModels.ql should be enough for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong feelings but I'd be inclined to leave the files in places because:
1/ The shared Python script assumes that these files are there.
2/ For consistency with C# and Java
3/ We might never have to ever use them, but perhaps it could come in handy for debugging or experimenting through the generation script.

* @name Capture mixed summary models.
* @description Finds applicable summary models to be used by other queries.
* @kind diagnostic
* @id rust/utils/modelgenerator/mixed-summary-models
* @tags modelgenerator
*/

import internal.CaptureModels

from DataFlowSummaryTargetApi api, string flow
where flow = captureMixedFlow(api, _)
select flow order by flow
13 changes: 13 additions & 0 deletions rust/ql/src/utils/modelgenerator/CaptureNeutralModels.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @name Capture neutral models.
* @description Finds neutral models to be used by other queries.
* @kind diagnostic
* @id rust/utils/modelgenerator/neutral-models
* @tags modelgenerator
*/

import internal.CaptureModels

from DataFlowSummaryTargetApi api, string noflow
where noflow = captureNoFlow(api)
select noflow order by noflow
13 changes: 13 additions & 0 deletions rust/ql/src/utils/modelgenerator/CaptureSinkModels.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @name Capture sink models.
* @description Finds public methods that act as sinks as they flow into a known sink.
* @kind diagnostic
* @id rust/utils/modelgenerator/sink-models
* @tags modelgenerator
*/

import internal.CaptureModels

from DataFlowSinkTargetApi api, string sink
where sink = captureSink(api)
select sink order by sink
13 changes: 13 additions & 0 deletions rust/ql/src/utils/modelgenerator/CaptureSourceModels.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @name Capture source models.
* @description Finds APIs that act as sources as they expose already known sources.
* @kind diagnostic
* @id rust/utils/modelgenerator/source-models
* @tags modelgenerator
*/

import internal.CaptureModels

from DataFlowSourceTargetApi api, string source
where source = captureSource(api)
select source order by source
13 changes: 13 additions & 0 deletions rust/ql/src/utils/modelgenerator/CaptureSummaryModels.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment.

* @name Capture summary models.
* @description Finds applicable summary models to be used by other queries.
* @kind diagnostic
* @id rust/utils/modelgenerator/summary-models
* @tags modelgenerator
*/

import internal.CaptureModels

from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api)
select flow order by flow
15 changes: 15 additions & 0 deletions rust/ql/src/utils/modelgenerator/GenerateFlowModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/python3

import sys
import os.path
import subprocess

# Add Model as Data script directory to sys.path.
gitroot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
madpath = os.path.join(gitroot, "misc/scripts/models-as-data/")
sys.path.append(madpath)

import generate_flow_model as model

language = "rust"
model.Generator.make(language).run()
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @name Capture Summary Models Partial Path
* @description Capture Summary Models Partial Path
* @kind path-problem
* @precision low
* @id rust/utils/modelgenerator/summary-models-partial-path
* @severity info
* @tags modelgenerator
*/

private import codeql.rust.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import PartialFlow::PartialPathGraph

int explorationLimit() { result = 3 }

module PartialFlow = PropagateFlow::FlowExplorationFwd<explorationLimit/0>;

from
PartialFlow::PartialPathNode source, PartialFlow::PartialPathNode sink,
DataFlowSummaryTargetApi api, DataFlow::ParameterNode p
where
PartialFlow::partialFlow(source, sink, _) and
p = source.getNode() and
p.asParameter() = api.getParamList().getAParamBase()
select sink.getNode(), source, sink, "There is flow from a $@ to $@.", source.getNode(),
"parameter", sink.getNode(), "intermediate value"

Check warning

Code scanning / CodeQL

Alert message style violation Warning

Don't repeat the alert location as a link.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are debug queries copy pasted from C#, so I'm inclined to ignore this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't worry about this too much, assuming we're not expecting users to use these queries directly. We might want to make it clearer what these queries are for though (e.g. in the @description) in case users do find them?

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @name Capture Summary Models Path
* @description Capture Summary Models Path
* @kind path-problem
* @precision low
* @id rust/utils/modelgenerator/summary-models-path
* @severity warning
* @tags modelgenerator
*/

private import codeql.rust.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import PropagateFlow::PathGraph

from
PropagateFlow::PathNode source, PropagateFlow::PathNode sink, DataFlowSummaryTargetApi api,
DataFlow::Node p, DataFlow::Node returnNodeExt
where
PropagateFlow::flowPath(source, sink) and
p = source.getNode() and
returnNodeExt = sink.getNode() and
exists(captureThroughFlow0(api, p, returnNodeExt))
select sink.getNode(), source, sink, "There is flow from $@ to the $@.", source.getNode(),
"parameter", sink.getNode(), "return value"

Check warning

Code scanning / CodeQL

Alert message style violation Warning

Don't repeat the alert location as a link.
1 change: 1 addition & 0 deletions rust/ql/src/utils/modelgenerator/debug/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The queries in this directory are purely used for model generator debugging purposes in VS Code.
162 changes: 162 additions & 0 deletions rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
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.TaintTrackingImpl
private import codeql.mad.modelgenerator.internal.ModelGeneratorImpl
private import codeql.rust.dataflow.internal.FlowSummaryImpl as FlowSummary

module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataFlow> {
// NOTE: We are not using type information for now.
class Type = Unit;

class Parameter = R::ParamBase;

class Callable = R::Callable;

class NodeExtended extends DataFlow::Node {
Callable getAsExprEnclosingCallable() { result = this.asExpr().getScope() }

Type getType() { any() }

Callable getEnclosingCallable() {
result = this.(Node::Node).getEnclosingCallable().asCfgScope()
}
}

private predicate relevant(Function api) {
// 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)
}

predicate isUninterestingForDataFlowModels(Callable api) { none() }

predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }

class SourceOrSinkTargetApi extends Callable {
SourceOrSinkTargetApi() { relevant(this) }
}

class SinkTargetApi extends SourceOrSinkTargetApi { }

class SourceTargetApi extends SourceOrSinkTargetApi { }

class SummaryTargetApi extends Callable {
private Callable lift;

SummaryTargetApi() {
lift = this and
relevant(this)
}

Callable lift() { result = lift }

predicate isRelevant() { relevant(this) }
}

predicate isRelevantType(Type t) { any() }

/**
* Gets the underlying type of the content `c`.
*/
Type getUnderlyingContentType(DataFlow::ContentSet c) { result = any(Type t) and exists(c) }

Check warning

Code scanning / CodeQL

Redundant cast Warning

Redundant cast to
Unit
.

string qualifierString() { result = "Argument[self]" }

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(R::Callable c, ParameterPosition pos) {
// TODO: Implement this to support returning through parameters.
result = "paramReturnNodeAsOutput(" + c + ", " + pos + ")"
}

bindingset[c]
string paramReturnNodeAsContentOutput(Callable c, ParameterPosition pos) {
// TODO: Implement this to support returning through parameters.
result = "paramReturnNodeAsContentOutput(" + c + ", " + pos + ")"
}

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() }

predicate apiSource(DataFlow::Node source) { none() }

bindingset[sourceEnclosing, api]
predicate irrelevantSourceSinkApi(Callable sourceEnclosing, SourceTargetApi api) { none() }

string getInputArgument(DataFlow::Node source) {
// TODO: Implement when we want to generate sources and sinks
result = "getInputArgument(" + source + ")"
}

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 isAdditionalContentFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { none() }

predicate isField(DataFlow::ContentSet c) {
c.(SingletonContentSet).getContent() instanceof StructFieldContent
}

predicate isCallback(DataFlow::ContentSet c) { none() }

string getSyntheticName(DataFlow::ContentSet c) { none() }

string printContent(DataFlow::ContentSet cs) {
exists(string arg | result = FlowSummary::Input::encodeContent(cs, arg) + "[" + arg + "]")
}

string partialModelRow(Callable api, int i) {
i = 0 and
(
result = api.(Function).getCrateOrigin()
or
not api.(Function).hasCrateOrigin() and result = ""
) // crate
or
i = 1 and result = api.(Function).getExtendedCanonicalPath() // name
}

string partialNeutralModelRow(Callable api, int i) { result = partialModelRow(api, i) }

// TODO: Implement this when we want to generate sources.
predicate sourceNode(DataFlow::Node node, string kind) { none() }

// TODO: Implement this when we want to generate sinks.
predicate sinkNode(DataFlow::Node node, string kind) { none() }
}

import MakeModelGenerator<Location, RustDataFlow, RustTaintTracking, ModelGeneratorInput>
Loading
Loading