mirror of
https://github.com/github/codeql.git
synced 2026-04-21 15:05:56 +02:00
Merge from main to resolve conflicts
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Path explanations now include flow that goes through callbacks passed into library functions. For example, if `map` is a library function, then in `result = map(xs, x => x + 1)` we will now include the step from `x` to `x + 1` in the path explanation, instead of going directly from `xs` to `result`. Note that this change does not affect actual query results, but only how path explanations are computed.
|
||||
@@ -4,22 +4,19 @@
|
||||
* modules.
|
||||
*/
|
||||
|
||||
private import codeql.util.Location
|
||||
|
||||
/** Provides language-specific data flow parameters. */
|
||||
signature module InputSig {
|
||||
signature module InputSig<LocationSig Location> {
|
||||
/**
|
||||
* A node in the data flow graph.
|
||||
*/
|
||||
class Node {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
class ParameterNode extends Node;
|
||||
@@ -30,9 +27,27 @@ signature module InputSig {
|
||||
ReturnKind getKind();
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in the data flow graph that represents an output of a call.
|
||||
*/
|
||||
class OutNode extends Node;
|
||||
|
||||
/**
|
||||
* A node in the data flow graph representing the value of some other node
|
||||
* after an operation that might have changed its state. A typical example is
|
||||
* an argument, which may have been modified by the callee. For example,
|
||||
* consider the following code calling a setter method:
|
||||
* ```
|
||||
* x.setFoo(y);
|
||||
* ```
|
||||
* The post-update node for the argument node `x` is the node representing the
|
||||
* value of `x` after the field `foo` has been updated.
|
||||
*/
|
||||
class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the pre-update node, that is, the node that represents the same
|
||||
* value prior to the operation.
|
||||
*/
|
||||
Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
@@ -131,6 +146,12 @@ signature module InputSig {
|
||||
string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if access paths with `c` at their head always should be tracked at
|
||||
* high precision. This disables adaptive access path precision for such
|
||||
* access paths. This may be beneficial for content that indicates an
|
||||
* element of an array or container.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c);
|
||||
|
||||
/**
|
||||
@@ -150,11 +171,19 @@ signature module InputSig {
|
||||
Content getAReadContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* A content approximation. A content approximation corresponds to one or
|
||||
* more `Content`s, and is used to provide an in-between level of precision
|
||||
* for pruning.
|
||||
*/
|
||||
class ContentApprox {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content approximation for content `c`.
|
||||
*/
|
||||
ContentApprox getContentApprox(Content c);
|
||||
|
||||
class ParameterPosition {
|
||||
@@ -169,8 +198,16 @@ signature module InputSig {
|
||||
string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the parameter position `ppos` matches the argument position
|
||||
* `apos`.
|
||||
*/
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos);
|
||||
|
||||
/**
|
||||
* Holds if there is a simple local flow step from `node1` to `node2`. These
|
||||
* are the value-preserving intra-callable flow steps.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node node1, Node node2);
|
||||
|
||||
/**
|
||||
@@ -278,9 +315,9 @@ signature module InputSig {
|
||||
default predicate ignoreFieldFlowBranchLimit(DataFlowCallable c) { none() }
|
||||
}
|
||||
|
||||
module Configs<InputSig Lang> {
|
||||
module Configs<LocationSig Location, InputSig<Location> Lang> {
|
||||
private import Lang
|
||||
private import internal.DataFlowImplCommon::MakeImplCommon<Lang>
|
||||
private import internal.DataFlowImplCommon::MakeImplCommon<Location, Lang>
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/** An input configuration for data flow. */
|
||||
@@ -333,6 +370,9 @@ module Configs<InputSig Lang> {
|
||||
*/
|
||||
default int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/** Gets the access path limit. */
|
||||
default int accessPathLimit() { result = Lang::accessPathLimit() }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
@@ -452,6 +492,9 @@ module Configs<InputSig Lang> {
|
||||
*/
|
||||
default int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/** Gets the access path limit. */
|
||||
default int accessPathLimit() { result = Lang::accessPathLimit() }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
@@ -488,10 +531,10 @@ module Configs<InputSig Lang> {
|
||||
}
|
||||
}
|
||||
|
||||
module DataFlowMake<InputSig Lang> {
|
||||
module DataFlowMake<LocationSig Location, InputSig<Location> Lang> {
|
||||
private import Lang
|
||||
private import internal.DataFlowImpl::MakeImpl<Lang>
|
||||
import Configs<Lang>
|
||||
private import internal.DataFlowImpl::MakeImpl<Location, Lang>
|
||||
import Configs<Location, Lang>
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `partialFlow` and `partialFlowRev`
|
||||
@@ -540,6 +583,8 @@ module DataFlowMake<InputSig Lang> {
|
||||
private module C implements FullStateConfigSig {
|
||||
import DefaultState<Config>
|
||||
import Config
|
||||
|
||||
predicate accessPathLimit = Config::accessPathLimit/0;
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
@@ -556,6 +601,8 @@ module DataFlowMake<InputSig Lang> {
|
||||
module GlobalWithState<StateConfigSig Config> implements GlobalFlowSig {
|
||||
private module C implements FullStateConfigSig {
|
||||
import Config
|
||||
|
||||
predicate accessPathLimit = Config::accessPathLimit/0;
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
@@ -570,19 +617,11 @@ module DataFlowMake<InputSig Lang> {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode();
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
signature module PathGraphSig<PathNodeSig PathNode> {
|
||||
@@ -625,6 +664,15 @@ module DataFlowMake<InputSig Lang> {
|
||||
result = this.asPathNode2().toString()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode() {
|
||||
result = this.asPathNode1().getNode() or
|
||||
result = this.asPathNode2().getNode()
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation() { result = this.getNode().getLocation() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
@@ -632,17 +680,10 @@ module DataFlowMake<InputSig Lang> {
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
deprecated predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
|
||||
this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode() {
|
||||
result = this.asPathNode1().getNode() or
|
||||
result = this.asPathNode2().getNode()
|
||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,7 +748,7 @@ module DataFlowMake<InputSig Lang> {
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
deprecated predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -715,6 +756,9 @@ module DataFlowMake<InputSig Lang> {
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation() { result = super.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
private import DataFlow as DF
|
||||
private import internal.DataFlowImpl
|
||||
private import codeql.util.Location
|
||||
|
||||
/**
|
||||
* Provides language-specific taint-tracking parameters.
|
||||
*/
|
||||
signature module InputSig<DF::InputSig Lang> {
|
||||
signature module InputSig<LocationSig Location, DF::InputSig<Location> Lang> {
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
@@ -33,10 +34,13 @@ signature module InputSig<DF::InputSig Lang> {
|
||||
/**
|
||||
* Construct the modules for taint-tracking analyses.
|
||||
*/
|
||||
module TaintFlowMake<DF::InputSig DataFlowLang, InputSig<DataFlowLang> TaintTrackingLang> {
|
||||
module TaintFlowMake<
|
||||
LocationSig Location, DF::InputSig<Location> DataFlowLang,
|
||||
InputSig<Location, DataFlowLang> TaintTrackingLang>
|
||||
{
|
||||
private import TaintTrackingLang
|
||||
private import DF::DataFlowMake<DataFlowLang> as DataFlow
|
||||
private import MakeImpl<DataFlowLang> as DataFlowInternal
|
||||
private import DF::DataFlowMake<Location, DataFlowLang> as DataFlow
|
||||
private import MakeImpl<Location, DataFlowLang> as DataFlowInternal
|
||||
|
||||
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
|
||||
DataFlowInternal::FullStateConfigSig
|
||||
|
||||
@@ -601,16 +601,22 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
* observed in a similarly synthesized post-update node for this read of `v`.
|
||||
*/
|
||||
private predicate synthRead(
|
||||
CapturedVariable v, BasicBlock bb, int i, boolean topScope, Expr closure
|
||||
CapturedVariable v, BasicBlock bb, int i, boolean topScope, Expr closure, boolean alias
|
||||
) {
|
||||
exists(ClosureExpr ce | closureCaptures(ce, v) |
|
||||
ce.hasCfgNode(bb, i) and ce = closure
|
||||
ce.hasCfgNode(bb, i) and ce = closure and alias = false
|
||||
or
|
||||
localOrNestedClosureAccess(ce, closure, bb, i)
|
||||
localOrNestedClosureAccess(ce, closure, bb, i) and alias = true
|
||||
) and
|
||||
if v.getCallable() != bb.getEnclosingCallable() then topScope = false else topScope = true
|
||||
}
|
||||
|
||||
private predicate synthRead(
|
||||
CapturedVariable v, BasicBlock bb, int i, boolean topScope, Expr closure
|
||||
) {
|
||||
synthRead(v, bb, i, topScope, closure, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is an access of a captured variable inside a closure in the
|
||||
* `i`th node of `bb`, such that we need to synthesize a `this.` qualifier.
|
||||
@@ -919,16 +925,22 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
)
|
||||
}
|
||||
|
||||
predicate storeStep(ClosureNode node1, CapturedVariable v, ClosureNode node2) {
|
||||
// store v in the closure or in the malloc in case of a relevant constructor call
|
||||
private predicate storeStepClosure(
|
||||
ClosureNode node1, CapturedVariable v, ClosureNode node2, boolean alias
|
||||
) {
|
||||
exists(BasicBlock bb, int i, Expr closure |
|
||||
synthRead(v, bb, i, _, closure) and
|
||||
synthRead(v, bb, i, _, closure, alias) and
|
||||
node1 = TSynthRead(v, bb, i, false)
|
||||
|
|
||||
node2 = TExprNode(closure, false)
|
||||
or
|
||||
node2 = TMallocNode(closure) and hasConstructorCapture(closure, v)
|
||||
)
|
||||
}
|
||||
|
||||
predicate storeStep(ClosureNode node1, CapturedVariable v, ClosureNode node2) {
|
||||
// store v in the closure or in the malloc in case of a relevant constructor call
|
||||
storeStepClosure(node1, v, node2, _)
|
||||
or
|
||||
// write to v inside the closure body
|
||||
exists(BasicBlock bb, int i, VariableWrite vw |
|
||||
@@ -964,6 +976,62 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
}
|
||||
|
||||
predicate clearsContent(ClosureNode node, CapturedVariable v) {
|
||||
/*
|
||||
* Stores into closure aliases block flow from previous stores, both to
|
||||
* avoid overlapping data flow paths, but also to avoid false positive
|
||||
* flow.
|
||||
*
|
||||
* Example 1 (overlapping paths):
|
||||
*
|
||||
* ```rb
|
||||
* def m
|
||||
* x = taint
|
||||
*
|
||||
* fn = -> { # (1)
|
||||
* sink x
|
||||
* }
|
||||
*
|
||||
* fn.call # (2)
|
||||
* ```
|
||||
*
|
||||
* If we don't clear `x` at `fn` (2), we will have two overlapping paths:
|
||||
*
|
||||
* ```
|
||||
* taint -> fn (2) [captured x]
|
||||
* taint -> fn (1) [captured x] -> fn (2) [captured x]
|
||||
* ```
|
||||
*
|
||||
* where the step `fn (1) [captured x] -> fn [captured x]` arises from normal
|
||||
* use-use flow for `fn`. Clearing `x` at `fn` (2) removes the second path above.
|
||||
*
|
||||
* Example 2 (false positive flow):
|
||||
*
|
||||
* ```rb
|
||||
* def m
|
||||
* x = taint
|
||||
*
|
||||
* fn = -> { # (1)
|
||||
* sink x
|
||||
* }
|
||||
*
|
||||
* x = nil # (2)
|
||||
*
|
||||
* fn.call # (3)
|
||||
* end
|
||||
* ```
|
||||
*
|
||||
* If we don't clear `x` at `fn` (3), we will have the following false positive
|
||||
* flow path:
|
||||
*
|
||||
* ```
|
||||
* taint -> fn (1) [captured x] -> fn (3) [captured x]
|
||||
* ```
|
||||
*
|
||||
* since normal use-use flow for `fn` does not take the overwrite at (2) into account.
|
||||
*/
|
||||
|
||||
storeStepClosure(_, v, node, true)
|
||||
or
|
||||
exists(BasicBlock bb, int i |
|
||||
captureWrite(v, bb, i, false, _) and
|
||||
node = TSynthThisQualifier(bb, i, false)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
||||
private import codeql.dataflow.DataFlow
|
||||
private import codeql.typetracking.TypeTracking as Tt
|
||||
private import codeql.util.Location
|
||||
private import codeql.util.Unit
|
||||
|
||||
module MakeImplCommon<InputSig Lang> {
|
||||
module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
private import Lang
|
||||
import Cached
|
||||
|
||||
@@ -393,6 +394,216 @@ module MakeImplCommon<InputSig Lang> {
|
||||
result = viableCallableLambda(call, _)
|
||||
}
|
||||
|
||||
signature module CallContextSensitivityInputSig {
|
||||
/** Holds if the edge is possibly needed in the direction `call` to `c`. */
|
||||
predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c);
|
||||
|
||||
/** Holds if the edge is possibly needed in the direction `c` to `call`. */
|
||||
predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c);
|
||||
|
||||
/**
|
||||
* Holds if the call context `ctx` may reduce the set of viable run-time
|
||||
* dispatch targets of call `call` in `c`.
|
||||
*/
|
||||
default predicate reducedViableImplInCallContextCand(
|
||||
DataFlowCall call, DataFlowCallable c, DataFlowCall ctx
|
||||
) {
|
||||
relevantCallEdgeIn(ctx, c) and
|
||||
mayBenefitFromCallContextExt(call, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow returning from callable `c` to call `call` might return
|
||||
* further and if this path may restrict the set of call sites that can be
|
||||
* returned to.
|
||||
*/
|
||||
default predicate reducedViableImplInReturnCand(DataFlowCallable c, DataFlowCall call) {
|
||||
relevantCallEdgeOut(call, c) and
|
||||
mayBenefitFromCallContextExt(call, _)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates releated to call-context sensitivity. */
|
||||
module CallContextSensitivity<CallContextSensitivityInputSig Input> {
|
||||
private import Input
|
||||
|
||||
pragma[nomagic]
|
||||
DataFlowCallable viableImplInCallContextExtIn(DataFlowCall call, DataFlowCall ctx) {
|
||||
reducedViableImplInCallContextCand(call, _, ctx) and
|
||||
result = viableImplInCallContextExt(call, ctx) and
|
||||
relevantCallEdgeIn(call, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `ctx` reduces the set of viable run-time
|
||||
* dispatch targets of call `call` in `c`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
reducedViableImplInCallContextCand(call, c, ctx) and
|
||||
ctxtgts = count(viableImplInCallContextExtIn(call, ctx)) and
|
||||
tgts = strictcount(DataFlowCallable tgt | relevantCallEdgeIn(call, tgt)) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n |
|
||||
relevantCallEdgeIn(call, callable) and
|
||||
getNodeEnclosingCallable(n) = callable and
|
||||
isUnreachableInCallCached(n, call)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
DataFlowCallable viableImplInCallContextExtOut(DataFlowCall call, DataFlowCall ctx) {
|
||||
exists(DataFlowCallable c |
|
||||
reducedViableImplInReturnCand(result, call) and
|
||||
result = viableImplInCallContextExt(call, ctx) and
|
||||
mayBenefitFromCallContextExt(call, c) and
|
||||
relevantCallEdgeOut(ctx, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow returning from callable `c` to call `call` might return
|
||||
* further and if this path restricts the set of call sites that can be
|
||||
* returned to.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
reducedViableImplInReturnCand(c, call) and
|
||||
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExtOut(call, ctx)) and
|
||||
tgts =
|
||||
strictcount(DataFlowCall ctx |
|
||||
callEnclosingCallable(call, any(DataFlowCallable encl | relevantCallEdgeOut(ctx, encl)))
|
||||
) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
signature module PrunedViableImplInputSig {
|
||||
predicate reducedViableImplInCallContext(
|
||||
DataFlowCall call, DataFlowCallable c, DataFlowCall ctx
|
||||
);
|
||||
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call);
|
||||
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable c);
|
||||
}
|
||||
|
||||
/**
|
||||
* This module is only parameterized so that we can refer to cached versions
|
||||
* of the input predicates in `CachedCallContextSensitivity`.
|
||||
*/
|
||||
module PrunedViableImpl<PrunedViableImplInputSig Input2> {
|
||||
/**
|
||||
* Gets a viable run-time dispatch target for the call `call` in the
|
||||
* context `ctx`. This is restricted to those calls for which a context
|
||||
* makes a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, CallContextSpecificCall ctx) {
|
||||
exists(DataFlowCall outer | ctx = TSpecificCall(outer) |
|
||||
result = viableImplInCallContextExtIn(call, outer) and
|
||||
Input2::reducedViableImplInCallContext(call, _, outer)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `call` does not have a reduced set of dispatch targets in call context `ctx`. */
|
||||
bindingset[call, ctx]
|
||||
predicate noPrunedViableImplInCallContext(DataFlowCall call, CallContext ctx) {
|
||||
exists(DataFlowCall outer | ctx = TSpecificCall(outer) |
|
||||
not Input2::reducedViableImplInCallContext(call, _, outer)
|
||||
)
|
||||
or
|
||||
ctx instanceof CallContextSomeCall
|
||||
or
|
||||
ctx instanceof CallContextAny
|
||||
or
|
||||
ctx instanceof CallContextReturn
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a call from `call` in `cc` to `result`, where `result` is
|
||||
* restricted by `relevantResolveTarget`.
|
||||
*/
|
||||
bindingset[call, cc]
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
result = prunedViableImplInCallContext(call, cc)
|
||||
or
|
||||
noPrunedViableImplInCallContext(call, cc) and
|
||||
relevantCallEdgeIn(call, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable call site for the return from `callable` in call context
|
||||
* `ctx`. This is restricted to those callables and contexts for which
|
||||
* the possible call sites are restricted.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
DataFlowCall prunedViableImplInCallContextReverse(
|
||||
DataFlowCallable callable, CallContextReturn ctx
|
||||
) {
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
callEnclosingCallable(call0, callable) and
|
||||
ctx = TReturn(c0, call0) and
|
||||
c0 = viableImplInCallContextExtOut(call0, result) and
|
||||
Input2::reducedViableImplInReturn(c0, call0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a return from `callable` in `cc` to `call`.
|
||||
*/
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContextNoCall cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and relevantCallEdgeOut(call, callable)
|
||||
or
|
||||
call = prunedViableImplInCallContextReverse(callable, cc)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` improves virtual dispatch in `callable`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
|
||||
Input2::reducedViableImplInCallContext(_, callable, call)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable c) {
|
||||
Input2::recordDataFlowCallSiteUnreachable(call, c) or
|
||||
recordDataFlowCallSiteDispatch(call, c)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate reducedViableImplInCallContextAlias = reducedViableImplInCallContext/3;
|
||||
|
||||
private predicate reducedViableImplInReturnAlias = reducedViableImplInReturn/2;
|
||||
|
||||
private predicate recordDataFlowCallSiteUnreachableAlias = recordDataFlowCallSiteUnreachable/2;
|
||||
|
||||
private module DefaultPrunedViableImplInput implements PrunedViableImplInputSig {
|
||||
predicate reducedViableImplInCallContext = reducedViableImplInCallContextAlias/3;
|
||||
|
||||
predicate reducedViableImplInReturn = reducedViableImplInReturnAlias/2;
|
||||
|
||||
predicate recordDataFlowCallSiteUnreachable = recordDataFlowCallSiteUnreachableAlias/2;
|
||||
}
|
||||
|
||||
import PrunedViableImpl<DefaultPrunedViableImplInput>
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
@@ -498,6 +709,102 @@ module MakeImplCommon<InputSig Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
cached
|
||||
predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
|
||||
(
|
||||
mayBenefitFromCallContext(call)
|
||||
or
|
||||
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
|
||||
) and
|
||||
callEnclosingCallable(call, callable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
result = viableCallableLambda(call, TDataFlowCallSome(ctx))
|
||||
or
|
||||
exists(DataFlowCallable enclosing |
|
||||
mayBenefitFromCallContextExt(call, enclosing) and
|
||||
enclosing = viableCallableExt(ctx) and
|
||||
result = viableCallableLambda(call, TDataFlowCallNone())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A cached version of the `CallContextSensitivity` module. Only used in
|
||||
* pruning stages 1+2 and flow exploration; all subsequent pruning stages use a
|
||||
* pruned version, based on the relevant call edges from the previous stage.
|
||||
*/
|
||||
cached
|
||||
module CachedCallContextSensitivity {
|
||||
private module CallContextSensitivityInput implements CallContextSensitivityInputSig {
|
||||
predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) {
|
||||
c = viableCallableExt(call)
|
||||
}
|
||||
|
||||
predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) {
|
||||
c = viableCallableExt(call)
|
||||
}
|
||||
}
|
||||
|
||||
private module Impl1 = CallContextSensitivity<CallContextSensitivityInput>;
|
||||
|
||||
cached
|
||||
predicate reducedViableImplInCallContext(
|
||||
DataFlowCall call, DataFlowCallable c, DataFlowCall ctx
|
||||
) {
|
||||
Impl1::reducedViableImplInCallContext(call, c, ctx)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable c) {
|
||||
Impl1::recordDataFlowCallSiteUnreachable(call, c)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
Impl1::reducedViableImplInReturn(c, call)
|
||||
}
|
||||
|
||||
private module PrunedViableImplInput implements Impl1::PrunedViableImplInputSig {
|
||||
predicate reducedViableImplInCallContext =
|
||||
CachedCallContextSensitivity::reducedViableImplInCallContext/3;
|
||||
|
||||
predicate reducedViableImplInReturn =
|
||||
CachedCallContextSensitivity::reducedViableImplInReturn/2;
|
||||
|
||||
predicate recordDataFlowCallSiteUnreachable =
|
||||
CachedCallContextSensitivity::recordDataFlowCallSiteUnreachable/2;
|
||||
}
|
||||
|
||||
private module Impl2 = Impl1::PrunedViableImpl<PrunedViableImplInput>;
|
||||
|
||||
import Impl2
|
||||
|
||||
cached
|
||||
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, CallContextSpecificCall ctx) {
|
||||
result = Impl2::prunedViableImplInCallContext(call, ctx)
|
||||
}
|
||||
|
||||
cached
|
||||
DataFlowCall prunedViableImplInCallContextReverse(
|
||||
DataFlowCallable callable, CallContextReturn ctx
|
||||
) {
|
||||
result = Impl2::prunedViableImplInCallContextReverse(callable, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` is the parameter of a viable dispatch target of `call`,
|
||||
* and `p` has position `ppos`.
|
||||
@@ -788,106 +1095,6 @@ module MakeImplCommon<InputSig Lang> {
|
||||
|
||||
import FlowThrough
|
||||
|
||||
cached
|
||||
private module DispatchWithCallContext {
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
|
||||
(
|
||||
mayBenefitFromCallContext(call)
|
||||
or
|
||||
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
|
||||
) and
|
||||
callEnclosingCallable(call, callable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
result = viableCallableLambda(call, TDataFlowCallSome(ctx))
|
||||
or
|
||||
exists(DataFlowCallable enclosing |
|
||||
mayBenefitFromCallContextExt(call, enclosing) and
|
||||
enclosing = viableCallableExt(ctx) and
|
||||
result = viableCallableLambda(call, TDataFlowCallNone())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `ctx` reduces the set of viable run-time
|
||||
* dispatch targets of call `call` in `c`.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInCallContext(
|
||||
DataFlowCall call, DataFlowCallable c, DataFlowCall ctx
|
||||
) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
mayBenefitFromCallContextExt(call, c) and
|
||||
c = viableCallableExt(ctx) and
|
||||
ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
|
||||
tgts = strictcount(viableCallableExt(call)) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable run-time dispatch target for the call `call` in the
|
||||
* context `ctx`. This is restricted to those calls for which a context
|
||||
* makes a difference.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, CallContextSpecificCall ctx) {
|
||||
exists(DataFlowCall outer | ctx = TSpecificCall(outer) |
|
||||
result = viableImplInCallContextExt(call, outer) and
|
||||
reducedViableImplInCallContext(call, _, outer)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow returning from callable `c` to call `call` might return
|
||||
* further and if this path restricts the set of call sites that can be
|
||||
* returned to.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
mayBenefitFromCallContextExt(call, _) and
|
||||
c = viableCallableExt(call) and
|
||||
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
|
||||
tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable call site for the return from `callable` in call context
|
||||
* `ctx`. This is restricted to those callables and contexts for which
|
||||
* the possible call sites are restricted.
|
||||
*/
|
||||
cached
|
||||
DataFlowCall prunedViableImplInCallContextReverse(
|
||||
DataFlowCallable callable, CallContextReturn ctx
|
||||
) {
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
callEnclosingCallable(call0, callable) and
|
||||
ctx = TReturn(c0, call0) and
|
||||
c0 = viableImplInCallContextExt(call0, result) and
|
||||
reducedViableImplInReturn(c0, call0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import DispatchWithCallContext
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to the pre-update node associated with post-update
|
||||
* node `n`, in the same callable, using only value-preserving steps.
|
||||
@@ -966,22 +1173,6 @@ module MakeImplCommon<InputSig Lang> {
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` improves virtual dispatch in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) }
|
||||
|
||||
@@ -994,9 +1185,13 @@ module MakeImplCommon<InputSig Lang> {
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or
|
||||
TSpecificCall(DataFlowCall call) {
|
||||
CachedCallContextSensitivity::recordDataFlowCallSite(call, _)
|
||||
} or
|
||||
TSomeCall() or
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
CachedCallContextSensitivity::reducedViableImplInReturn(c, call)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TReturnPosition =
|
||||
@@ -1448,28 +1643,13 @@ module MakeImplCommon<InputSig Lang> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
recordDataFlowCallSiteDispatch(call, callable) or
|
||||
recordDataFlowCallSiteUnreachable(call, callable)
|
||||
}
|
||||
final private class NodeFinal = Node;
|
||||
|
||||
/**
|
||||
* A `Node` at which a cast can occur such that the type should be checked.
|
||||
*/
|
||||
class CastingNode instanceof Node {
|
||||
class CastingNode extends NodeFinal {
|
||||
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(
|
||||
@@ -1542,7 +1722,7 @@ module MakeImplCommon<InputSig Lang> {
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
recordDataFlowCallSite(this.getCall(), callable)
|
||||
CachedCallContextSensitivity::recordDataFlowCallSite(this.getCall(), callable)
|
||||
}
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
|
||||
@@ -1615,17 +1795,9 @@ module MakeImplCommon<InputSig Lang> {
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ParamNode instanceof Node {
|
||||
class ParamNode extends NodeFinal {
|
||||
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.
|
||||
@@ -1636,17 +1808,9 @@ module MakeImplCommon<InputSig Lang> {
|
||||
}
|
||||
|
||||
/** A data-flow node that represents a call argument. */
|
||||
class ArgNode instanceof Node {
|
||||
class ArgNode extends NodeFinal {
|
||||
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)
|
||||
@@ -1657,17 +1821,9 @@ module MakeImplCommon<InputSig Lang> {
|
||||
* 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 instanceof Node {
|
||||
class ReturnNodeExt extends NodeFinal {
|
||||
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) }
|
||||
}
|
||||
@@ -1676,16 +1832,8 @@ module MakeImplCommon<InputSig Lang> {
|
||||
* 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 instanceof Node {
|
||||
class OutNodeExt extends NodeFinal {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1773,58 +1921,6 @@ module MakeImplCommon<InputSig Lang> {
|
||||
result = getReturnPosition0(ret, ret.getKind())
|
||||
}
|
||||
|
||||
/** Holds if `call` does not have a reduced set of dispatch targets in call context `ctx`. */
|
||||
bindingset[call, ctx]
|
||||
predicate noPrunedViableImplInCallContext(DataFlowCall call, CallContext ctx) {
|
||||
exists(DataFlowCall outer | ctx = TSpecificCall(outer) |
|
||||
not reducedViableImplInCallContext(call, _, outer)
|
||||
)
|
||||
or
|
||||
ctx instanceof CallContextSomeCall
|
||||
or
|
||||
ctx instanceof CallContextAny
|
||||
or
|
||||
ctx instanceof CallContextReturn
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a return from `callable` in `cc` to `call`.
|
||||
*/
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and callable = viableCallableExt(call)
|
||||
or
|
||||
call = prunedViableImplInCallContextReverse(callable, cc)
|
||||
}
|
||||
|
||||
signature predicate relevantResolveTargetSig(DataFlowCallable c);
|
||||
|
||||
module ResolveCall<relevantResolveTargetSig/1 relevantResolveTarget> {
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable prunedRelevantViableImplInCallContext(DataFlowCall call, CallContext cc) {
|
||||
result = prunedViableImplInCallContext(call, cc) and
|
||||
relevantResolveTarget(result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableRelevantCallableExt(DataFlowCall call) {
|
||||
result = viableCallableExt(call) and
|
||||
relevantResolveTarget(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a call from `call` in `cc` to `result`, where `result` is
|
||||
* restricted by `relevantResolveTarget`.
|
||||
*/
|
||||
bindingset[call, cc]
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
result = prunedRelevantViableImplInCallContext(call, cc)
|
||||
or
|
||||
noPrunedViableImplInCallContext(call, cc) and
|
||||
result = viableRelevantCallableExt(call)
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional Boolean value. */
|
||||
class BooleanOption extends TBooleanOption {
|
||||
string toString() {
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
|
||||
private import codeql.dataflow.DataFlow as DF
|
||||
private import codeql.dataflow.TaintTracking as TT
|
||||
private import codeql.util.Location
|
||||
|
||||
signature module InputSig<DF::InputSig DataFlowLang> {
|
||||
signature module InputSig<LocationSig Location, DF::InputSig<Location> DataFlowLang> {
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
default predicate uniqueEnclosingCallableExclude(DataFlowLang::Node n) { none() }
|
||||
|
||||
@@ -71,8 +72,8 @@ signature module InputSig<DF::InputSig DataFlowLang> {
|
||||
}
|
||||
|
||||
module MakeConsistency<
|
||||
DF::InputSig DataFlowLang, TT::InputSig<DataFlowLang> TaintTrackingLang,
|
||||
InputSig<DataFlowLang> Input>
|
||||
LocationSig Location, DF::InputSig<Location> DataFlowLang,
|
||||
TT::InputSig<Location, DataFlowLang> TaintTrackingLang, InputSig<Location, DataFlowLang> Input>
|
||||
{
|
||||
private import DataFlowLang
|
||||
private import TaintTrackingLang
|
||||
@@ -128,10 +129,7 @@ module MakeConsistency<
|
||||
|
||||
query predicate uniqueNodeLocation(Node n, string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c = count(n.getLocation()) and
|
||||
c != 1 and
|
||||
not Input::uniqueNodeLocationExclude(n) and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
@@ -142,7 +140,7 @@ module MakeConsistency<
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
not exists(n.getLocation()) and
|
||||
not Input::missingLocationExclude(n)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
|
||||
@@ -10,7 +10,7 @@ private import AccessPathSyntax as AccessPathSyntax
|
||||
/**
|
||||
* Provides language-specific parameters.
|
||||
*/
|
||||
signature module InputSig<DF::InputSig Lang> {
|
||||
signature module InputSig<LocationSig Location, DF::InputSig<Location> Lang> {
|
||||
/**
|
||||
* A base class of callables that are candidates for flow summary modeling.
|
||||
*/
|
||||
@@ -139,10 +139,12 @@ signature module InputSig<DF::InputSig Lang> {
|
||||
}
|
||||
}
|
||||
|
||||
module Make<DF::InputSig DataFlowLang, InputSig<DataFlowLang> Input> {
|
||||
module Make<
|
||||
LocationSig Location, DF::InputSig<Location> DataFlowLang, InputSig<Location, DataFlowLang> Input>
|
||||
{
|
||||
private import DataFlowLang
|
||||
private import Input
|
||||
private import codeql.dataflow.internal.DataFlowImplCommon::MakeImplCommon<DataFlowLang>
|
||||
private import codeql.dataflow.internal.DataFlowImplCommon::MakeImplCommon<Location, DataFlowLang>
|
||||
private import codeql.util.Unit
|
||||
|
||||
final private class SummarizedCallableBaseFinal = SummarizedCallableBase;
|
||||
@@ -1457,7 +1459,7 @@ module Make<DF::InputSig DataFlowLang, InputSig<DataFlowLang> Input> {
|
||||
AccessPathSyntax::parseInt(part.getArgumentList()) < 0
|
||||
}
|
||||
|
||||
signature module SourceSinkInterpretationInputSig<LocationSig Location> {
|
||||
signature module SourceSinkInterpretationInputSig {
|
||||
class Element {
|
||||
string toString();
|
||||
|
||||
@@ -1523,8 +1525,7 @@ module Make<DF::InputSig DataFlowLang, InputSig<DataFlowLang> Input> {
|
||||
* Should eventually be replaced with API graphs like in dynamic languages.
|
||||
*/
|
||||
module SourceSinkInterpretation<
|
||||
LocationSig Location,
|
||||
SourceSinkInterpretationInputSig<Location> SourceSinkInterpretationInput>
|
||||
SourceSinkInterpretationInputSig SourceSinkInterpretationInput>
|
||||
{
|
||||
private import SourceSinkInterpretationInput
|
||||
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
private import codeql.dataflow.DataFlow as DF
|
||||
private import codeql.dataflow.TaintTracking as TT
|
||||
private import codeql.util.test.InlineExpectationsTest as IET
|
||||
private import codeql.util.Location
|
||||
|
||||
signature module InputSig<DF::InputSig DataFlowLang> {
|
||||
signature module InputSig<LocationSig Location, DF::InputSig<Location> DataFlowLang> {
|
||||
predicate defaultSource(DataFlowLang::Node source);
|
||||
|
||||
predicate defaultSink(DataFlowLang::Node source);
|
||||
@@ -40,12 +41,13 @@ signature module InputSig<DF::InputSig DataFlowLang> {
|
||||
}
|
||||
|
||||
module InlineFlowTestMake<
|
||||
DF::InputSig DataFlowLang, TT::InputSig<DataFlowLang> TaintTrackingLang,
|
||||
IET::InlineExpectationsTestSig Test, InputSig<DataFlowLang> Impl>
|
||||
LocationSig Location, DF::InputSig<Location> DataFlowLang,
|
||||
TT::InputSig<Location, DataFlowLang> TaintTrackingLang, IET::InlineExpectationsTestSig Test,
|
||||
InputSig<Location, DataFlowLang> Impl>
|
||||
{
|
||||
private module DataFlow = DF::DataFlowMake<DataFlowLang>;
|
||||
private module DataFlow = DF::DataFlowMake<Location, DataFlowLang>;
|
||||
|
||||
private module TaintTracking = TT::TaintFlowMake<DataFlowLang, TaintTrackingLang>;
|
||||
private module TaintTracking = TT::TaintFlowMake<Location, DataFlowLang, TaintTrackingLang>;
|
||||
|
||||
private module InlineExpectationsTest = IET::Make<Test>;
|
||||
|
||||
@@ -76,7 +78,7 @@ module InlineFlowTestMake<
|
||||
|
||||
private predicate hasLocationInfo(DataFlowLang::Node node, Test::Location location) {
|
||||
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
node.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
|
||||
node.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
|
||||
location.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user