Merge branch 'main' into identity-consistency-check

This commit is contained in:
Mathias Vorreiter Pedersen
2023-05-03 22:01:06 +01:00
555 changed files with 21638 additions and 9738 deletions

View File

@@ -1,3 +1,7 @@
## 1.5.1
No user-facing changes.
## 1.5.0
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.0
lastReleaseVersion: 1.5.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.5.1-dev
version: 1.5.2-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,7 @@
## 1.5.1
No user-facing changes.
## 1.5.0
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.0
lastReleaseVersion: 1.5.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.5.1-dev
version: 1.5.2-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,7 @@
## 0.6.1
No user-facing changes.
## 0.6.0
### Deprecated APIs

View File

@@ -0,0 +1,3 @@
## 0.6.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.0
lastReleaseVersion: 0.6.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 0.6.1-dev
version: 0.6.2-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -12,9 +12,9 @@ class Attribute extends Element, @cil_attribute {
Method getConstructor() { cil_attribute(this, _, result) }
/** Gets the type of this attribute. */
Type getType() { result = getConstructor().getDeclaringType() }
Type getType() { result = this.getConstructor().getDeclaringType() }
override string toString() { result = "[" + getType().getName() + "(...)]" }
override string toString() { result = "[" + this.getType().getName() + "(...)]" }
/** Gets the value of the `i`th argument of this attribute. */
string getArgument(int i) { cil_attribute_positional_argument(this, i, result) }
@@ -23,9 +23,9 @@ class Attribute extends Element, @cil_attribute {
string getNamedArgument(string name) { cil_attribute_named_argument(this, name, result) }
/** Gets an argument of this attribute, if any. */
string getAnArgument() { result = getArgument(_) or result = getNamedArgument(_) }
string getAnArgument() { result = this.getArgument(_) or result = this.getNamedArgument(_) }
override CS::Location getLocation() { result = getDeclaration().getLocation() }
override CS::Location getLocation() { result = this.getDeclaration().getLocation() }
}
/** A generic attribute to a declaration. */

View File

@@ -1,156 +1,183 @@
/**
* Provides classes for performing global (inter-procedural)
* content-sensitive data flow analyses.
*
* Unlike `DataFlow::Global`, we allow for data to be stored (possibly nested) inside
* contents of sources and sinks.
* We track flow paths of the form
*
* ```
* source --value-->* node
* (--read--> node --value-->* node)*
* --(non-value|value)-->* node
* (--store--> node --value-->* node)*
* --value-->* sink
* ```
*
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
* step, `--store-->` is a store step, and `--(non-value)-->` is a
* non-value-preserving flow step.
*
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
* steps, followed by 0 or more stores, with value-preserving steps allowed in
* between all other steps.
*/
private import csharp
private import codeql.util.Boolean
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Private as DataFlowPrivate
module ContentDataFlow {
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Private as DataFlowPrivate
private import DataFlowImplForContentDataFlow as DF
class Node = DF::Node;
class FlowFeature = DF::FlowFeature;
class ContentSet = DF::ContentSet;
// predicate stageStats = DF::stageStats/8;
/**
* An input configuration for content data flow.
*/
signature module ConfigSig {
/**
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
* ```ql
* class MyAnalysisConfiguration extends ContentDataFlowConfiguration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isBarrier`.
* // Optionally override `isAdditionalFlowStep`.
* // Optionally override `getAFeature`.
* // Optionally override `accessPathLimit`.
* // Optionally override `isRelevantContent`.
* }
* ```
*
* Unlike `DataFlow::Configuration` (on which this class is based), we allow
* for data to be stored (possibly nested) inside contents of sources and sinks.
* We track flow paths of the form
*
* ```
* source --value-->* node
* (--read--> node --value-->* node)*
* --(non-value|value)-->* node
* (--store--> node --value-->* node)*
* --value-->* sink
* ```
*
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
* step, `--store-->` is a store step, and `--(non-value)-->` is a
* non-value-preserving flow step.
*
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
* steps, followed by 0 or more stores, with value-preserving steps allowed in
* between all other steps.
* Holds if `source` is a relevant data flow source.
*/
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
predicate isSource(DataFlow::Node source);
/**
* Holds if `source` is a relevant data flow source.
*/
abstract predicate isSource(Node source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(DataFlow::Node sink);
/**
* Holds if `sink` is a relevant data flow sink.
*/
abstract predicate isSink(Node sink);
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
default predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrier(DataFlow::Node node) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrier(Node node) { none() }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*/
default DataFlow::FlowFeature getAFeature() { none() }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*/
FlowFeature getAFeature() { none() }
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
default int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
default predicate isRelevantContent(DataFlow::ContentSet c) { any() }
}
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
predicate isRelevantContent(ContentSet c) { any() }
/**
* Constructs a global content data flow computation.
*/
module Global<ConfigSig ContentConfig> {
private module FlowConfig implements DataFlow::StateConfigSig {
class FlowState = State;
/**
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
* for this configuration. `preservesValue` indicates whether any of the additional
* flow steps defined by `isAdditionalFlowStep` are needed.
*
* For the source access path, `sourceAp`, the top of the stack represents the content
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow from `source.Field2.Field1`.
*
* For the sink access path, `sinkAp`, the top of the stack represents the content
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
*/
final predicate hasFlow(
Node source, AccessPath sourceAp, Node sink, AccessPath sinkAp, boolean preservesValue
) {
exists(DF::PathNode pathSource, DF::PathNode pathSink |
this.(ConfigurationAdapter).hasFlowPath(pathSource, pathSink) and
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
source = pathSource.getNode() and
sink = pathSink.getNode()
|
pathSink.getState().(InitState).decode(preservesValue)
or
pathSink.getState().(ReadState).decode(_, preservesValue)
or
pathSink.getState().(StoreState).decode(_, preservesValue)
predicate isSource(DataFlow::Node source, FlowState state) {
ContentConfig::isSource(source) and
state.(InitState).decode(true)
}
predicate isSink(DataFlow::Node sink, FlowState state) {
ContentConfig::isSink(sink) and
(
state instanceof InitState or
state instanceof StoreState or
state instanceof ReadState
)
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
storeStep(node1, state1, _, node2, state2) or
readStep(node1, state1, _, node2, state2) or
additionalStep(node1, state1, node2, state2)
}
predicate isAdditionalFlowStep = ContentConfig::isAdditionalFlowStep/2;
predicate isBarrier = ContentConfig::isBarrier/1;
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
DataFlow::FlowFeature getAFeature() { result = ContentConfig::getAFeature() }
// needed to record reads/stores inside summarized callables
predicate includeHiddenNodes() { any() }
}
private module Flow = DataFlow::GlobalWithState<FlowConfig>;
/**
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
* for this configuration. `preservesValue` indicates whether any of the additional
* flow steps defined by `isAdditionalFlowStep` are needed.
*
* For the source access path, `sourceAp`, the top of the stack represents the content
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow from `source.Field2.Field1`.
*
* For the sink access path, `sinkAp`, the top of the stack represents the content
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
*/
predicate flow(
DataFlow::Node source, AccessPath sourceAp, DataFlow::Node sink, AccessPath sinkAp,
boolean preservesValue
) {
exists(Flow::PathNode pathSource, Flow::PathNode pathSink |
Flow::flowPath(pathSource, pathSink) and
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
source = pathSource.getNode() and
sink = pathSink.getNode()
|
pathSink.getState().(InitState).decode(preservesValue)
or
pathSink.getState().(ReadState).decode(_, preservesValue)
or
pathSink.getState().(StoreState).decode(_, preservesValue)
)
}
private newtype TState =
TInitState(Boolean preservesValue) or
TStoreState(int size, Boolean preservesValue) {
size in [1 .. ContentConfig::accessPathLimit()]
} or
TReadState(int size, Boolean preservesValue) { size in [1 .. ContentConfig::accessPathLimit()] }
abstract private class State extends TState {
abstract string toString();
}
/** A flow state representing no reads or stores. */
private class InitState extends DF::FlowState {
private class InitState extends State, TInitState {
private boolean preservesValue_;
InitState() { this = "Init(" + preservesValue_ + ")" and preservesValue_ in [false, true] }
InitState() { this = TInitState(preservesValue_) }
override string toString() { result = "Init(" + preservesValue_ + ")" }
predicate decode(boolean preservesValue) { preservesValue = preservesValue_ }
}
/** A flow state representing that content has been stored into. */
private class StoreState extends DF::FlowState {
private class StoreState extends State, TStoreState {
private boolean preservesValue_;
private int size_;
StoreState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "StoreState(" + size_ + "," + preservesValue_ + ")"
}
StoreState() { this = TStoreState(size_, preservesValue_) }
override string toString() { result = "StoreState(" + size_ + "," + preservesValue_ + ")" }
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
@@ -158,15 +185,13 @@ module ContentDataFlow {
}
/** A flow state representing that content has been read from. */
private class ReadState extends DF::FlowState {
private class ReadState extends State, TReadState {
private boolean preservesValue_;
private int size_;
ReadState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "ReadState(" + size_ + "," + preservesValue_ + ")"
}
ReadState() { this = TReadState(size_, preservesValue_) }
override string toString() { result = "ReadState(" + size_ + "," + preservesValue_ + ")" }
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
@@ -174,12 +199,12 @@ module ContentDataFlow {
}
private predicate storeStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, StoreState state2,
Configuration config
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
StoreState state2
) {
exists(boolean preservesValue, int size |
storeSet(node1, c, node2, _, _) and
config.isRelevantContent(c) and
ContentConfig::isRelevantContent(c) and
state2.decode(size + 1, preservesValue)
|
state1.(InitState).decode(preservesValue) and size = 0
@@ -191,12 +216,12 @@ module ContentDataFlow {
}
private predicate readStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, ReadState state2,
Configuration config
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
ReadState state2
) {
exists(int size |
readSet(node1, c, node2) and
config.isRelevantContent(c) and
ContentConfig::isRelevantContent(c) and
state2.decode(size + 1, true)
|
state1.(InitState).decode(true) and
@@ -207,9 +232,9 @@ module ContentDataFlow {
}
private predicate additionalStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2, Configuration config
DataFlow::Node node1, State state1, DataFlow::Node node2, State state2
) {
config.isAdditionalFlowStep(node1, node2) and
ContentConfig::isAdditionalFlowStep(node1, node2) and
(
state1 instanceof InitState and
state2.(InitState).decode(false)
@@ -221,40 +246,9 @@ module ContentDataFlow {
)
}
private class ConfigurationAdapter extends DF::Configuration instanceof Configuration {
final override predicate isSource(Node source, DF::FlowState state) {
Configuration.super.isSource(source) and
state.(InitState).decode(true)
}
final override predicate isSink(Node sink, DF::FlowState state) {
Configuration.super.isSink(sink) and
(
state instanceof InitState or
state instanceof StoreState or
state instanceof ReadState
)
}
final override predicate isAdditionalFlowStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2
) {
storeStep(node1, state1, _, node2, state2, this) or
readStep(node1, state1, _, node2, state2, this) or
additionalStep(node1, state1, node2, state2, this)
}
final override predicate isBarrier(Node node) { Configuration.super.isBarrier(node) }
final override FlowFeature getAFeature() { result = Configuration.super.getAFeature() }
// needed to record reads/stores inside summarized callables
final override predicate includeHiddenNodes() { any() }
}
private newtype TAccessPath =
TAccessPathNil() or
TAccessPathCons(ContentSet head, AccessPath tail) {
TAccessPathCons(DataFlow::ContentSet head, AccessPath tail) {
nodeReachesStore(_, _, _, _, head, _, tail)
or
nodeReachesRead(_, _, _, _, head, tail, _)
@@ -263,7 +257,7 @@ module ContentDataFlow {
/** An access path. */
class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */
ContentSet getHead() { this = TAccessPathCons(result, _) }
DataFlow::ContentSet getHead() { this = TAccessPathCons(result, _) }
/** Gets the tail of this access path, if any. */
AccessPath getTail() { this = TAccessPathCons(_, result) }
@@ -278,7 +272,7 @@ module ContentDataFlow {
this = TAccessPathNil() and
result = ""
or
exists(ContentSet head, AccessPath tail |
exists(DataFlow::ContentSet head, AccessPath tail |
this = TAccessPathCons(head, tail) and
result = head + "." + tail
)
@@ -287,7 +281,7 @@ module ContentDataFlow {
// important to use `edges` and not `PathNode::getASuccessor()`, as the latter
// is not pruned for reachability
private predicate pathSucc = DF::PathGraph::edges/2;
private predicate pathSucc = Flow::PathGraph::edges/2;
/**
* Provides a big-step flow relation, where flow stops at read/store steps that
@@ -295,10 +289,10 @@ module ContentDataFlow {
* summarized callables can be recorded as well.
*/
private module BigStepFlow {
private predicate reachesSink(DF::PathNode node) {
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
private predicate reachesSink(Flow::PathNode node) {
FlowConfig::isSink(node.getNode(), node.getState())
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
pathSucc(node, mid) and
reachesSink(mid)
)
@@ -309,76 +303,72 @@ module ContentDataFlow {
* in the big-step relation.
*/
pragma[nomagic]
private predicate excludeStep(DF::PathNode pred, DF::PathNode succ) {
private predicate excludeStep(Flow::PathNode pred, Flow::PathNode succ) {
pathSucc(pred, succ) and
(
// we need to record reads/stores inside summarized callables
DF::PathGraph::subpaths(pred, _, _, succ)
Flow::PathGraph::subpaths(pred, _, _, succ)
or
// only allow flow into a summarized callable, as part of the big-step
// relation, when flow can reach a sink without going back out
DF::PathGraph::subpaths(pred, succ, _, _) and
Flow::PathGraph::subpaths(pred, succ, _, _) and
not reachesSink(succ)
or
// needed to record store steps
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
or
// needed to record read steps
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
)
}
pragma[nomagic]
private DataFlowCallable getEnclosingCallableImpl(DF::PathNode node) {
private DataFlowCallable getEnclosingCallableImpl(Flow::PathNode node) {
result = getNodeEnclosingCallable(node.getNode())
}
pragma[inline]
private DataFlowCallable getEnclosingCallable(DF::PathNode node) {
private DataFlowCallable getEnclosingCallable(Flow::PathNode node) {
pragma[only_bind_into](result) = getEnclosingCallableImpl(pragma[only_bind_out](node))
}
pragma[nomagic]
private predicate bigStepEntry(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
private predicate bigStepEntry(Flow::PathNode node) {
(
any(ConfigurationAdapter config).isSource(node.getNode(), node.getState())
FlowConfig::isSource(node.getNode(), node.getState())
or
excludeStep(_, node)
or
DF::PathGraph::subpaths(_, node, _, _)
Flow::PathGraph::subpaths(_, node, _, _)
)
}
pragma[nomagic]
private predicate bigStepExit(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
private predicate bigStepExit(Flow::PathNode node) {
(
bigStepEntry(node)
or
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
FlowConfig::isSink(node.getNode(), node.getState())
or
excludeStep(node, _)
or
DF::PathGraph::subpaths(_, _, node, _)
Flow::PathGraph::subpaths(_, _, node, _)
)
}
pragma[nomagic]
private predicate step(DF::PathNode pred, DF::PathNode succ) {
private predicate step(Flow::PathNode pred, Flow::PathNode succ) {
pathSucc(pred, succ) and
not excludeStep(pred, succ)
}
pragma[nomagic]
private predicate stepRec(DF::PathNode pred, DF::PathNode succ) {
private predicate stepRec(Flow::PathNode pred, Flow::PathNode succ) {
step(pred, succ) and
not bigStepEntry(pred)
}
private predicate stepRecPlus(DF::PathNode n1, DF::PathNode n2) = fastTC(stepRec/2)(n1, n2)
private predicate stepRecPlus(Flow::PathNode n1, Flow::PathNode n2) = fastTC(stepRec/2)(n1, n2)
/**
* Holds if there is flow `pathSucc+(pred) = succ`, and such a flow path does
@@ -386,8 +376,8 @@ module ContentDataFlow {
* steps.
*/
pragma[nomagic]
private predicate bigStep(DF::PathNode pred, DF::PathNode succ) {
exists(DF::PathNode mid |
private predicate bigStep(Flow::PathNode pred, Flow::PathNode succ) {
exists(Flow::PathNode mid |
bigStepEntry(pred) and
step(pred, mid)
|
@@ -399,13 +389,13 @@ module ContentDataFlow {
}
pragma[nomagic]
predicate bigStepNotLocal(DF::PathNode pred, DF::PathNode succ) {
predicate bigStepNotLocal(Flow::PathNode pred, Flow::PathNode succ) {
bigStep(pred, succ) and
not getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
pragma[nomagic]
predicate bigStepMaybeLocal(DF::PathNode pred, DF::PathNode succ) {
predicate bigStepMaybeLocal(Flow::PathNode pred, Flow::PathNode succ) {
bigStep(pred, succ) and
getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
@@ -422,55 +412,54 @@ module ContentDataFlow {
*/
pragma[nomagic]
private predicate nodeReaches(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node,
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
AccessPath reads, AccessPath stores
) {
exists(ConfigurationAdapter config |
node = source and
reads = scReads and
stores = scStores
|
config.hasFlowPath(source, _) and
node = source and
reads = scReads and
stores = scStores and
(
Flow::flowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
or
// the argument in a sub path can be reached, so we start flow from the sub path
// parameter, while recording the read/store summary context
exists(DF::PathNode arg |
exists(Flow::PathNode arg |
nodeReachesSubpathArg(_, _, _, arg, scReads, scStores) and
DF::PathGraph::subpaths(arg, source, _, _)
Flow::PathGraph::subpaths(arg, source, _, _)
)
)
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepMaybeLocal(mid, node)
)
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepNotLocal(mid, node) and
// when flow is not local, we cannot flow back out, so we may stop
// flow early when computing summary flow
any(ConfigurationAdapter config).hasFlowPath(source, _) and
Flow::flowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
)
or
// store step
exists(AccessPath storesMid, ContentSet c |
exists(AccessPath storesMid, DataFlow::ContentSet c |
nodeReachesStore(source, scReads, scStores, node, c, reads, storesMid) and
stores = TAccessPathCons(c, storesMid)
)
or
// read step
exists(AccessPath readsMid, ContentSet c |
exists(AccessPath readsMid, DataFlow::ContentSet c |
nodeReachesRead(source, scReads, scStores, node, c, readsMid, stores) and
reads = TAccessPathCons(c, readsMid)
)
or
// flow-through step; match outer stores/reads with inner store/read summary contexts
exists(DF::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
exists(Flow::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
nodeReachesSubpathArg(source, scReads, scStores, mid, innerScReads, innerScStores) and
subpathArgReachesOut(mid, innerScReads, innerScStores, node, reads, stores)
)
@@ -478,47 +467,45 @@ module ContentDataFlow {
pragma[nomagic]
private predicate nodeReachesStore(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesRead(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesSubpathArg(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode arg,
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode arg,
AccessPath reads, AccessPath stores
) {
nodeReaches(source, scReads, scStores, arg, reads, stores) and
DF::PathGraph::subpaths(arg, _, _, _)
Flow::PathGraph::subpaths(arg, _, _, _)
}
pragma[nomagic]
private predicate subpathArgReachesOut(
DF::PathNode arg, AccessPath scReads, AccessPath scStores, DF::PathNode out, AccessPath reads,
AccessPath stores
Flow::PathNode arg, AccessPath scReads, AccessPath scStores, Flow::PathNode out,
AccessPath reads, AccessPath stores
) {
exists(DF::PathNode source, DF::PathNode ret |
exists(Flow::PathNode source, Flow::PathNode ret |
nodeReaches(source, scReads, scStores, ret, reads, stores) and
DF::PathGraph::subpaths(arg, source, ret, out)
Flow::PathGraph::subpaths(arg, source, ret, out)
)
}
}

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -1,398 +0,0 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
*/
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
import FlowStateString
private import codeql.util.Unit
/**
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
* ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isBarrier`.
* // Optionally override `isAdditionalFlowStep`.
* }
* ```
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
* the edges are those data-flow steps that preserve the value of the node
* along with any additional edges defined by `isAdditionalFlowStep`.
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
* and/or out-going edges from those nodes, respectively.
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but two classes extending
* `DataFlow::Configuration` should never depend on each other. One of them
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant data flow source.
*/
predicate isSource(Node source) { none() }
/**
* Holds if `source` is a relevant data flow source with the given initial
* `state`.
*/
predicate isSource(Node source, FlowState state) { none() }
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(Node sink) { none() }
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
* `node` from the data flow graph.
*/
predicate isBarrier(Node node) { none() }
/**
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
/**
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited.
*/
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be
* taken at `node`.
*/
predicate allowImplicitRead(Node node, ContentSet c) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
int fieldFlowBranchLimit() { result = 2 }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*
* These features are generally not relevant for typical end-to-end data flow
* queries, but should only be used for constructing paths that need to
* somehow be pluggable in another path context.
*/
FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
*/
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
*/
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
/**
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
*
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
deprecated int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
}
/**
* This class exists to prevent mutual recursion between the user-overridden
* member predicates of `Configuration` and the rest of the data-flow library.
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
override predicate hasFlow(Node source, Node sink) {
strictcount(Node n | this.isSource(n)) < 0
or
strictcount(Node n | this.isSource(n, _)) < 0
or
strictcount(Node n | this.isSink(n)) < 0
or
strictcount(Node n | this.isSink(n, _)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
or
super.hasFlow(source, sink)
}
}
/** A bridge class to access the deprecated `isBarrierGuard`. */
private class BarrierGuardGuardedNodeBridge extends Unit {
abstract predicate guardedNode(Node n, Configuration config);
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
}
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
deprecated override predicate guardedNode(Node n, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g) and
n = g.getAGuardedNode()
)
}
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
}
}
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
config.isAdditionalFlowStep(_, result, _, _) or
config.isAdditionalFlowStep(_, _, _, result)
}
private newtype TConfigState =
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
getConfig(state).isSource(source, getState(state))
or
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
}
predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state))
or
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
}
predicate isBarrier(Node node) { none() }
predicate isBarrier(Node node, FlowState state) {
getConfig(state).isBarrier(node, getState(state)) or
getConfig(state).isBarrier(node) or
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
}
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {
any(Configuration config).allowImplicitRead(node, c)
}
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
predicate sourceGrouping(Node source, string sourceGroup) {
any(Configuration config).sourceGrouping(source, sourceGroup)
}
predicate sinkGrouping(Node sink, string sinkGroup) {
any(Configuration config).sinkGrouping(sink, sinkGroup)
}
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
final string toStringWithContext() { result = super.toStringWithContext() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
final predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
/** Holds if this node is a grouping of source nodes. */
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
/** Holds if this node is a grouping of sink nodes. */
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
sink0.getNode() = sink
)
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
predicate flowsTo = hasFlow/3;

View File

@@ -23,11 +23,15 @@ abstract class Sink extends DataFlow::Node { }
*/
abstract private class InstanceMethodSink extends Sink {
InstanceMethodSink() {
not exists(
SafeConstructorTrackingConfig safeConstructorTracking, DataFlow::Node safeTypeUsage,
MethodCall mc
|
safeConstructorTracking.hasFlow(_, safeTypeUsage) and
not exists(DataFlow::Node safeTypeUsage, MethodCall mc |
(
DataContractJsonSafeConstructorTracking::flowTo(safeTypeUsage) or
JavaScriptSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
XmlObjectSerializerDerivedConstructorTracking::flowTo(safeTypeUsage) or
XmlSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
DataContractSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
XmlMessageFormatterSafeConstructorTracking::flowTo(safeTypeUsage)
) and
mc.getQualifier() = safeTypeUsage.asExpr() and
mc.getAnArgument() = this.asExpr()
)
@@ -47,9 +51,11 @@ abstract class Sanitizer extends DataFlow::Node { }
private class RemoteSource extends Source instanceof RemoteFlowSource { }
/**
* DEPRECATED: Use `TaintToObjectMethodTracking` instead.
*
* User input to object method call deserialization flow tracking.
*/
class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
deprecated class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
TaintToObjectMethodTrackingConfig() { this = "TaintToObjectMethodTrackingConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -60,9 +66,27 @@ class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
}
/**
* User input to object method call deserialization flow tracking configuration.
*/
private module TaintToObjectMethodTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof InstanceMethodSink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to object method call deserialization flow tracking module.
*/
module TaintToObjectMethodTracking = TaintTracking::Global<TaintToObjectMethodTrackingConfig>;
/**
* DEPRECATED: Use `JsonConvertTracking` instead.
*
* User input to `JsonConvert` call deserialization flow tracking.
*/
class JsonConvertTrackingConfig extends TaintTracking::Configuration {
deprecated class JsonConvertTrackingConfig extends TaintTracking::Configuration {
JsonConvertTrackingConfig() { this = "JsonConvertTrackingConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -74,6 +98,24 @@ class JsonConvertTrackingConfig extends TaintTracking::Configuration {
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to `JsonConvert` call deserialization flow tracking configuration.
*/
private module JsonConvertTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) {
sink instanceof NewtonsoftJsonConvertDeserializeObjectMethodSink
}
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to `JsonConvert` call deserialization flow tracking module.
*/
module JsonConvertTracking = TaintTracking::Global<JsonConvertTrackingConfig>;
/**
* DEPRECATED: Use `TypeNameTracking` instead.
*
@@ -186,9 +228,12 @@ private module TypeNameTrackingConfig implements DataFlow::ConfigSig {
module TypeNameTracking = DataFlow::Global<TypeNameTrackingConfig>;
/**
* DEPRECATED: Use `TaintToConstructorOrStaticMethodTracking` instead.
*
* User input to static method or constructor call deserialization flow tracking.
*/
class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Configuration {
deprecated class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Configuration
{
TaintToConstructorOrStaticMethodTrackingConfig() {
this = "TaintToConstructorOrStaticMethodTrackingConfig"
}
@@ -201,9 +246,28 @@ class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Conf
}
/**
* User input to static method or constructor call deserialization flow tracking configuration.
*/
private module TaintToConstructorOrStaticMethodTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof ConstructorOrStaticMethodSink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to static method or constructor call deserialization flow tracking module.
*/
module TaintToConstructorOrStaticMethodTracking =
TaintTracking::Global<TaintToConstructorOrStaticMethodTrackingConfig>;
/**
* DEPRECATED: Use `TaintToObjectTypeTracking` instead.
*
* User input to instance type flow tracking.
*/
class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
deprecated class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
TaintToObjectTypeTrackingConfig() { this = "TaintToObjectTypeTrackingConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -234,9 +298,47 @@ class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
}
/**
* User input to instance type flow tracking config.
*/
private module TaintToObjectTypeTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget() instanceof UnsafeDeserializer and
sink.asExpr() = mc.getQualifier()
)
}
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodCall mc, Method m |
m = mc.getTarget() and
m.getDeclaringType().hasQualifiedName("System", "Type") and
m.hasName("GetType") and
m.isStatic() and
n1.asExpr() = mc.getArgument(0) and
n2.asExpr() = mc
)
or
exists(ObjectCreation oc |
n1.asExpr() = oc.getAnArgument() and
n2.asExpr() = oc and
oc.getObjectType() instanceof StrongTypeDeserializer
)
}
}
/**
* User input to instance type flow tracking module.
*/
module TaintToObjectTypeTracking = TaintTracking::Global<TaintToObjectTypeTrackingConfig>;
/**
* DEPRECATED: Use `WeakTypeCreationToUsageTracking` instead.
*
* Unsafe deserializer creation to usage tracking config.
*/
class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuration {
deprecated class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuration {
WeakTypeCreationToUsageTrackingConfig() { this = "DeserializerCreationToUsageTrackingConfig" }
override predicate isSource(DataFlow::Node source) {
@@ -255,13 +357,30 @@ class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuratio
}
/**
* Safe deserializer creation to usage tracking config.
* Unsafe deserializer creation to usage tracking config.
*/
abstract class SafeConstructorTrackingConfig extends TaintTracking2::Configuration {
bindingset[this]
SafeConstructorTrackingConfig() { any() }
private module WeakTypeCreationToUsageTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc.getObjectType() instanceof WeakTypeDeserializer and
source.asExpr() = oc
)
}
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget() instanceof UnsafeDeserializer and
sink.asExpr() = mc.getQualifier()
)
}
}
/**
* Unsafe deserializer creation to usage tracking module.
*/
module WeakTypeCreationToUsageTracking =
TaintTracking::Global<WeakTypeCreationToUsageTrackingConfig>;
/** BinaryFormatter */
private predicate isBinaryFormatterCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -367,13 +486,8 @@ private class DataContractJsonSerializerDeserializeMethodSink extends DataContra
}
}
private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
DataContractJsonSafeConstructorTrackingConfiguration() {
this = "DataContractJsonSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module DataContractJsonSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -385,7 +499,7 @@ private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeC
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isDataContractJsonSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -393,6 +507,9 @@ private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeC
}
}
private module DataContractJsonSafeConstructorTracking =
TaintTracking::Global<DataContractJsonSafeConstructorTrackingConfig>;
/** JavaScriptSerializer */
private predicate isJavaScriptSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -417,13 +534,8 @@ private class JavaScriptSerializerDeserializeMethodSink extends JavaScriptSerial
}
}
private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
JavaScriptSerializerSafeConstructorTrackingConfiguration() {
this = "JavaScriptSerializerSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module JavaScriptSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -434,7 +546,7 @@ private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends S
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isJavaScriptSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -442,6 +554,9 @@ private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends S
}
}
private module JavaScriptSerializerSafeConstructorTracking =
TaintTracking::Global<JavaScriptSerializerSafeConstructorTrackingConfig>;
/** XmlObjectSerializer */
private predicate isXmlObjectSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -461,13 +576,8 @@ private class XmlObjectSerializerDeserializeMethodSink extends XmlObjectSerializ
}
}
private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
XmlObjectSerializerDerivedConstructorTrackingConfiguration() {
this = "XmlObjectSerializerDerivedConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module XmlObjectSerializerDerivedConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(ValueOrRefType declaringType |
@@ -481,7 +591,7 @@ private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isXmlObjectSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -489,6 +599,9 @@ private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends
}
}
private module XmlObjectSerializerDerivedConstructorTracking =
TaintTracking::Global<XmlObjectSerializerDerivedConstructorTrackingConfig>;
/** XmlSerializer */
private predicate isXmlSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -507,13 +620,8 @@ private class XmlSerializerDeserializeMethodSink extends XmlSerializerSink {
}
}
private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
XmlSerializerSafeConstructorTrackingConfiguration() {
this = "XmlSerializerSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module XmlSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -525,7 +633,7 @@ private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeCons
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isXmlSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -533,6 +641,9 @@ private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeCons
}
}
private module XmlSerializerSafeConstructorTracking =
TaintTracking::Global<XmlSerializerSafeConstructorTrackingConfig>;
/** DataContractSerializer */
private predicate isDataContractSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -555,13 +666,8 @@ private class DataContractSerializerDeserializeMethodSink extends DataContractSe
}
}
private class DataContractSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
DataContractSerializerSafeConstructorTrackingConfiguration() {
this = "DataContractSerializerSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module DataContractSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -573,7 +679,7 @@ private class DataContractSerializerSafeConstructorTrackingConfiguration extends
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isDataContractSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -581,6 +687,9 @@ private class DataContractSerializerSafeConstructorTrackingConfiguration extends
}
}
private module DataContractSerializerSafeConstructorTracking =
TaintTracking::Global<DataContractSerializerSafeConstructorTrackingConfig>;
/** XmlMessageFormatter */
private predicate isXmlMessageFormatterCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -599,13 +708,8 @@ private class XmlMessageFormatterDeserializeMethodSink extends XmlMessageFormatt
}
}
private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
XmlMessageFormatterSafeConstructorTrackingConfiguration() {
this = "XmlMessageFormatterSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module XmlMessageFormatterSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -617,7 +721,7 @@ private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends Sa
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isXmlMessageFormatterCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -625,6 +729,9 @@ private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends Sa
}
}
private module XmlMessageFormatterSafeConstructorTracking =
TaintTracking::Global<XmlMessageFormatterSafeConstructorTrackingConfig>;
/** LosFormatter */
private predicate isLosFormatterCall(MethodCall mc, Method m) {
m = mc.getTarget() and

View File

@@ -1,3 +1,9 @@
## 0.6.1
### Minor Analysis Improvements
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.
## 0.6.0
### Minor Analysis Improvements

View File

@@ -13,43 +13,40 @@
import csharp
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery
import DataFlow::PathGraph
import Flow::PathGraph
from DataFlow::PathNode userInput, DataFlow::PathNode deserializeCallArg
module Flow =
DataFlow::MergePathGraph3<TaintToObjectMethodTracking::PathNode,
TaintToConstructorOrStaticMethodTracking::PathNode, JsonConvertTracking::PathNode,
TaintToObjectMethodTracking::PathGraph, TaintToConstructorOrStaticMethodTracking::PathGraph,
JsonConvertTracking::PathGraph>;
from Flow::PathNode userInput, Flow::PathNode deserializeCallArg
where
exists(TaintToObjectMethodTrackingConfig taintTracking |
// all flows from user input to deserialization with weak and strong type serializers
taintTracking.hasFlowPath(userInput, deserializeCallArg)
) and
// all flows from user input to deserialization with weak and strong type serializers
TaintToObjectMethodTracking::flowPath(userInput.asPathNode1(), deserializeCallArg.asPathNode1()) and
// intersect with strong types, but user controlled or weak types deserialization usages
(
exists(
DataFlow::Node weakTypeUsage,
WeakTypeCreationToUsageTrackingConfig weakTypeDeserializerTracking, MethodCall mc
|
weakTypeDeserializerTracking.hasFlowTo(weakTypeUsage) and
exists(DataFlow::Node weakTypeUsage, MethodCall mc |
WeakTypeCreationToUsageTracking::flowTo(weakTypeUsage) and
mc.getQualifier() = weakTypeUsage.asExpr() and
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
)
or
exists(
TaintToObjectTypeTrackingConfig userControlledTypeTracking, DataFlow::Node taintedTypeUsage,
MethodCall mc
|
userControlledTypeTracking.hasFlowTo(taintedTypeUsage) and
exists(DataFlow::Node taintedTypeUsage, MethodCall mc |
TaintToObjectTypeTracking::flowTo(taintedTypeUsage) and
mc.getQualifier() = taintedTypeUsage.asExpr() and
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
)
)
or
// no type check needed - straightforward taint -> sink
exists(TaintToConstructorOrStaticMethodTrackingConfig taintTracking2 |
taintTracking2.hasFlowPath(userInput, deserializeCallArg)
)
TaintToConstructorOrStaticMethodTracking::flowPath(userInput.asPathNode2(),
deserializeCallArg.asPathNode2())
or
// JsonConvert static method call, but with additional unsafe typename tracking
exists(JsonConvertTrackingConfig taintTrackingJsonConvert, DataFlow::Node settingsCallArg |
taintTrackingJsonConvert.hasFlowPath(userInput, deserializeCallArg) and
exists(DataFlow::Node settingsCallArg |
JsonConvertTracking::flowPath(userInput.asPathNode3(), deserializeCallArg.asPathNode3()) and
TypeNameTracking::flow(_, settingsCallArg) and
deserializeCallArg.getNode().asExpr().getParent() = settingsCallArg.asExpr().getParent()
)

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.
## 0.6.1
### Minor Analysis Improvements
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.0
lastReleaseVersion: 0.6.1

View File

@@ -6,7 +6,7 @@ private import IRFunctionBaseInternal
private newtype TIRFunction =
TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or
TVarInitIRFunction(Language::GlobalVariable var) { IRConstruction::Raw::varHasIRFunc(var) }
TVarInitIRFunction(Language::Variable var) { IRConstruction::Raw::varHasIRFunc(var) }
/**
* The IR for a function. This base class contains only the predicates that are the same between all

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.6.1-dev
version: 0.6.2-dev
groups:
- csharp
- queries

View File

@@ -139,9 +139,9 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig {
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
exists(DataFlowImplCommon::TypedContent tc |
DataFlowImplCommon::store(node1, tc, node2, _) and
isRelevantContent(tc.getContent()) and
exists(DataFlow::Content c |
DataFlowImplCommon::store(node1, c, node2, _, _) and
isRelevantContent(c) and
(
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
or
@@ -178,7 +178,7 @@ string captureThroughFlow(DataFlowTargetApi api) {
string output
|
ThroughFlow::flow(p, returnNodeExt) and
returnNodeExt.getEnclosingCallable() = api and
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
input = parameterNodeAsInput(p) and
output = returnNodeAsOutput(returnNodeExt) and
input != output and

View File

@@ -121,7 +121,7 @@ class InstanceParameterNode = DataFlowPrivate::InstanceParameterNode;
pragma[nomagic]
private CS::Parameter getParameter(DataFlowImplCommon::ReturnNodeExt node, ParameterPosition pos) {
result = node.getEnclosingCallable().getParameter(pos.getPosition())
result = node.(DataFlow::Node).getEnclosingCallable().getParameter(pos.getPosition())
}
/**

View File

@@ -1,23 +1,23 @@
import csharp
import semmle.code.csharp.dataflow.internal.ContentDataFlow
import semmle.code.csharp.dataflow.internal.ContentDataFlow as ContentDataFlow
class Conf extends ContentDataFlow::Configuration {
Conf() { this = "ContentFlowConf" }
module ContentConfig implements ContentDataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget().hasUndecoratedName("Sink") and
mc.getAnArgument() = sink.asExpr()
)
}
override int accessPathLimit() { result = 2 }
int accessPathLimit() { result = 2 }
}
module ContentFlow = ContentDataFlow::Global<ContentConfig>;
from
Conf conf, ContentDataFlow::Node source, ContentDataFlow::AccessPath sourceAp,
ContentDataFlow::Node sink, ContentDataFlow::AccessPath sinkAp, boolean preservesValue
where conf.hasFlow(source, sourceAp, sink, sinkAp, preservesValue)
DataFlow::Node source, ContentFlow::AccessPath sourceAp, DataFlow::Node sink,
ContentFlow::AccessPath sinkAp, boolean preservesValue
where ContentFlow::flow(source, sourceAp, sink, sinkAp, preservesValue)
select source, sourceAp, sink, sinkAp, preservesValue

View File

@@ -1,19 +1,12 @@
edges
| ../../../../resources/stubs/Newtonsoft.Json/13.0.1/Newtonsoft.Json.cs:930:20:930:20 | 4 : Int32 | Test.cs:19:32:19:52 | access to constant Auto : Int32 |
| Test.cs:9:46:9:49 | access to parameter data : TextBox | Test.cs:9:46:9:54 | access to property Text |
| Test.cs:17:46:17:49 | access to parameter data : TextBox | Test.cs:17:46:17:54 | access to property Text |
| Test.cs:19:32:19:52 | access to constant Auto : Int32 | Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings |
| Test.cs:19:32:19:52 | access to constant Auto : TypeNameHandling | Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings |
| Test.cs:25:46:25:49 | access to parameter data : TextBox | Test.cs:25:46:25:54 | access to property Text |
nodes
| ../../../../resources/stubs/Newtonsoft.Json/13.0.1/Newtonsoft.Json.cs:930:20:930:20 | 4 : Int32 | semmle.label | 4 : Int32 |
| Test.cs:9:46:9:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
| Test.cs:9:46:9:54 | access to property Text | semmle.label | access to property Text |
| Test.cs:17:46:17:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
| Test.cs:17:46:17:54 | access to property Text | semmle.label | access to property Text |
| Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings | semmle.label | object creation of type JsonSerializerSettings |
| Test.cs:19:32:19:52 | access to constant Auto : Int32 | semmle.label | access to constant Auto : Int32 |
| Test.cs:19:32:19:52 | access to constant Auto : TypeNameHandling | semmle.label | access to constant Auto : TypeNameHandling |
| Test.cs:25:46:25:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
| Test.cs:25:46:25:54 | access to property Text | semmle.label | access to property Text |
subpaths