Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 6 additions & 19 deletions cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
}

/**
* Holds if the call context `ctx` reduces the set of viable dispatch
* targets of `ma` in `c`.
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate reducedViableImplInCallContext(Call call, Function f, Call ctx) { none() }
predicate mayBenefitFromCallContext(Call call, Function f) { none() }

/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s for which the context makes a difference.
* 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 prunedViableImplInCallContext(Call call, Call ctx) { none() }

/**
* Holds if flow returning from `m` to `ma` might return further and if
* this path restricts the set of call sites that can be returned to.
*/
predicate reducedViableImplInReturn(Function f, Call call) { none() }

/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s and results for which the return flow from the
* result to `ma` restricts the possible context `ctx`.
*/
Function prunedViableImplInCallContextReverse(Call call, Call ctx) { none() }
Function viableImplInCallContext(Call call, Call ctx) { none() }
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,67 @@ private module Cached {
import Final
}

import FlowThrough

cached
private module DispatchWithCallContext {
/**
* 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 |
mayBenefitFromCallContext(call, c) and
c = viableCallable(ctx) and
ctxtgts = count(viableImplInCallContext(call, ctx)) and
tgts = strictcount(viableCallable(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, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}

/**
* 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 |
mayBenefitFromCallContext(call, _) and
c = viableCallable(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}

/**
* Gets a viable run-time dispatch target for the call `call` in the
* context `ctx`. This is restricted to those calls and results for which
* the return flow from the result to `call` restricts the possible context
* `ctx`.
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInReturn(result, call)
}
}

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.
Expand Down Expand Up @@ -371,8 +432,6 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}

import FlowThrough

/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,28 +226,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
}

/**
* Holds if the call context `ctx` reduces the set of viable dispatch
* targets of `ma` in `c`.
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate reducedViableImplInCallContext(CallInstruction call, Function f, CallInstruction ctx) {
none()
}

/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s for which the context makes a difference.
*/
Function prunedViableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }

/**
* Holds if flow returning from `m` to `ma` might return further and if
* this path restricts the set of call sites that can be returned to.
*/
predicate reducedViableImplInReturn(Function f, CallInstruction call) { none() }
predicate mayBenefitFromCallContext(CallInstruction call, Function f) { none() }

/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s and results for which the return flow from the
* result to `ma` restricts the possible context `ctx`.
* 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 prunedViableImplInCallContextReverse(CallInstruction call, CallInstruction ctx) { none() }
Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,67 @@ private module Cached {
import Final
}

import FlowThrough

cached
private module DispatchWithCallContext {
/**
* 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 |
mayBenefitFromCallContext(call, c) and
c = viableCallable(ctx) and
ctxtgts = count(viableImplInCallContext(call, ctx)) and
tgts = strictcount(viableCallable(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, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}

/**
* 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 |
mayBenefitFromCallContext(call, _) and
c = viableCallable(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}

/**
* Gets a viable run-time dispatch target for the call `call` in the
* context `ctx`. This is restricted to those calls and results for which
* the return flow from the result to `call` restricts the possible context
* `ctx`.
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInReturn(result, call)
}
}

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.
Expand Down Expand Up @@ -371,8 +432,6 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}

import FlowThrough

/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c

cached
private module Cached {
private import CallContext
private import semmle.code.csharp.Caching

cached
Expand Down Expand Up @@ -108,6 +107,12 @@ private module Cached {
/** Gets a viable run-time target for the call `call`. */
cached
DataFlowCallable viableImpl(DataFlowCall call) { result = call.getARuntimeTarget() }
}

import Cached

private module DispatchImpl {
private import CallContext

/**
* Gets a viable run-time target for the delegate call `call`, requiring
Expand All @@ -123,7 +128,7 @@ private module Cached {
* call is a delegate call, or if the qualifier accesses a parameter of
* the enclosing callable `c` (including the implicit `this` parameter).
*/
private predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) {
predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) {
c = call.getEnclosingCallable() and
(
exists(CallContext cc | exists(viableDelegateCallable(call, cc)) |
Expand All @@ -134,26 +139,11 @@ private module Cached {
)
}

/**
* Holds if the call context `ctx` reduces the set of viable run-time
* targets of call `call` in `c`.
*/
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
mayBenefitFromCallContext(call, c) and
c = viableCallable(ctx) and
ctxtgts = count(viableImplInCallContext(call, ctx)) and
tgts = strictcount(viableImpl(call)) and
ctxtgts < tgts
)
}

/**
* 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.
*/
private DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) |
cc.isArgument(ctx.getExpr(), _)
)
Expand All @@ -164,47 +154,9 @@ private module Cached {
.getDispatchCall()
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
}

/**
* Gets a viable run-time target for the call `call` in the context
* `ctx`. This is restricted to those call nodes for which a context
* might make a difference.
*/
cached
DotNet::Callable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}

/**
* 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 |
mayBenefitFromCallContext(call, _) and
c = viableImpl(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}

/**
* Gets a viable run-time target for the call `call` in the context `ctx`.
* This is restricted to those call nodes and results for which the return
* flow from the result to `call` restricts the possible context `ctx`.
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInReturn(result, call)
}
}

import Cached
import DispatchImpl

/**
* Gets a node that can read the value returned from `call` with return kind
Expand Down
Loading