mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
362 lines
13 KiB
Plaintext
362 lines
13 KiB
Plaintext
/**
|
|
* 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
|
|
deprecated import FlowStateString
|
|
private import codeql.util.Unit
|
|
|
|
/**
|
|
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
|
*
|
|
* 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 deprecated 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() }
|
|
|
|
/**
|
|
* 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)) }
|
|
|
|
/**
|
|
* 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 deprecated 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)
|
|
}
|
|
}
|
|
|
|
deprecated 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 =
|
|
deprecated TMkConfigState(Configuration config, FlowState state) {
|
|
state = relevantState(config) or state instanceof FlowStateEmpty
|
|
}
|
|
|
|
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
|
|
|
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
|
|
|
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
|
|
|
deprecated 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) { none() }
|
|
|
|
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)
|
|
}
|
|
|
|
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
|
|
|
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
|
|
|
predicate isBarrierIn(Node node, FlowState state) { none() }
|
|
|
|
predicate isBarrierOut(Node node, FlowState state) { none() }
|
|
|
|
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
|
singleConfiguration() and
|
|
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
|
model = ""
|
|
}
|
|
|
|
predicate isAdditionalFlowStep(
|
|
Node node1, FlowState state1, Node node2, FlowState state2, string model
|
|
) {
|
|
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
|
getConfig(state2) = getConfig(state1) and
|
|
model = ""
|
|
or
|
|
not singleConfiguration() and
|
|
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
|
state2 = state1 and
|
|
model = ""
|
|
}
|
|
|
|
predicate allowImplicitRead(Node node, ContentSet c) {
|
|
any(Configuration config).allowImplicitRead(node, c)
|
|
}
|
|
|
|
predicate neverSkip(Node node) { none() }
|
|
|
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
|
|
|
int accessPathLimit() { result = 5 }
|
|
|
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
|
|
|
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
|
|
|
predicate observeDiffInformedIncrementalMode() { none() }
|
|
}
|
|
|
|
deprecated 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.
|
|
*/
|
|
deprecated 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. */
|
|
deprecated final FlowState getState() { result = getState(super.getState()) }
|
|
|
|
/** Gets the associated configuration. */
|
|
deprecated 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) }
|
|
}
|
|
|
|
deprecated module PathGraph = I::PathGraph;
|
|
|
|
deprecated 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
|
|
)
|
|
}
|
|
|
|
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
|
I::flowPath(source, sink) and source.getConfiguration() = config
|
|
}
|
|
|
|
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
|
|
|
deprecated predicate flowsTo = hasFlow/3;
|