Data flow: Remove column from mayBenefitFromCallContext

This commit is contained in:
Tom Hvitved
2024-01-09 10:35:53 +01:00
parent 25e2271b2f
commit f90201eb56
17 changed files with 85 additions and 126 deletions

View File

@@ -54,18 +54,6 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
not f.isStatic()
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, Function f) { none() }
/**
* 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.
*/
Function viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
/** A parameter position represented by an integer. */
class ParameterPosition extends int {
ParameterPosition() { any(ParameterNode p).isParameterOf(_, this) }

View File

@@ -249,9 +249,7 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable f) {
mayBenefitFromCallContext(call, f, _)
}
predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallContext(call, _, _) }
/**
* Holds if `call` is a call through a function pointer, and the pointer

View File

@@ -22,4 +22,8 @@ module CppDataFlow implements InputSig {
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
predicate validParameterAliasStep = Private::validParameterAliasStep/2;
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
}

View File

@@ -148,17 +148,17 @@ private module Cached {
import Cached
private module DispatchImpl {
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. This is the case if the
* call is a delegate call, or if the qualifier accesses a parameter of
* the enclosing callable `c` (including the implicit `this` parameter).
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) {
private predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) {
c = call.getEnclosingCallable() and
call.(NonDelegateDataFlowCall).getDispatchCall().mayBenefitFromCallContext()
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallContext(call, _) }
/**
* 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.

View File

@@ -20,4 +20,8 @@ module CsharpDataFlow implements InputSig {
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
predicate accessPathLimit = Private::accessPathLimit/0;
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
}

View File

@@ -588,18 +588,17 @@ However, joining the virtual dispatch relation with itself in this way is
usually way too big to be feasible. Instead, the relation above should only be
defined for those values of `call` for which the set of resulting dispatch
targets might be reduced. To do this, define the set of `call`s that might for
some reason benefit from a call context as the following predicate (the `c`
column should be `call.getEnclosingCallable()`):
some reason benefit from a call context as the following predicate:
```ql
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c)
predicate mayBenefitFromCallContext(DataFlowCall call)
```
And then define `DataFlowCallable viableImplInCallContext(DataFlowCall call,
DataFlowCall ctx)` as sketched above, but restricted to
`mayBenefitFromCallContext(call, _)`.
`mayBenefitFromCallContext(call)`.
The shared implementation will then compare counts of virtual dispatch targets
using `viableCallable` and `viableImplInCallContext` for each `call` in
`mayBenefitFromCallContext(call, _)` and track call contexts during flow
`mayBenefitFromCallContext(call)` and track call contexts during flow
calculation when differences in these counts show an improved precision in
further calls.

View File

@@ -104,18 +104,6 @@ DataFlowCallable viableCallable(DataFlowCall ma) {
)
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable f) { none() }
/**
* 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.
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
private int parameterPosition() {
result = [-1 .. any(DataFlowCallable c).getType().getNumParameter()]
}

View File

@@ -116,10 +116,10 @@ private module DispatchImpl {
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. This is the case if the
* qualifier is a parameter of the enclosing callable `c`.
* qualifier is a parameter of the enclosing callable of `call`.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) {
mayBenefitFromCallContext(call.asCall(), c.asCallable(), _)
predicate mayBenefitFromCallContext(DataFlowCall call) {
mayBenefitFromCallContext(call.asCall(), _, _)
}
/**

View File

@@ -18,4 +18,8 @@ module JavaDataFlow implements InputSig {
import Public
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
}

View File

@@ -1009,22 +1009,6 @@ predicate attributeClearStep(Node n, AttributeContent c) {
*/
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() }
//--------
// Virtual dispatch with call context
//--------
/**
* 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.
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. This is the case if the qualifier accesses a parameter of
* the enclosing callable `c` (including the implicit `this` parameter).
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { none() }
/**
* 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.

View File

@@ -1088,8 +1088,8 @@ private CfgScope getTargetSingleton(RelevantCall call, string method) {
}
/**
* Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, the receiver
* of `call` is a parameter access, where the corresponding argument of `ctx` is `arg`.
* Holds if `ctx` targets the enclosing callable of `call`, the receiver of `call` is a
* parameter access, where the corresponding argument of `ctx` is `arg`.
*
* `name` is the name of the method being called by `call`, `source` is a
* `LocalSourceNode` that flows to `arg`, and `paramDef` is the SSA definition for the
@@ -1098,11 +1098,11 @@ private CfgScope getTargetSingleton(RelevantCall call, string method) {
pragma[nomagic]
private predicate argMustFlowToReceiver(
RelevantCall ctx, DataFlow::LocalSourceNode source, DataFlow::Node arg, RelevantCall call,
Callable encl, string name
string name
) {
exists(
ParameterNodeImpl p, SsaDefinitionExtNode paramDef, ParameterPosition ppos,
ArgumentPosition apos
ArgumentPosition apos, Callable encl
|
// the receiver of `call` references `p`
exists(DataFlow::Node receiver |
@@ -1133,7 +1133,7 @@ private predicate argMustFlowToReceiver(
}
/**
* Holds if `ctx` targets `encl`, which is the enclosing callable of `new`, and
* Holds if `ctx` targets the enclosing callable of `new`, and
* the receiver of `new` is a parameter access, where the corresponding argument
* `arg` of `ctx` has type `tp`.
*
@@ -1141,10 +1141,10 @@ private predicate argMustFlowToReceiver(
*/
pragma[nomagic]
private predicate mayBenefitFromCallContextInitialize(
RelevantCall ctx, RelevantCall new, DataFlow::Node arg, Callable encl, Module tp, string name
RelevantCall ctx, RelevantCall new, DataFlow::Node arg, Module tp, string name
) {
exists(DataFlow::LocalSourceNode source |
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, new, encl, "new") and
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, new, "new") and
source = trackModuleAccess(tp) and
name = "initialize" and
exists(lookupMethod(tp, name))
@@ -1152,7 +1152,7 @@ private predicate mayBenefitFromCallContextInitialize(
}
/**
* Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, and
* Holds if `ctx` targets the enclosing callable of `call`, and
* the receiver of `call` is a parameter access, where the corresponding argument
* `arg` of `ctx` has type `tp`.
*
@@ -1161,11 +1161,10 @@ private predicate mayBenefitFromCallContextInitialize(
*/
pragma[nomagic]
private predicate mayBenefitFromCallContextInstance(
RelevantCall ctx, RelevantCall call, DataFlow::Node arg, Callable encl, Module tp, boolean exact,
string name
RelevantCall ctx, RelevantCall call, DataFlow::Node arg, Module tp, boolean exact, string name
) {
exists(DataFlow::LocalSourceNode source |
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, call, encl,
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, call,
pragma[only_bind_into](name)) and
source = trackInstance(tp, exact) and
exists(lookupMethod(tp, pragma[only_bind_into](name)))
@@ -1173,7 +1172,7 @@ private predicate mayBenefitFromCallContextInstance(
}
/**
* Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, and
* Holds if `ctx` targets the enclosing callable of `call`, and
* the receiver of `call` is a parameter access, where the corresponding argument
* `arg` of `ctx` is a module access targeting a module of type `tp`.
*
@@ -1182,12 +1181,11 @@ private predicate mayBenefitFromCallContextInstance(
*/
pragma[nomagic]
private predicate mayBenefitFromCallContextSingleton(
RelevantCall ctx, RelevantCall call, DataFlow::Node arg, Callable encl, Module tp, boolean exact,
string name
RelevantCall ctx, RelevantCall call, DataFlow::Node arg, Module tp, boolean exact, string name
) {
exists(DataFlow::LocalSourceNode source |
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), pragma[only_bind_into](arg), call,
encl, pragma[only_bind_into](name)) and
pragma[only_bind_into](name)) and
exists(lookupSingletonMethod(tp, pragma[only_bind_into](name), exact))
|
source = trackModuleAccess(tp) and
@@ -1208,16 +1206,14 @@ private predicate mayBenefitFromCallContextSingleton(
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. This is the case if the
* receiver accesses a parameter of the enclosing callable `c` (including
* the implicit `self` parameter).
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) {
mayBenefitFromCallContextInitialize(_, call.asCall(), _, c.asCallable(), _, _)
predicate mayBenefitFromCallContext(DataFlowCall call) {
mayBenefitFromCallContextInitialize(_, call.asCall(), _, _, _)
or
mayBenefitFromCallContextInstance(_, call.asCall(), _, c.asCallable(), _, _, _)
mayBenefitFromCallContextInstance(_, call.asCall(), _, _, _, _)
or
mayBenefitFromCallContextSingleton(_, call.asCall(), _, c.asCallable(), _, _, _)
mayBenefitFromCallContextSingleton(_, call.asCall(), _, _, _, _)
}
/**
@@ -1226,25 +1222,25 @@ predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) {
*/
pragma[nomagic]
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
mayBenefitFromCallContext(call, _) and
mayBenefitFromCallContext(call) and
(
// `ctx` can provide a potentially better type bound
exists(RelevantCall call0, Callable res |
call0 = call.asCall() and
res = result.asCallable() and
exists(Module m, string name |
mayBenefitFromCallContextInitialize(ctx.asCall(), pragma[only_bind_into](call0), _, _,
mayBenefitFromCallContextInitialize(ctx.asCall(), pragma[only_bind_into](call0), _,
pragma[only_bind_into](m), pragma[only_bind_into](name)) and
res = getInitializeTarget(call0) and
res = lookupMethod(m, name)
or
exists(boolean exact |
mayBenefitFromCallContextInstance(ctx.asCall(), pragma[only_bind_into](call0), _, _,
mayBenefitFromCallContextInstance(ctx.asCall(), pragma[only_bind_into](call0), _,
pragma[only_bind_into](m), pragma[only_bind_into](exact), pragma[only_bind_into](name)) and
res = getTargetInstance(call0, name) and
res = lookupMethod(m, name, exact)
or
mayBenefitFromCallContextSingleton(ctx.asCall(), pragma[only_bind_into](call0), _, _,
mayBenefitFromCallContextSingleton(ctx.asCall(), pragma[only_bind_into](call0), _,
pragma[only_bind_into](m), pragma[only_bind_into](exact), pragma[only_bind_into](name)) and
res = getTargetSingleton(call0, name) and
res = lookupSingletonMethod(m, name, exact)
@@ -1257,15 +1253,15 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
exists(RelevantCall call0, RelevantCall ctx0, DataFlow::Node arg, string name |
call0 = call.asCall() and
ctx0 = ctx.asCall() and
argMustFlowToReceiver(ctx0, _, arg, call0, _, name) and
not mayBenefitFromCallContextInitialize(ctx0, call0, arg, _, _, _) and
not mayBenefitFromCallContextInstance(ctx0, call0, arg, _, _, _, name) and
not mayBenefitFromCallContextSingleton(ctx0, call0, arg, _, _, _, name) and
argMustFlowToReceiver(ctx0, _, arg, call0, name) and
not mayBenefitFromCallContextInitialize(ctx0, call0, arg, _, _) and
not mayBenefitFromCallContextInstance(ctx0, call0, arg, _, _, name) and
not mayBenefitFromCallContextSingleton(ctx0, call0, arg, _, _, name) and
result.asCallable() = viableSourceCallable(call0)
)
or
// library calls should always be able to resolve
argMustFlowToReceiver(ctx.asCall(), _, _, call.asCall(), _, _) and
argMustFlowToReceiver(ctx.asCall(), _, _, call.asCall(), _) and
result = viableLibraryCallable(call)
)
}

View File

@@ -27,4 +27,8 @@ module RubyDataFlow implements InputSig {
predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1;
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
}

View File

@@ -194,24 +194,24 @@ subpaths
| call_sensitivity.rb:105:10:105:10 | x | call_sensitivity.rb:178:11:178:19 | call to taint | call_sensitivity.rb:105:10:105:10 | x | $@ | call_sensitivity.rb:178:11:178:19 | call to taint | call to taint |
| call_sensitivity.rb:105:10:105:10 | x | call_sensitivity.rb:187:12:187:19 | call to taint | call_sensitivity.rb:105:10:105:10 | x | $@ | call_sensitivity.rb:187:12:187:19 | call to taint | call to taint |
mayBenefitFromCallContext
| call_sensitivity.rb:51:5:51:10 | call to sink | call_sensitivity.rb:50:3:52:5 | method1 |
| call_sensitivity.rb:55:5:55:13 | call to method1 | call_sensitivity.rb:54:3:56:5 | method2 |
| call_sensitivity.rb:59:5:59:18 | call to method2 | call_sensitivity.rb:58:3:60:5 | call_method2 |
| call_sensitivity.rb:63:5:63:16 | call to method1 | call_sensitivity.rb:62:3:64:5 | method3 |
| call_sensitivity.rb:67:5:67:25 | call to method3 | call_sensitivity.rb:66:3:68:5 | call_method3 |
| call_sensitivity.rb:81:5:81:18 | call to method1 | call_sensitivity.rb:80:3:82:5 | method5 |
| call_sensitivity.rb:89:5:89:23 | call to singleton_method1 | call_sensitivity.rb:88:3:90:5 | singleton_method2 |
| call_sensitivity.rb:93:5:93:28 | call to singleton_method2 | call_sensitivity.rb:92:3:94:5 | call_singleton_method2 |
| call_sensitivity.rb:97:5:97:26 | call to singleton_method1 | call_sensitivity.rb:96:3:98:5 | singleton_method3 |
| call_sensitivity.rb:101:5:101:35 | call to singleton_method3 | call_sensitivity.rb:100:3:102:5 | call_singleton_method3 |
| call_sensitivity.rb:105:5:105:10 | call to sink | call_sensitivity.rb:104:3:107:5 | initialize |
| call_sensitivity.rb:106:5:106:13 | call to method1 | call_sensitivity.rb:104:3:107:5 | initialize |
| call_sensitivity.rb:110:5:110:9 | call to new | call_sensitivity.rb:109:3:111:5 | call_new |
| call_sensitivity.rb:137:5:137:18 | call to method2 | call_sensitivity.rb:136:3:138:5 | call_method2 |
| call_sensitivity.rb:141:5:141:25 | call to method3 | call_sensitivity.rb:140:3:142:5 | call_method3 |
| call_sensitivity.rb:149:5:149:28 | call to singleton_method2 | call_sensitivity.rb:148:3:150:5 | call_singleton_method2 |
| call_sensitivity.rb:153:5:153:35 | call to singleton_method3 | call_sensitivity.rb:152:3:154:5 | call_singleton_method3 |
| call_sensitivity.rb:175:3:175:12 | call to new | call_sensitivity.rb:174:1:176:3 | create |
| call_sensitivity.rb:51:5:51:10 | call to sink |
| call_sensitivity.rb:55:5:55:13 | call to method1 |
| call_sensitivity.rb:59:5:59:18 | call to method2 |
| call_sensitivity.rb:63:5:63:16 | call to method1 |
| call_sensitivity.rb:67:5:67:25 | call to method3 |
| call_sensitivity.rb:81:5:81:18 | call to method1 |
| call_sensitivity.rb:89:5:89:23 | call to singleton_method1 |
| call_sensitivity.rb:93:5:93:28 | call to singleton_method2 |
| call_sensitivity.rb:97:5:97:26 | call to singleton_method1 |
| call_sensitivity.rb:101:5:101:35 | call to singleton_method3 |
| call_sensitivity.rb:105:5:105:10 | call to sink |
| call_sensitivity.rb:106:5:106:13 | call to method1 |
| call_sensitivity.rb:110:5:110:9 | call to new |
| call_sensitivity.rb:137:5:137:18 | call to method2 |
| call_sensitivity.rb:141:5:141:25 | call to method3 |
| call_sensitivity.rb:149:5:149:28 | call to singleton_method2 |
| call_sensitivity.rb:153:5:153:35 | call to singleton_method3 |
| call_sensitivity.rb:175:3:175:12 | call to new |
viableImplInCallContext
| call_sensitivity.rb:51:5:51:10 | call to sink | call_sensitivity.rb:55:5:55:13 | call to method1 | call_sensitivity.rb:5:1:7:3 | sink |
| call_sensitivity.rb:51:5:51:10 | call to sink | call_sensitivity.rb:63:5:63:16 | call to method1 | call_sensitivity.rb:5:1:7:3 | sink |

View File

@@ -9,7 +9,7 @@ import DefaultFlowTest
import TaintFlow::PathGraph
import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
query predicate mayBenefitFromCallContext = DataFlowDispatch::mayBenefitFromCallContext/2;
query predicate mayBenefitFromCallContext = DataFlowDispatch::mayBenefitFromCallContext/1;
query predicate viableImplInCallContext = DataFlowDispatch::viableImplInCallContext/2;

View File

@@ -77,13 +77,13 @@ signature module InputSig {
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c);
default predicate mayBenefitFromCallContext(DataFlowCall call) { none() }
/**
* 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.
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx);
default DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
/**
* Gets a node that can read the value returned from `call` with return kind

View File

@@ -791,10 +791,12 @@ module MakeImplCommon<InputSig Lang> {
*/
pragma[nomagic]
private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
mayBenefitFromCallContext(call, callable)
or
callEnclosingCallable(call, callable) and
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
(
mayBenefitFromCallContext(call)
or
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
) and
callEnclosingCallable(call, callable)
}
/**

View File

@@ -302,18 +302,6 @@ private module Cached {
import Cached
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { none() }
/**
* 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.
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
/** A parameter position. */
class ParameterPosition extends TParameterPosition {
/** Gets a textual representation of this position. */