Merge from main to resolve conflicts

This commit is contained in:
Dave Bartolomeo
2024-03-19 10:41:31 -04:00
1361 changed files with 222127 additions and 93308 deletions

View File

@@ -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.

View File

@@ -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() }
}
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

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