mirror of
https://github.com/github/codeql.git
synced 2026-05-02 20:25:13 +02:00
Merge branch 'main' into js/shared-dataflow
This commit is contained in:
@@ -1,3 +1,21 @@
|
||||
## 1.0.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The data flow library now adds intermediate nodes when data flows out of a function via a parameter, in order to make path explanations easier to follow. The intermediate nodes have the same location as the underlying parameter, but must be accessed via `PathNode.asParameterReturnNode` instead of `PathNode.asNode`.
|
||||
|
||||
## 0.2.7
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.2.6
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
3
shared/dataflow/change-notes/released/0.2.7.md
Normal file
3
shared/dataflow/change-notes/released/0.2.7.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.2.7
|
||||
|
||||
No user-facing changes.
|
||||
9
shared/dataflow/change-notes/released/1.0.0.md
Normal file
9
shared/dataflow/change-notes/released/1.0.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The data flow library now adds intermediate nodes when data flows out of a function via a parameter, in order to make path explanations easier to follow. The intermediate nodes have the same location as the underlying parameter, but must be accessed via `PathNode.asParameterReturnNode` instead of `PathNode.asNode`.
|
||||
3
shared/dataflow/change-notes/released/1.0.1.md
Normal file
3
shared/dataflow/change-notes/released/1.0.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.0.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.6
|
||||
lastReleaseVersion: 1.0.1
|
||||
|
||||
@@ -72,12 +72,24 @@ signature module InputSig<LocationSig Location> {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this call. */
|
||||
Location getLocation();
|
||||
|
||||
DataFlowCallable getEnclosingCallable();
|
||||
|
||||
/** Gets a best-effort total ordering. */
|
||||
int totalorder();
|
||||
}
|
||||
|
||||
class DataFlowCallable {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this callable. */
|
||||
Location getLocation();
|
||||
|
||||
/** Gets a best-effort total ordering. */
|
||||
int totalorder();
|
||||
}
|
||||
|
||||
class ReturnKind {
|
||||
@@ -130,7 +142,6 @@ signature module InputSig<LocationSig Location> {
|
||||
* steps, then it will check that the types of `n1` and `n2` are compatible.
|
||||
* If they are not, then flow will be blocked.
|
||||
*/
|
||||
bindingset[t1, t2]
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2);
|
||||
|
||||
/**
|
||||
@@ -251,10 +262,18 @@ signature module InputSig<LocationSig Location> {
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c);
|
||||
|
||||
/** A set of `Node`s in a `DataFlowCallable`. */
|
||||
class NodeRegion {
|
||||
/** Holds if this region contains `n`. */
|
||||
predicate contains(Node n);
|
||||
|
||||
int totalOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is unreachable when the call context is `call`.
|
||||
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
|
||||
*/
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call);
|
||||
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call);
|
||||
|
||||
default int accessPathLimit() { result = 5 }
|
||||
|
||||
@@ -609,7 +628,7 @@ module DataFlowMake<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate accessPathLimit = Config::accessPathLimit/0;
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = ""
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = "Config"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,7 +650,7 @@ module DataFlowMake<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate accessPathLimit = Config::accessPathLimit/0;
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = ""
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = "Config"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ module TaintFlowMake<
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlowLang::Node node1, DataFlowLang::Node node2, string model
|
||||
) {
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = ""
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = "Config"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ module TaintFlowMake<
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlowLang::Node node1, DataFlowLang::Node node2, string model
|
||||
) {
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = ""
|
||||
Config::isAdditionalFlowStep(node1, node2) and model = "Config"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
TNodeNormal(Node n) or
|
||||
TNodeImplicitRead(Node n, boolean hasRead) {
|
||||
Config::allowImplicitRead(n, _) and hasRead = [false, true]
|
||||
} or
|
||||
TParamReturnNode(ParameterNode p, SndLevelScopeOption scope) {
|
||||
paramReturnNode(_, p, scope, _)
|
||||
}
|
||||
|
||||
private class NodeEx extends TNodeEx {
|
||||
@@ -170,13 +173,21 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
result = this.asNode().toString()
|
||||
or
|
||||
exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
|
||||
or
|
||||
result = this.asParamReturnNode().toString() + " [Return]"
|
||||
}
|
||||
|
||||
Node asNode() { this = TNodeNormal(result) }
|
||||
|
||||
predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
|
||||
|
||||
Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
|
||||
ParameterNode asParamReturnNode() { this = TParamReturnNode(result, _) }
|
||||
|
||||
Node projectToNode() {
|
||||
this = TNodeNormal(result) or
|
||||
this = TNodeImplicitRead(result, _) or
|
||||
this = TParamReturnNode(result, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable getEnclosingCallable0() {
|
||||
@@ -189,7 +200,11 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
|
||||
private DataFlowType getDataFlowType0() {
|
||||
nodeDataFlowType(this.asNode(), result)
|
||||
or
|
||||
nodeDataFlowType(this.asParamReturnNode(), result)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
DataFlowType getDataFlowType() {
|
||||
@@ -215,12 +230,21 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node from which flow can return to the caller. This is either a regular
|
||||
* `ReturnNode` or a synthesized node for flow out via a parameter.
|
||||
*/
|
||||
private class RetNodeEx extends NodeEx {
|
||||
RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
|
||||
private ReturnPosition pos;
|
||||
|
||||
ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
|
||||
RetNodeEx() {
|
||||
pos = getValueReturnPosition(this.asNode()) or
|
||||
pos = getParamReturnPosition(_, this.asParamReturnNode())
|
||||
}
|
||||
|
||||
ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
|
||||
ReturnPosition getReturnPosition() { result = pos }
|
||||
|
||||
ReturnKindExt getKind() { result = pos.getKind() }
|
||||
}
|
||||
|
||||
private predicate inBarrier(NodeEx node) {
|
||||
@@ -324,9 +348,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
not stateBarrier(node2, state2)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
bindingset[n, cc]
|
||||
pragma[inline_late]
|
||||
private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) {
|
||||
isUnreachableInCallCached(n.asNode(), cc.getCall())
|
||||
cc.unreachable(n.asNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,12 +366,19 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
or
|
||||
exists(Node n |
|
||||
Config::allowImplicitRead(n, _) and
|
||||
node1.asNode() = n and
|
||||
node2.isImplicitReadNode(n, false) and
|
||||
not fullBarrier(node1) and
|
||||
model = ""
|
||||
)
|
||||
or
|
||||
exists(Node n1, Node n2, SndLevelScopeOption scope |
|
||||
node1.asNode() = n1 and
|
||||
node2 = TParamReturnNode(n2, scope) and
|
||||
paramReturnNode(pragma[only_bind_into](n1), pragma[only_bind_into](n2),
|
||||
pragma[only_bind_into](scope), _) and
|
||||
model = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,7 +394,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
or
|
||||
exists(Node n |
|
||||
Config::allowImplicitRead(n, _) and
|
||||
node1.isImplicitReadNode(n, true) and
|
||||
node2.asNode() = n and
|
||||
not fullBarrier(node2) and
|
||||
@@ -497,10 +528,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sourceCallCtx(CallContext cc) {
|
||||
if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx() {
|
||||
exists(FlowFeature feature | feature = Config::getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
@@ -1113,26 +1140,17 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode())
|
||||
}
|
||||
|
||||
private module SndLevelScopeOption = Option<DataFlowSecondLevelScope>;
|
||||
|
||||
private class SndLevelScopeOption = SndLevelScopeOption::Option;
|
||||
|
||||
pragma[nomagic]
|
||||
private SndLevelScopeOption getScope(RetNodeEx ret) {
|
||||
result = SndLevelScopeOption::some(getSecondLevelScopeCached(ret.asNode()))
|
||||
or
|
||||
result instanceof SndLevelScopeOption::None and
|
||||
not exists(getSecondLevelScopeCached(ret.asNode()))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnCallEdge1(
|
||||
DataFlowCallable c, SndLevelScopeOption scope, DataFlowCall call, NodeEx out
|
||||
) {
|
||||
exists(RetNodeEx ret |
|
||||
flowOutOfCallNodeCand1(call, ret, _, out) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
scope = getScope(ret)
|
||||
c = ret.getEnclosingCallable()
|
||||
|
|
||||
scope = getSecondLevelScopeCached(ret.asNode())
|
||||
or
|
||||
ret = TParamReturnNode(_, scope)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1352,6 +1370,18 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
CcCall ccSomeCall();
|
||||
|
||||
/*
|
||||
* The following `instanceof` predicates are necessary for proper
|
||||
* caching, since we're able to cache predicates, but not the underlying
|
||||
* types.
|
||||
*/
|
||||
|
||||
predicate instanceofCc(Cc cc);
|
||||
|
||||
predicate instanceofCcCall(CcCall cc);
|
||||
|
||||
predicate instanceofCcNoCall(CcNoCall cc);
|
||||
|
||||
class LocalCc;
|
||||
|
||||
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx);
|
||||
@@ -1362,15 +1392,15 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
bindingset[call, c]
|
||||
CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c);
|
||||
|
||||
DataFlowCallable viableImplCallContextReducedReverse(DataFlowCall call, CcNoCall ctx);
|
||||
DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable c, CcNoCall ctx);
|
||||
|
||||
predicate viableImplNotCallContextReducedReverse(CcNoCall ctx);
|
||||
|
||||
bindingset[call, c]
|
||||
CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call);
|
||||
|
||||
bindingset[node, cc]
|
||||
LocalCc getLocalCc(NodeEx node, Cc cc);
|
||||
bindingset[cc]
|
||||
LocalCc getLocalCc(Cc cc);
|
||||
|
||||
bindingset[node1, state1]
|
||||
bindingset[node2, state2]
|
||||
@@ -1414,6 +1444,31 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate compatibleContainer0(ApHeadContent apc, DataFlowType containerType) {
|
||||
exists(DataFlowType containerType0, Content c |
|
||||
PrevStage::storeStepCand(_, _, c, _, _, containerType0) and
|
||||
not isTopType(containerType0) and
|
||||
compatibleTypesCached(containerType0, containerType) and
|
||||
apc = projectToHeadContent(c)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate topTypeContent(ApHeadContent apc) {
|
||||
exists(DataFlowType containerType0, Content c |
|
||||
PrevStage::storeStepCand(_, _, c, _, _, containerType0) and
|
||||
isTopType(containerType0) and
|
||||
apc = projectToHeadContent(c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[apc, containerType]
|
||||
pragma[inline_late]
|
||||
private predicate compatibleContainer(ApHeadContent apc, DataFlowType containerType) {
|
||||
compatibleContainer0(apc, containerType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable with access path `ap` from a source.
|
||||
*
|
||||
@@ -1435,7 +1490,15 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and
|
||||
PrevStage::revFlow(node, state, apa) and
|
||||
filter(node, state, t0, ap, t)
|
||||
filter(node, state, t0, ap, t) and
|
||||
(
|
||||
if castingNodeEx(node)
|
||||
then
|
||||
ap instanceof ApNil or
|
||||
compatibleContainer(getHeadContent(ap), node.getDataFlowType()) or
|
||||
topTypeContent(getHeadContent(ap))
|
||||
else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1459,7 +1522,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
localCc = getLocalCc(cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, localCc) and
|
||||
t = t0
|
||||
@@ -1596,7 +1659,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
ApOption argAp, Typ t, Ap ap, boolean emptyAp, ApApprox apa, boolean cc
|
||||
) {
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and
|
||||
(if outercc instanceof CcCall then cc = true else cc = false) and
|
||||
(if instanceofCcCall(outercc) then cc = true else cc = false) and
|
||||
if ap instanceof ApNil then emptyAp = true else emptyAp = false
|
||||
}
|
||||
|
||||
@@ -1652,6 +1715,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
DataFlowCall call, ArgNodeEx arg, CcCall ctx
|
||||
) {
|
||||
callEdgeArgParamRestricted(call, _, arg, _, _, _) and
|
||||
instanceofCcCall(ctx) and
|
||||
result = viableImplCallContextReducedInlineLate(call, ctx)
|
||||
}
|
||||
|
||||
@@ -1667,6 +1731,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
bindingset[call, ctx]
|
||||
pragma[inline_late]
|
||||
private predicate viableImplNotCallContextReducedInlineLate(DataFlowCall call, Cc ctx) {
|
||||
instanceofCc(ctx) and
|
||||
viableImplNotCallContextReduced(call, ctx)
|
||||
}
|
||||
|
||||
@@ -1676,6 +1741,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
DataFlowCall call, ArgNodeEx arg, Cc outercc
|
||||
) {
|
||||
callEdgeArgParamRestricted(call, _, arg, _, _, _) and
|
||||
instanceofCc(outercc) and
|
||||
viableImplNotCallContextReducedInlineLate(call, outercc)
|
||||
}
|
||||
|
||||
@@ -1780,19 +1846,19 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplCallContextReducedReverseRestricted(
|
||||
DataFlowCall call, CcNoCall ctx
|
||||
private DataFlowCall viableImplCallContextReducedReverseRestricted(
|
||||
DataFlowCallable c, CcNoCall ctx
|
||||
) {
|
||||
result = viableImplCallContextReducedReverse(call, ctx) and
|
||||
PrevStage::callEdgeReturn(call, result, _, _, _, _, _)
|
||||
result = viableImplCallContextReducedReverse(c, ctx) and
|
||||
PrevStage::callEdgeReturn(result, c, _, _, _, _, _)
|
||||
}
|
||||
|
||||
bindingset[ctx, result]
|
||||
bindingset[c, ctx]
|
||||
pragma[inline_late]
|
||||
private DataFlowCallable viableImplCallContextReducedReverseInlineLate(
|
||||
DataFlowCall call, CcNoCall ctx
|
||||
private DataFlowCall viableImplCallContextReducedReverseInlineLate(
|
||||
DataFlowCallable c, CcNoCall ctx
|
||||
) {
|
||||
result = viableImplCallContextReducedReverseRestricted(call, ctx)
|
||||
result = viableImplCallContextReducedReverseRestricted(c, ctx)
|
||||
}
|
||||
|
||||
bindingset[call]
|
||||
@@ -1820,6 +1886,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
RetNodeEx ret, FlowState state, CcNoCall cc, ParamNodeOption summaryCtx, TypOption argT,
|
||||
ApOption argAp, Typ t, Ap ap, ApApprox apa
|
||||
) {
|
||||
instanceofCcNoCall(cc) and
|
||||
fwdFlow(ret, state, cc, summaryCtx, argT, argAp, t, ap, apa)
|
||||
}
|
||||
|
||||
@@ -1831,7 +1898,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
fwdFlowIntoRet(ret, _, innercc, _, _, _, _, _, apa) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
(
|
||||
inner = viableImplCallContextReducedReverseInlineLate(call, innercc) and
|
||||
call = viableImplCallContextReducedReverseInlineLate(inner, innercc) and
|
||||
flowOutOfCallApaInlineLate(call, inner, ret, out, allowsFieldFlow, apa)
|
||||
or
|
||||
flowOutOfCallApaNotCallContextReduced(call, inner, ret, out, allowsFieldFlow, apa,
|
||||
@@ -1879,6 +1946,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow1Param(ParamNodeEx p, FlowState state, CcCall cc, Typ t0, Ap ap) {
|
||||
instanceofCcCall(cc) and
|
||||
fwdFlow1(p, state, cc, _, _, _, t0, _, ap, _)
|
||||
}
|
||||
|
||||
@@ -1945,6 +2013,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
ApApprox argApa, Typ t, Ap ap, ApApprox apa
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
instanceofCcCall(ccc) and
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT),
|
||||
pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and
|
||||
@@ -2510,10 +2579,16 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
predicate instanceofCc(Cc cc) { any() }
|
||||
|
||||
predicate instanceofCcCall(CcCall cc) { any() }
|
||||
|
||||
predicate instanceofCcNoCall(CcNoCall cc) { any() }
|
||||
|
||||
class LocalCc = Unit;
|
||||
|
||||
bindingset[node, cc]
|
||||
LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
|
||||
bindingset[cc]
|
||||
LocalCc getLocalCc(Cc cc) { any() }
|
||||
|
||||
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) { none() }
|
||||
|
||||
@@ -2523,9 +2598,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
bindingset[call, c]
|
||||
CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c) { any() }
|
||||
|
||||
DataFlowCallable viableImplCallContextReducedReverse(DataFlowCall call, CcNoCall ctx) {
|
||||
none()
|
||||
}
|
||||
DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable c, CcNoCall ctx) { none() }
|
||||
|
||||
predicate viableImplNotCallContextReducedReverse(CcNoCall ctx) { any() }
|
||||
|
||||
@@ -2533,105 +2606,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
}
|
||||
|
||||
private signature module Level1CallContextInputSig {
|
||||
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, CallContextSpecificCall ctx);
|
||||
|
||||
bindingset[call, ctx]
|
||||
predicate noPrunedViableImplInCallContext(DataFlowCall call, CallContext ctx);
|
||||
|
||||
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable);
|
||||
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable);
|
||||
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call);
|
||||
|
||||
DataFlowCall prunedViableImplInCallContextReverse(
|
||||
DataFlowCallable callable, CallContextReturn ctx
|
||||
);
|
||||
}
|
||||
|
||||
private module Level1CallContext<Level1CallContextInputSig Input> {
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
pragma[inline]
|
||||
predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
|
||||
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
module NoLocalCallContext {
|
||||
class LocalCc = Unit;
|
||||
|
||||
bindingset[node, cc]
|
||||
LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
|
||||
|
||||
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) {
|
||||
result = Input::prunedViableImplInCallContext(call, ctx)
|
||||
}
|
||||
|
||||
bindingset[call, ctx]
|
||||
predicate viableImplNotCallContextReduced(DataFlowCall call, Cc ctx) {
|
||||
Input::noPrunedViableImplInCallContext(call, ctx)
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c) {
|
||||
if Input::recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
}
|
||||
|
||||
module LocalCallContext {
|
||||
class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[node, cc]
|
||||
LocalCc getLocalCc(NodeEx node, Cc cc) {
|
||||
result =
|
||||
getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
|
||||
node.getEnclosingCallable())
|
||||
}
|
||||
|
||||
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) {
|
||||
result = Input::prunedViableImplInCallContext(call, ctx)
|
||||
}
|
||||
|
||||
bindingset[call, ctx]
|
||||
predicate viableImplNotCallContextReduced(DataFlowCall call, Cc ctx) {
|
||||
Input::noPrunedViableImplInCallContext(call, ctx)
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c) {
|
||||
if
|
||||
Input::recordDataFlowCallSiteDispatch(call, c) or
|
||||
Input::recordDataFlowCallSiteUnreachable(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
}
|
||||
|
||||
DataFlowCallable viableImplCallContextReducedReverse(DataFlowCall call, CcNoCall ctx) {
|
||||
call = Input::prunedViableImplInCallContextReverse(result, ctx)
|
||||
}
|
||||
|
||||
predicate viableImplNotCallContextReducedReverse(CcNoCall ctx) {
|
||||
ctx instanceof CallContextAny
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
if Input::reducedViableImplInReturn(c, call)
|
||||
then result = TReturn(c, call)
|
||||
else result = ccNone()
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
private module PrevStage = Stage1;
|
||||
|
||||
@@ -2669,11 +2643,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
private module Level1CallContextInput implements Level1CallContextInputSig {
|
||||
import CachedCallContextSensitivity
|
||||
}
|
||||
|
||||
import Level1CallContext<Level1CallContextInput>
|
||||
import CachedCallContextSensitivity
|
||||
import NoLocalCallContext
|
||||
|
||||
bindingset[node1, state1]
|
||||
@@ -2831,7 +2801,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](state2), false)
|
||||
or
|
||||
additionalLocalStateStep(node1, state1, node2, state2) and
|
||||
label = "" and
|
||||
label = "Config" and
|
||||
Stage2::revFlow(node1, state1, false) and
|
||||
Stage2::revFlow(node2, state2, false)
|
||||
}
|
||||
@@ -2916,12 +2886,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
private predicate castingNodeEx(NodeEx node) {
|
||||
node.asNode() instanceof CastingNode or exists(node.asParamReturnNode())
|
||||
}
|
||||
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Typ = DataFlowType;
|
||||
class Typ = Unit;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
@@ -2929,7 +2901,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
Typ getTyp(DataFlowType t) { result = t }
|
||||
Typ getTyp(DataFlowType t) { any() }
|
||||
|
||||
bindingset[c, t, tail]
|
||||
Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) }
|
||||
@@ -2947,30 +2919,27 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
additional module Level1CallContextInput implements Level1CallContextInputSig {
|
||||
private module CallContextSensitivityInput implements CallContextSensitivityInputSig {
|
||||
predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2;
|
||||
private module CallContextSensitivityInput implements CallContextSensitivityInputSig {
|
||||
predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2;
|
||||
|
||||
predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2;
|
||||
predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2;
|
||||
|
||||
predicate reducedViableImplInCallContextCand =
|
||||
CachedCallContextSensitivity::reducedViableImplInCallContext/3;
|
||||
predicate reducedViableImplInCallContextCand =
|
||||
CachedCallContextSensitivity::reducedViableImplInCallContext/3;
|
||||
|
||||
predicate reducedViableImplInReturnCand =
|
||||
CachedCallContextSensitivity::reducedViableImplInReturn/2;
|
||||
}
|
||||
|
||||
import CallContextSensitivity<CallContextSensitivityInput>
|
||||
predicate reducedViableImplInReturnCand =
|
||||
CachedCallContextSensitivity::reducedViableImplInReturn/2;
|
||||
}
|
||||
|
||||
import Level1CallContext<Level1CallContextInput>
|
||||
import CallContextSensitivity<CallContextSensitivityInput>
|
||||
import NoLocalCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
Typ t, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _, _) and
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, _) and
|
||||
exists(t) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
@@ -2993,7 +2962,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
// the cons candidates including types are used to construct subsequent
|
||||
// access path approximations.
|
||||
t0 = t and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
@@ -3002,11 +2970,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
}
|
||||
|
||||
bindingset[typ, contentType]
|
||||
predicate typecheckStore(Typ typ, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(typ, contentType)
|
||||
}
|
||||
predicate typecheckStore(Typ typ, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage3 = MkStage<Stage2>::Stage<Stage3Param>;
|
||||
@@ -3016,7 +2980,11 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
if castingNodeEx(node)
|
||||
then
|
||||
exists(DataFlowType nt | nt = node.getDataFlowType() |
|
||||
if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0)
|
||||
if typeStrongerThanFilter(nt, t0)
|
||||
then t = nt
|
||||
else (
|
||||
compatibleTypesFilter(nt, t0) and t = t0
|
||||
)
|
||||
)
|
||||
else t = t0
|
||||
}
|
||||
@@ -3115,7 +3083,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate typecheckStore(Typ typ, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(typ, contentType)
|
||||
compatibleTypesFilter(typ, contentType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3339,23 +3307,18 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
|
||||
|
||||
additional module Level1CallContextInput implements Level1CallContextInputSig {
|
||||
private module CallContextSensitivityInput implements CallContextSensitivityInputSig {
|
||||
predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2;
|
||||
private module CallContextSensitivityInput implements CallContextSensitivityInputSig {
|
||||
predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2;
|
||||
|
||||
predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2;
|
||||
predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2;
|
||||
|
||||
predicate reducedViableImplInCallContextCand =
|
||||
Stage3Param::Level1CallContextInput::reducedViableImplInCallContext/3;
|
||||
predicate reducedViableImplInCallContextCand =
|
||||
Stage3Param::reducedViableImplInCallContext/3;
|
||||
|
||||
predicate reducedViableImplInReturnCand =
|
||||
Stage3Param::Level1CallContextInput::reducedViableImplInReturn/2;
|
||||
}
|
||||
|
||||
import CallContextSensitivity<CallContextSensitivityInput>
|
||||
predicate reducedViableImplInReturnCand = Stage3Param::reducedViableImplInReturn/2;
|
||||
}
|
||||
|
||||
import Level1CallContext<Level1CallContextInput>
|
||||
import CallContextSensitivity<CallContextSensitivityInput>
|
||||
import LocalCallContext
|
||||
|
||||
predicate localStep(
|
||||
@@ -3376,7 +3339,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
bindingset[typ, contentType]
|
||||
predicate typecheckStore(Typ typ, DataFlowType contentType) {
|
||||
compatibleTypes(typ, contentType)
|
||||
compatibleTypesFilter(typ, contentType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3389,7 +3352,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage5::parameterMayFlowThrough(p, _) and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _,
|
||||
Stage5::fwdFlow(n, state, any(Stage5Param::CcCall ccc), TParamNodeSome(p.asNode()), _,
|
||||
TAccessPathApproxSome(apa), _, apa0, _)
|
||||
)
|
||||
}
|
||||
@@ -3443,6 +3406,11 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
Location getLocation() { result = p.getLocation() }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stage5ConsCand(Content c, DataFlowType t, AccessPathFront apf, int len) {
|
||||
Stage5::consCand(c, t, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of length 2 access path approximations that correspond to `apa`.
|
||||
*/
|
||||
@@ -3450,11 +3418,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
exists(Content c, int len |
|
||||
c = apa.getHead() and
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(DataFlowType t, AccessPathFront apf |
|
||||
Stage5::consCand(c, t,
|
||||
any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1))
|
||||
)
|
||||
result = strictcount(DataFlowType t, AccessPathFront apf | stage5ConsCand(c, t, apf, len))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,6 +3531,36 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
private module PrunedCallContextSensitivityStage5 {
|
||||
private module CallContextSensitivityInput implements CallContextSensitivityInputSig {
|
||||
predicate relevantCallEdgeIn = Stage5::relevantCallEdgeIn/2;
|
||||
|
||||
predicate relevantCallEdgeOut = Stage5::relevantCallEdgeOut/2;
|
||||
|
||||
predicate reducedViableImplInCallContextCand =
|
||||
Stage5Param::reducedViableImplInCallContext/3;
|
||||
|
||||
predicate reducedViableImplInReturnCand = Stage5Param::reducedViableImplInReturn/2;
|
||||
}
|
||||
|
||||
import CallContextSensitivity<CallContextSensitivityInput>
|
||||
import LocalCallContext
|
||||
}
|
||||
|
||||
private class CallContext = PrunedCallContextSensitivityStage5::Cc;
|
||||
|
||||
private class CallContextCall = PrunedCallContextSensitivityStage5::CcCall;
|
||||
|
||||
private class CallContextNoCall = PrunedCallContextSensitivityStage5::CcNoCall;
|
||||
|
||||
private predicate callContextNone = PrunedCallContextSensitivityStage5::ccNone/0;
|
||||
|
||||
private predicate callContextSomeCall = PrunedCallContextSensitivityStage5::ccSomeCall/0;
|
||||
|
||||
private predicate sourceCallCtx(CallContext cc) {
|
||||
if hasSourceCallCtx() then cc = callContextSomeCall() else cc = callContextNone()
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap,
|
||||
@@ -3802,6 +3796,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
or
|
||||
hiddenNode(this.getNodeEx().asParamReturnNode())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3925,6 +3921,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the parameter node through which data is returned, if any. */
|
||||
final ParameterNode asParameterReturnNode() { result = super.getNodeEx().asParamReturnNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
@@ -3941,10 +3940,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) }
|
||||
}
|
||||
|
||||
private import codeql.dataflow.test.ProvenancePathGraph as ProvenancePathGraph
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph implements PathGraphSig<PathNode> {
|
||||
module PathGraph implements PathGraphSig<PathNode>, ProvenancePathGraph::PathGraphSig<PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b, string key, string val) {
|
||||
a.(PathNodeImpl).getANonHiddenSuccessor(val) = b and
|
||||
@@ -4143,9 +4144,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC =
|
||||
getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
|
||||
midnode.getEnclosingCallable()) and
|
||||
localCC = PrunedCallContextSensitivityStage5::getLocalCc(cc) and
|
||||
t = mid.getType() and
|
||||
ap = mid.getAp() and
|
||||
summaryLabel = mid.getSummaryLabel()
|
||||
@@ -4191,7 +4190,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
or
|
||||
jumpStepEx(mid.getNodeExOutgoing(), node) and
|
||||
state = mid.getState() and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc instanceof SummaryCtxNone and
|
||||
t = mid.getType() and
|
||||
ap = mid.getAp() and
|
||||
@@ -4201,7 +4200,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
or
|
||||
additionalJumpStep(mid.getNodeExOutgoing(), node, label) and
|
||||
state = mid.getState() and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc instanceof SummaryCtxNone and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
t = node.getDataFlowType() and
|
||||
@@ -4210,14 +4209,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
summaryLabel = "-"
|
||||
or
|
||||
additionalJumpStateStep(mid.getNodeExOutgoing(), mid.getState(), node, state) and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc instanceof SummaryCtxNone and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
t = node.getDataFlowType() and
|
||||
ap = TAccessPathNil() and
|
||||
isStoreStep = false and
|
||||
summaryLabel = "-" and
|
||||
label = ""
|
||||
label = "Config"
|
||||
or
|
||||
exists(Content c, DataFlowType t0, AccessPath ap0 |
|
||||
pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and
|
||||
@@ -4280,7 +4279,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
Stage5::storeStepCand(mid.getNodeExOutgoing(), _, c, node, contentType, t) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext() and
|
||||
compatibleTypes(t0, contentType)
|
||||
compatibleTypesFilter(t0, contentType)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4300,22 +4299,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
private module PrunedCallContextSensitivityStage5 {
|
||||
private module CallContextSensitivityInput implements CallContextSensitivityInputSig {
|
||||
predicate relevantCallEdgeIn = Stage5::relevantCallEdgeIn/2;
|
||||
|
||||
predicate relevantCallEdgeOut = Stage5::relevantCallEdgeOut/2;
|
||||
|
||||
predicate reducedViableImplInCallContextCand =
|
||||
Stage5Param::Level1CallContextInput::reducedViableImplInCallContext/3;
|
||||
|
||||
predicate reducedViableImplInReturnCand =
|
||||
Stage5Param::Level1CallContextInput::reducedViableImplInReturn/2;
|
||||
}
|
||||
|
||||
import CallContextSensitivity<CallContextSensitivityInput>
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc,
|
||||
@@ -4325,11 +4308,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
pathOutOfCallable0(mid, pos, state, innercc, apa) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
PrunedCallContextSensitivityStage5::resolveReturn(innercc, c, call)
|
||||
|
|
||||
if PrunedCallContextSensitivityStage5::reducedViableImplInReturn(c, call)
|
||||
then cc = TReturn(c, call)
|
||||
else cc = TAnyCallContext()
|
||||
PrunedCallContextSensitivityStage5::resolveReturn(innercc, c, call) and
|
||||
cc = PrunedCallContextSensitivityStage5::getCallContextReturn(c, call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4415,11 +4395,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
|
|
||||
if PrunedCallContextSensitivityStage5::recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call)
|
||||
else innercc = TSomeCall()
|
||||
) and
|
||||
innercc = PrunedCallContextSensitivityStage5::getCallContextCall(call, callable)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4767,22 +4744,32 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
private predicate flagDisable() { none() }
|
||||
|
||||
module FlowExplorationFwd<explorationLimitSig/0 explorationLimit> {
|
||||
private import FlowExploration<explorationLimit/0, flagEnable/0, flagDisable/0>
|
||||
import Public
|
||||
private import FlowExploration<explorationLimit/0, flagEnable/0, flagDisable/0> as F
|
||||
import F::Public
|
||||
|
||||
predicate partialFlow = partialFlowFwd/3;
|
||||
predicate partialFlow = F::partialFlowFwd/3;
|
||||
}
|
||||
|
||||
module FlowExplorationRev<explorationLimitSig/0 explorationLimit> {
|
||||
private import FlowExploration<explorationLimit/0, flagDisable/0, flagEnable/0>
|
||||
import Public
|
||||
private import FlowExploration<explorationLimit/0, flagDisable/0, flagEnable/0> as F
|
||||
import F::Public
|
||||
|
||||
predicate partialFlow = partialFlowRev/3;
|
||||
predicate partialFlow = F::partialFlowRev/3;
|
||||
}
|
||||
|
||||
private module FlowExploration<
|
||||
explorationLimitSig/0 explorationLimit, flag/0 flagFwd, flag/0 flagRev>
|
||||
{
|
||||
class CallContext = CachedCallContextSensitivity::Cc;
|
||||
|
||||
class CallContextCall = CachedCallContextSensitivity::CcCall;
|
||||
|
||||
class CallContextNoCall = CachedCallContextSensitivity::CcNoCall;
|
||||
|
||||
predicate callContextNone = CachedCallContextSensitivity::ccNone/0;
|
||||
|
||||
predicate callContextSomeCall = CachedCallContextSensitivity::ccSomeCall/0;
|
||||
|
||||
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) {
|
||||
exists(NodeEx node1, NodeEx node2 |
|
||||
jumpStepEx(node1, node2)
|
||||
@@ -4953,7 +4940,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
) {
|
||||
flagFwd() and
|
||||
sourceNode(node, state) and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
sc3 = TSummaryCtx3None() and
|
||||
@@ -5167,7 +5154,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
predicate isSource() {
|
||||
sourceNode(node, state) and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
sc3 = TSummaryCtx3None() and
|
||||
@@ -5219,7 +5206,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap,
|
||||
boolean isStoreStep
|
||||
) {
|
||||
not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
|
||||
not isUnreachableInCall1(node,
|
||||
CachedCallContextSensitivity::LocalCallContext::getLocalCc(cc)) and
|
||||
(
|
||||
localFlowStepEx(mid.getNodeEx(), node, _) and
|
||||
state = mid.getState() and
|
||||
@@ -5256,7 +5244,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
or
|
||||
jumpStepEx(mid.getNodeEx(), node) and
|
||||
state = mid.getState() and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
sc3 = TSummaryCtx3None() and
|
||||
@@ -5267,7 +5255,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
or
|
||||
additionalJumpStep(mid.getNodeEx(), node, _) and
|
||||
state = mid.getState() and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
sc3 = TSummaryCtx3None() and
|
||||
@@ -5278,7 +5266,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
isStoreStep = false
|
||||
or
|
||||
additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and
|
||||
cc instanceof CallContextAny and
|
||||
cc = callContextNone() and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
sc3 = TSummaryCtx3None() and
|
||||
@@ -5341,7 +5329,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
storeExUnrestricted(midNode, c, node, contentType, t2) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(t1, contentType)
|
||||
compatibleTypesFilter(t1, contentType)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5393,11 +5381,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
CachedCallContextSensitivity::resolveReturn(innercc, c, call)
|
||||
|
|
||||
if CachedCallContextSensitivity::reducedViableImplInReturn(c, call)
|
||||
then cc = TReturn(c, call)
|
||||
else cc = TAnyCallContext()
|
||||
CachedCallContextSensitivity::resolveReturn(innercc, c, call) and
|
||||
cc = CachedCallContextSensitivity::getCallContextReturn(c, call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5448,11 +5433,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
sc1 = TSummaryCtx1Param(p) and
|
||||
sc2 = TSummaryCtx2Some(state) and
|
||||
sc3 = TSummaryCtx3Some(t) and
|
||||
sc4 = TSummaryCtx4Some(ap)
|
||||
|
|
||||
if CachedCallContextSensitivity::recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call)
|
||||
else innercc = TSomeCall()
|
||||
sc4 = TSummaryCtx4Some(ap) and
|
||||
innercc =
|
||||
CachedCallContextSensitivity::LocalCallContext::getCallContextCall(call, callable)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5620,7 +5603,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and
|
||||
pos = getReturnPosition(node.asNode()) and
|
||||
pos = node.(RetNodeEx).getReturnPosition() and
|
||||
isStoreStep = false
|
||||
)
|
||||
or
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
private import codeql.dataflow.DataFlow
|
||||
private import codeql.typetracking.TypeTracking as Tt
|
||||
private import codeql.util.Location
|
||||
private import codeql.util.Option
|
||||
private import codeql.util.Unit
|
||||
private import codeql.util.Option
|
||||
private import codeql.util.internal.MakeSets
|
||||
|
||||
module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
private import Lang
|
||||
@@ -137,7 +140,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate callStep(Node n1, LocalSourceNode n2) { viableParamArg(_, n2, n1) }
|
||||
|
||||
predicate returnStep(Node n1, LocalSourceNode n2) {
|
||||
viableReturnPosOut(_, getReturnPosition(n1), n2)
|
||||
viableReturnPosOut(_, [getValueReturnPosition(n1), getParamReturnPosition(n1, _)], n2)
|
||||
}
|
||||
|
||||
predicate hasFeatureBacktrackStoreTarget() { none() }
|
||||
@@ -222,9 +225,18 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
|
||||
result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
|
||||
pragma[nomagic]
|
||||
private predicate hasSimpleReturnKindIn(ReturnNode ret, ReturnKind kind, DataFlowCallable c) {
|
||||
c = getNodeEnclosingCallable(ret) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret) {
|
||||
exists(ReturnKind kind, DataFlowCallable c |
|
||||
hasSimpleReturnKindIn(ret, kind, c) and
|
||||
result = TReturnPositionSimple0(c, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -276,7 +288,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
|
||||
not expectsContent(node, _) and
|
||||
if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode
|
||||
then compatibleTypes(t, getNodeDataFlowType(node))
|
||||
then compatibleTypesFilter(t, getNodeDataFlowType(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
@@ -349,7 +361,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
// flow out of a callable
|
||||
exists(TReturnPositionSimple pos |
|
||||
revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
|
||||
getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
|
||||
pos = getReturnPositionSimple(node) and
|
||||
toReturn = true
|
||||
)
|
||||
}
|
||||
@@ -430,7 +442,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
private import Input
|
||||
|
||||
pragma[nomagic]
|
||||
DataFlowCallable viableImplInCallContextExtIn(DataFlowCall call, DataFlowCall ctx) {
|
||||
private DataFlowCallable viableImplInCallContextExtIn(DataFlowCall call, DataFlowCall ctx) {
|
||||
reducedViableImplInCallContextCand(call, _, ctx) and
|
||||
result = viableImplInCallContextExt(call, ctx) and
|
||||
relevantCallEdgeIn(call, result)
|
||||
@@ -455,15 +467,15 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n |
|
||||
exists(NodeRegion nr |
|
||||
relevantCallEdgeIn(call, callable) and
|
||||
getNodeEnclosingCallable(n) = callable and
|
||||
isUnreachableInCallCached(n, call)
|
||||
getNodeRegionEnclosingCallable(nr) = callable and
|
||||
isUnreachableInCallCached(nr, call)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
DataFlowCallable viableImplInCallContextExtOut(DataFlowCall call, DataFlowCall ctx) {
|
||||
private DataFlowCallable viableImplInCallContextExtOut(DataFlowCall call, DataFlowCall ctx) {
|
||||
exists(DataFlowCallable c |
|
||||
reducedViableImplInReturnCand(result, call) and
|
||||
result = viableImplInCallContextExt(call, ctx) and
|
||||
@@ -490,14 +502,170 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
private module CallSetsInput implements MkSetsInputSig {
|
||||
class Key = TCallEdge;
|
||||
|
||||
class Value = DataFlowCall;
|
||||
|
||||
DataFlowCall getAValue(TCallEdge ctxEdge) {
|
||||
exists(DataFlowCall ctx, DataFlowCallable c |
|
||||
ctxEdge = TMkCallEdge(ctx, c) and
|
||||
reducedViableImplInCallContext(result, c, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
int totalorder(DataFlowCall e) { result = callOrder(e) }
|
||||
}
|
||||
|
||||
private module CallSets = MakeSets<CallSetsInput>;
|
||||
|
||||
private module CallSetOption = Option<CallSets::ValueSet>;
|
||||
|
||||
/**
|
||||
* A set of call sites for which dispatch is affected by the call context.
|
||||
*
|
||||
* A `None` value indicates the empty set.
|
||||
*/
|
||||
private class CallSet = CallSetOption::Option;
|
||||
|
||||
private module DispatchSetsInput implements MkSetsInputSig {
|
||||
class Key = TCallEdge;
|
||||
|
||||
class Value = TCallEdge;
|
||||
|
||||
TCallEdge getAValue(TCallEdge ctxEdge) {
|
||||
exists(DataFlowCall ctx, DataFlowCallable c, DataFlowCall call, DataFlowCallable tgt |
|
||||
ctxEdge = mkCallEdge(ctx, c) and
|
||||
result = mkCallEdge(call, tgt) and
|
||||
viableImplInCallContextExtIn(call, ctx) = tgt and
|
||||
reducedViableImplInCallContext(call, c, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
int totalorder(TCallEdge e) { result = edgeOrder(e) }
|
||||
}
|
||||
|
||||
private module DispatchSets = MakeSets<DispatchSetsInput>;
|
||||
|
||||
private module DispatchSetsOption = Option<DispatchSets::ValueSet>;
|
||||
|
||||
/**
|
||||
* A set of call edges that are allowed in the call context. This applies to
|
||||
* all calls in the associated `CallSet`, in particular, this means that if
|
||||
* a call has no associated edges in the `DispatchSet`, then either all
|
||||
* edges are allowed or none are depending on whether the call is in the
|
||||
* `CallSet`.
|
||||
*
|
||||
* A `None` value indicates the empty set.
|
||||
*/
|
||||
private class DispatchSet = DispatchSetsOption::Option;
|
||||
|
||||
private predicate relevantCtx(TCallEdge ctx) {
|
||||
exists(CallSets::getValueSet(ctx)) or exists(getUnreachableSet(ctx))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasCtx(
|
||||
TCallEdge ctx, CallSet calls, DispatchSet tgts, UnreachableSetOption unreachable
|
||||
) {
|
||||
relevantCtx(ctx) and
|
||||
(
|
||||
CallSets::getValueSet(ctx) = calls.asSome()
|
||||
or
|
||||
not exists(CallSets::getValueSet(ctx)) and calls.isNone()
|
||||
) and
|
||||
(
|
||||
DispatchSets::getValueSet(ctx) = tgts.asSome()
|
||||
or
|
||||
not exists(DispatchSets::getValueSet(ctx)) and tgts.isNone()
|
||||
) and
|
||||
(
|
||||
getUnreachableSet(ctx) = unreachable.asSome()
|
||||
or
|
||||
not exists(getUnreachableSet(ctx)) and unreachable.isNone()
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(CallSet calls, DispatchSet tgts, UnreachableSetOption unreachable) {
|
||||
hasCtx(_, calls, tgts, unreachable)
|
||||
} or
|
||||
TSomeCall() or
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
|
||||
|
||||
/**
|
||||
* A call context to restrict the targets of virtual dispatch and prune local flow.
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(CallSet calls, DispatchSet tgts, UnreachableSetOption unreachable)` :
|
||||
* Flow entered through a specific call that improves the set of viable
|
||||
* dispatch targets for all of `calls` to the set of dispatch targets in
|
||||
* `tgts`, and/or the specific call prunes unreachable nodes in the
|
||||
* current callable as given by `unreachable`.
|
||||
* - `TSomeCall()` : Flow entered through a parameter. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
|
||||
* this dispatch target of `call` implies a reduced set of dispatch origins
|
||||
* to which data may flow if it should reach a `return` statement.
|
||||
*/
|
||||
abstract private class CallContext extends TCallContext {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
abstract private class CallContextCall extends CallContext { }
|
||||
|
||||
abstract private class CallContextNoCall extends CallContext { }
|
||||
|
||||
private class CallContextAny extends CallContextNoCall, TAnyCallContext {
|
||||
override string toString() { result = "CcAny" }
|
||||
}
|
||||
|
||||
private class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() { result = "CcCallSpecific" }
|
||||
}
|
||||
|
||||
private class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
}
|
||||
|
||||
private class CallContextReturn extends CallContextNoCall, TReturn {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
CallContextCall getSpecificCallContextCall(DataFlowCall call, DataFlowCallable c) {
|
||||
exists(CallSet calls, DispatchSet tgts, UnreachableSetOption unreachable |
|
||||
hasCtx(TMkCallEdge(call, c), calls, tgts, unreachable) and
|
||||
result = TSpecificCall(calls, tgts, unreachable)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callContextAffectsDispatch(DataFlowCall call, CallContext ctx) {
|
||||
exists(CallSet calls | ctx = TSpecificCall(calls, _, _) | calls.asSome().contains(call))
|
||||
}
|
||||
|
||||
CallContextNoCall getSpecificCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
result = TReturn(c, call)
|
||||
}
|
||||
|
||||
signature module PrunedViableImplInputSig {
|
||||
predicate reducedViableImplInCallContext(
|
||||
DataFlowCall call, DataFlowCallable c, DataFlowCall ctx
|
||||
);
|
||||
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call);
|
||||
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable c);
|
||||
|
||||
CallContextCall getSpecificCallContextCall(DataFlowCall call, DataFlowCallable c);
|
||||
|
||||
predicate callContextAffectsDispatch(DataFlowCall call, CallContext ctx);
|
||||
|
||||
CallContextNoCall getSpecificCallContextReturn(DataFlowCallable c, DataFlowCall call);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -505,31 +673,44 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
* of the input predicates in `CachedCallContextSensitivity`.
|
||||
*/
|
||||
module PrunedViableImpl<PrunedViableImplInputSig Input2> {
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
pragma[inline]
|
||||
predicate matchesCall(CcCall cc, DataFlowCall call) {
|
||||
cc = Input2::getSpecificCallContextCall(call, _) or
|
||||
cc = ccSomeCall()
|
||||
}
|
||||
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
predicate instanceofCc(Cc cc) { any() }
|
||||
|
||||
predicate instanceofCcCall(CcCall cc) { any() }
|
||||
|
||||
predicate instanceofCcNoCall(CcNoCall cc) { any() }
|
||||
|
||||
/**
|
||||
* 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)
|
||||
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CallContextCall ctx) {
|
||||
exists(DispatchSet tgts | ctx = TSpecificCall(_, tgts, _) |
|
||||
tgts.asSome().contains(TMkCallEdge(call, result))
|
||||
)
|
||||
}
|
||||
|
||||
/** 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
|
||||
predicate viableImplNotCallContextReduced(DataFlowCall call, CallContext ctx) {
|
||||
not Input2::callContextAffectsDispatch(call, ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,9 +719,9 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
*/
|
||||
bindingset[call, cc]
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
result = prunedViableImplInCallContext(call, cc)
|
||||
result = viableImplCallContextReduced(call, cc)
|
||||
or
|
||||
noPrunedViableImplInCallContext(call, cc) and
|
||||
viableImplNotCallContextReduced(call, cc) and
|
||||
relevantCallEdgeIn(call, result)
|
||||
}
|
||||
|
||||
@@ -550,17 +731,25 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
* the possible call sites are restricted.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
DataFlowCall prunedViableImplInCallContextReverse(
|
||||
DataFlowCallable callable, CallContextReturn ctx
|
||||
DataFlowCall viableImplCallContextReducedReverse(
|
||||
DataFlowCallable callable, CallContextNoCall ctx
|
||||
) {
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
callEnclosingCallable(call0, callable) and
|
||||
ctx = TReturn(c0, call0) and
|
||||
c0 = viableImplInCallContextExtOut(call0, result) and
|
||||
Input2::reducedViableImplInReturn(c0, call0)
|
||||
reducedViableImplInReturn(c0, call0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a return does not have a reduced set of viable call sites to
|
||||
* return to in call context `ctx`.
|
||||
*/
|
||||
predicate viableImplNotCallContextReducedReverse(CallContextNoCall ctx) {
|
||||
ctx instanceof CallContextAny
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a return from `callable` in `cc` to `call`.
|
||||
*/
|
||||
@@ -568,14 +757,22 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate resolveReturn(CallContextNoCall cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and relevantCallEdgeOut(call, callable)
|
||||
or
|
||||
call = prunedViableImplInCallContextReverse(callable, cc)
|
||||
call = viableImplCallContextReducedReverse(callable, cc)
|
||||
}
|
||||
|
||||
/** Gets the call context when returning from `c` to `call`. */
|
||||
bindingset[call, c]
|
||||
CallContextNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
result = Input2::getSpecificCallContextReturn(c, call)
|
||||
or
|
||||
not exists(Input2::getSpecificCallContextReturn(c, call)) and result = TAnyCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` improves virtual dispatch in `callable`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
|
||||
private predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
|
||||
Input2::reducedViableImplInCallContext(_, callable, call)
|
||||
}
|
||||
|
||||
@@ -587,25 +784,79 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
Input2::recordDataFlowCallSiteUnreachable(call, c) or
|
||||
recordDataFlowCallSiteDispatch(call, c)
|
||||
}
|
||||
|
||||
module NoLocalCallContext {
|
||||
class LocalCc = Unit;
|
||||
|
||||
bindingset[cc]
|
||||
LocalCc getLocalCc(CallContext cc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
CallContextCall getCallContextCall(DataFlowCall call, DataFlowCallable c) {
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = Input2::getSpecificCallContextCall(call, c)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
}
|
||||
|
||||
module LocalCallContext {
|
||||
class LocalCc = LocalCallContext;
|
||||
|
||||
private UnreachableSet getUnreachable(CallContext ctx) {
|
||||
exists(UnreachableSetOption unreachable | ctx = TSpecificCall(_, _, unreachable) |
|
||||
result = unreachable.asSome()
|
||||
)
|
||||
}
|
||||
|
||||
private LocalCallContext getLocalCallContext(CallContext ctx) {
|
||||
result = TSpecificLocalCall(getUnreachable(ctx))
|
||||
or
|
||||
not exists(getUnreachable(ctx)) and
|
||||
result instanceof LocalCallContextAny
|
||||
}
|
||||
|
||||
bindingset[cc]
|
||||
pragma[inline_late]
|
||||
LocalCc getLocalCc(CallContext cc) { result = getLocalCallContext(cc) }
|
||||
|
||||
bindingset[call, c]
|
||||
CallContextCall getCallContextCall(DataFlowCall call, DataFlowCallable c) {
|
||||
if recordDataFlowCallSite(call, c)
|
||||
then result = Input2::getSpecificCallContextCall(call, c)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private predicate reducedViableImplInCallContextAlias = reducedViableImplInCallContext/3;
|
||||
|
||||
private predicate reducedViableImplInReturnAlias = reducedViableImplInReturn/2;
|
||||
|
||||
private predicate recordDataFlowCallSiteUnreachableAlias = recordDataFlowCallSiteUnreachable/2;
|
||||
|
||||
private predicate getSpecificCallContextCallAlias = getSpecificCallContextCall/2;
|
||||
|
||||
private predicate callContextAffectsDispatchAlias = callContextAffectsDispatch/2;
|
||||
|
||||
private predicate getSpecificCallContextReturnAlias = getSpecificCallContextReturn/2;
|
||||
|
||||
private module DefaultPrunedViableImplInput implements PrunedViableImplInputSig {
|
||||
predicate reducedViableImplInCallContext = reducedViableImplInCallContextAlias/3;
|
||||
|
||||
predicate reducedViableImplInReturn = reducedViableImplInReturnAlias/2;
|
||||
|
||||
predicate recordDataFlowCallSiteUnreachable = recordDataFlowCallSiteUnreachableAlias/2;
|
||||
|
||||
predicate getSpecificCallContextCall = getSpecificCallContextCallAlias/2;
|
||||
|
||||
predicate callContextAffectsDispatch = callContextAffectsDispatchAlias/2;
|
||||
|
||||
predicate getSpecificCallContextReturn = getSpecificCallContextReturnAlias/2;
|
||||
}
|
||||
|
||||
import PrunedViableImpl<DefaultPrunedViableImplInput>
|
||||
}
|
||||
|
||||
module SndLevelScopeOption = Option<DataFlowSecondLevelScope>;
|
||||
|
||||
class SndLevelScopeOption = SndLevelScopeOption::Option;
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
@@ -617,7 +868,12 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
cached
|
||||
DataFlowSecondLevelScope getSecondLevelScopeCached(Node n) { result = getSecondLevelScope(n) }
|
||||
SndLevelScopeOption getSecondLevelScopeCached(Node n) {
|
||||
result = SndLevelScopeOption::some(getSecondLevelScope(n))
|
||||
or
|
||||
result instanceof SndLevelScopeOption::None and
|
||||
not exists(getSecondLevelScope(n))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
|
||||
@@ -630,6 +886,20 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
cached
|
||||
predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) }
|
||||
|
||||
cached
|
||||
predicate compatibleTypesCached(DataFlowType t1, DataFlowType t2) { compatibleTypes(t1, t2) }
|
||||
|
||||
private predicate relevantType(DataFlowType t) { t = getNodeType(_) }
|
||||
|
||||
cached
|
||||
predicate isTopType(DataFlowType t) {
|
||||
strictcount(DataFlowType t0 | relevantType(t0)) =
|
||||
strictcount(DataFlowType t0 | relevantType(t0) and compatibleTypesCached(t, t0))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate typeStrongerThanCached(DataFlowType t1, DataFlowType t2) { typeStrongerThan(t1, t2) }
|
||||
|
||||
cached
|
||||
predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) }
|
||||
|
||||
@@ -640,7 +910,9 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
|
||||
predicate isUnreachableInCallCached(NodeRegion nr, DataFlowCall call) {
|
||||
isUnreachableInCall(nr, call)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate outNodeExt(Node n) {
|
||||
@@ -663,13 +935,17 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate returnNodeExt(Node n, ReturnKindExt k) {
|
||||
k = TValueReturn(n.(ReturnNode).getKind())
|
||||
or
|
||||
exists(ParamNode p, ParameterPosition pos |
|
||||
predicate valueReturnNode(ReturnNode n, ReturnKindExt k) { k = TValueReturn(n.getKind()) }
|
||||
|
||||
cached
|
||||
predicate paramReturnNode(
|
||||
PostUpdateNode n, ParamNode p, SndLevelScopeOption scope, ReturnKindExt k
|
||||
) {
|
||||
exists(ParameterPosition pos |
|
||||
parameterValueFlowsToPreUpdate(p, n) and
|
||||
p.isParameterOf(_, pos) and
|
||||
k = TParamUpdate(pos)
|
||||
k = TParamUpdate(pos) and
|
||||
scope = getSecondLevelScopeCached(n)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -782,15 +1058,36 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
Impl1::reducedViableImplInReturn(c, call)
|
||||
}
|
||||
|
||||
cached
|
||||
CcCall getSpecificCallContextCall(DataFlowCall call, DataFlowCallable c) {
|
||||
result = Impl1::getSpecificCallContextCall(call, c)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate callContextAffectsDispatch(DataFlowCall call, Cc ctx) {
|
||||
Impl1::callContextAffectsDispatch(call, ctx)
|
||||
}
|
||||
|
||||
cached
|
||||
CcNoCall getSpecificCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
result = Impl1::getSpecificCallContextReturn(c, call)
|
||||
}
|
||||
|
||||
private module PrunedViableImplInput implements Impl1::PrunedViableImplInputSig {
|
||||
predicate reducedViableImplInCallContext =
|
||||
CachedCallContextSensitivity::reducedViableImplInCallContext/3;
|
||||
|
||||
predicate reducedViableImplInReturn =
|
||||
CachedCallContextSensitivity::reducedViableImplInReturn/2;
|
||||
|
||||
predicate recordDataFlowCallSiteUnreachable =
|
||||
CachedCallContextSensitivity::recordDataFlowCallSiteUnreachable/2;
|
||||
|
||||
predicate getSpecificCallContextCall =
|
||||
CachedCallContextSensitivity::getSpecificCallContextCall/2;
|
||||
|
||||
predicate callContextAffectsDispatch =
|
||||
CachedCallContextSensitivity::callContextAffectsDispatch/2;
|
||||
|
||||
predicate getSpecificCallContextReturn =
|
||||
CachedCallContextSensitivity::getSpecificCallContextReturn/2;
|
||||
}
|
||||
|
||||
private module Impl2 = Impl1::PrunedViableImpl<PrunedViableImplInput>;
|
||||
@@ -798,15 +1095,22 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
import Impl2
|
||||
|
||||
cached
|
||||
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, CallContextSpecificCall ctx) {
|
||||
result = Impl2::prunedViableImplInCallContext(call, ctx)
|
||||
predicate instanceofCc(Cc cc) { any() }
|
||||
|
||||
cached
|
||||
predicate instanceofCcCall(CcCall cc) { any() }
|
||||
|
||||
cached
|
||||
predicate instanceofCcNoCall(CcNoCall cc) { any() }
|
||||
|
||||
cached
|
||||
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) {
|
||||
result = Impl2::viableImplCallContextReduced(call, ctx)
|
||||
}
|
||||
|
||||
cached
|
||||
DataFlowCall prunedViableImplInCallContextReverse(
|
||||
DataFlowCallable callable, CallContextReturn ctx
|
||||
) {
|
||||
result = Impl2::prunedViableImplInCallContextReverse(callable, ctx)
|
||||
DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable callable, CcNoCall ctx) {
|
||||
result = Impl2::viableImplCallContextReducedReverse(callable, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,7 +1132,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
exists(ParameterPosition ppos |
|
||||
viableParam(call, ppos, p) and
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
|
||||
compatibleTypesFilter(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
|
||||
golangSpecificParamArgFilter(call, p, arg)
|
||||
)
|
||||
}
|
||||
@@ -981,10 +1285,10 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
then
|
||||
// normal flow through
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node))
|
||||
compatibleTypesFilter(getNodeDataFlowType(p), getNodeDataFlowType(node))
|
||||
or
|
||||
// getter
|
||||
compatibleTypes(read.getContentType(), getNodeDataFlowType(node))
|
||||
compatibleTypesFilter(read.getContentType(), getNodeDataFlowType(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
@@ -1017,7 +1321,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypes(getNodeDataFlowType(p), read.getContainerType())
|
||||
compatibleTypesFilter(getNodeDataFlowType(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read, model)
|
||||
@@ -1078,11 +1382,11 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
|
||||
// normal flow through
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out))
|
||||
compatibleTypesFilter(getNodeDataFlowType(arg), getNodeDataFlowType(out))
|
||||
or
|
||||
// getter
|
||||
compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getNodeDataFlowType(out))
|
||||
compatibleTypesFilter(getNodeDataFlowType(arg), read.getContainerType()) and
|
||||
compatibleTypesFilter(read.getContentType(), getNodeDataFlowType(out))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1208,29 +1512,74 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
ContentApprox getContentApproxCached(Content c) { result = getContentApprox(c) }
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(DataFlowCall call) {
|
||||
CachedCallContextSensitivity::recordDataFlowCallSite(call, _)
|
||||
} or
|
||||
TSomeCall() or
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
CachedCallContextSensitivity::reducedViableImplInReturn(c, call)
|
||||
newtype TCallEdge =
|
||||
TMkCallEdge(DataFlowCall call, DataFlowCallable tgt) { viableCallableExt(call) = tgt }
|
||||
|
||||
cached
|
||||
int edgeOrder(TCallEdge edge) {
|
||||
edge =
|
||||
rank[result](TCallEdge e, DataFlowCall call, DataFlowCallable tgt |
|
||||
e = TMkCallEdge(call, tgt)
|
||||
|
|
||||
e order by call.totalorder(), tgt.totalorder()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
int callOrder(DataFlowCall call) { result = call.totalorder() }
|
||||
|
||||
private module UnreachableSetsInput implements MkSetsInputSig {
|
||||
class Key = TCallEdge;
|
||||
|
||||
class Value = NodeRegion;
|
||||
|
||||
NodeRegion getAValue(TCallEdge edge) {
|
||||
exists(DataFlowCall call, DataFlowCallable tgt |
|
||||
edge = mkCallEdge(call, tgt) and
|
||||
getNodeRegionEnclosingCallable(result) = tgt and
|
||||
isUnreachableInCallCached(result, call)
|
||||
)
|
||||
}
|
||||
|
||||
int totalorder(NodeRegion nr) { result = nr.totalOrder() }
|
||||
}
|
||||
|
||||
private module UnreachableSets = MakeSets<UnreachableSetsInput>;
|
||||
|
||||
/** A set of nodes that is unreachable in some call context. */
|
||||
cached
|
||||
class UnreachableSet instanceof UnreachableSets::ValueSet {
|
||||
cached
|
||||
string toString() { result = "Unreachable" }
|
||||
|
||||
cached
|
||||
predicate contains(Node n) { exists(NodeRegion nr | super.contains(nr) and nr.contains(n)) }
|
||||
|
||||
cached
|
||||
DataFlowCallable getEnclosingCallable() {
|
||||
exists(NodeRegion nr | super.contains(nr) and result = getNodeRegionEnclosingCallable(nr))
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
UnreachableSet getUnreachableSet(TCallEdge edge) { result = UnreachableSets::getValueSet(edge) }
|
||||
|
||||
private module UnreachableSetOption = Option<UnreachableSet>;
|
||||
|
||||
class UnreachableSetOption = UnreachableSetOption::Option;
|
||||
|
||||
cached
|
||||
newtype TReturnPosition =
|
||||
TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) {
|
||||
exists(ReturnNodeExt ret |
|
||||
c = returnNodeGetEnclosingCallable(ret) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
hasValueReturnKindIn(_, kind, c)
|
||||
or
|
||||
hasParamReturnKindIn(_, _, kind, c)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TLocalFlowCallContext =
|
||||
TAnyLocalCall() or
|
||||
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) }
|
||||
TSpecificLocalCall(UnreachableSets::ValueSet ns)
|
||||
|
||||
cached
|
||||
newtype TReturnKindExt =
|
||||
@@ -1279,9 +1628,23 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
bindingset[call, tgt]
|
||||
pragma[inline_late]
|
||||
private TCallEdge mkCallEdge(DataFlowCall call, DataFlowCallable tgt) {
|
||||
result = TMkCallEdge(call, tgt)
|
||||
}
|
||||
|
||||
bindingset[t1, t2]
|
||||
pragma[inline_late]
|
||||
private predicate typeStrongerThan0(DataFlowType t1, DataFlowType t2) { typeStrongerThan(t1, t2) }
|
||||
predicate compatibleTypesFilter(DataFlowType t1, DataFlowType t2) {
|
||||
compatibleTypesCached(t1, t2)
|
||||
}
|
||||
|
||||
bindingset[t1, t2]
|
||||
pragma[inline_late]
|
||||
predicate typeStrongerThanFilter(DataFlowType t1, DataFlowType t2) {
|
||||
typeStrongerThanCached(t1, t2)
|
||||
}
|
||||
|
||||
private predicate callEdge(DataFlowCall call, DataFlowCallable c, ArgNode arg, ParamNode p) {
|
||||
viableParamArg(call, p, arg) and
|
||||
@@ -1362,7 +1725,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
nodeDataFlowType(arg, at) and
|
||||
nodeDataFlowType(p, pt) and
|
||||
relevantCallEdge(_, _, arg, p) and
|
||||
typeStrongerThan0(pt, at)
|
||||
typeStrongerThanFilter(pt, at)
|
||||
)
|
||||
or
|
||||
exists(ParamNode p, DataFlowType at, DataFlowType pt |
|
||||
@@ -1373,7 +1736,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
nodeDataFlowType(p, pt) and
|
||||
paramMustFlow(p, arg) and
|
||||
relevantCallEdge(_, _, arg, _) and
|
||||
typeStrongerThan0(at, pt)
|
||||
typeStrongerThanFilter(at, pt)
|
||||
)
|
||||
or
|
||||
exists(ParamNode p |
|
||||
@@ -1413,7 +1776,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
nodeDataFlowType(arg, at) and
|
||||
nodeDataFlowType(p, pt) and
|
||||
relevantCallEdge(_, _, arg, p) and
|
||||
typeStrongerThan0(at, pt)
|
||||
typeStrongerThanFilter(at, pt)
|
||||
)
|
||||
or
|
||||
exists(ArgNode arg |
|
||||
@@ -1482,8 +1845,13 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
* stronger then compatibility is checked and `t1` is returned.
|
||||
*/
|
||||
bindingset[t1, t2]
|
||||
pragma[inline_late]
|
||||
DataFlowType getStrongestType(DataFlowType t1, DataFlowType t2) {
|
||||
if typeStrongerThan(t2, t1) then result = t2 else (compatibleTypes(t1, t2) and result = t1)
|
||||
if typeStrongerThanCached(t2, t1)
|
||||
then result = t2
|
||||
else (
|
||||
compatibleTypesFilter(t1, t2) and result = t1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1604,7 +1972,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
exists(DataFlowType t1, DataFlowType t2 |
|
||||
typeFlowArgType(arg, t1, cc) and
|
||||
relevantArgParamIn(arg, p, t2) and
|
||||
compatibleTypes(t1, t2)
|
||||
compatibleTypesFilter(t1, t2)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1648,7 +2016,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
exists(DataFlowType t1, DataFlowType t2 |
|
||||
typeFlowParamType(p, t1, false) and
|
||||
relevantArgParamOut(arg, p, t2) and
|
||||
compatibleTypes(t1, t2)
|
||||
compatibleTypesFilter(t1, t2)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1703,76 +2071,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context to restrict the targets of virtual dispatch, prune local flow,
|
||||
* and match the call sites of flow into a method with flow out of a method.
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(DataFlowCall call)` : Flow entered through the
|
||||
* given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable
|
||||
* or helps prune unreachable nodes in the current callable.
|
||||
* - `TSomeCall()` : Flow entered through a parameter. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
|
||||
* this dispatch target of `call` implies a reduced set of dispatch origins
|
||||
* to which data may flow if it should reach a `return` statement.
|
||||
*/
|
||||
abstract class CallContext extends TCallContext {
|
||||
abstract string toString();
|
||||
|
||||
/** Holds if this call context is relevant for `callable`. */
|
||||
abstract predicate relevantFor(DataFlowCallable callable);
|
||||
}
|
||||
|
||||
abstract class CallContextNoCall extends CallContext { }
|
||||
|
||||
class CallContextAny extends CallContextNoCall, TAnyCallContext {
|
||||
override string toString() { result = "CcAny" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||
}
|
||||
|
||||
abstract class CallContextCall extends CallContext {
|
||||
/** Holds if this call context may be `call`. */
|
||||
bindingset[call]
|
||||
abstract predicate matchesCall(DataFlowCall call);
|
||||
}
|
||||
|
||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")")
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
CachedCallContextSensitivity::recordDataFlowCallSite(this.getCall(), callable)
|
||||
}
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
|
||||
|
||||
DataFlowCall getCall() { this = TSpecificCall(result) }
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContextNoCall, TReturn {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context that is relevant for pruning local flow.
|
||||
*/
|
||||
@@ -1790,30 +2088,22 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
}
|
||||
|
||||
class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
|
||||
LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
|
||||
LocalCallContextSpecificCall() { this = TSpecificLocalCall(ns) }
|
||||
|
||||
DataFlowCall call;
|
||||
UnreachableSet ns;
|
||||
|
||||
DataFlowCall getCall() { result = call }
|
||||
override string toString() { result = "LocalCcCall" }
|
||||
|
||||
override string toString() { result = "LocalCcCall(" + call + ")" }
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
ns.getEnclosingCallable() = callable
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
|
||||
/** Holds if this call context makes `n` unreachable. */
|
||||
predicate unreachable(Node n) { ns.contains(n) }
|
||||
}
|
||||
|
||||
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local call context given the call context and the callable that
|
||||
* the contexts apply to.
|
||||
*/
|
||||
LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
|
||||
ctx.relevantFor(callable) and
|
||||
if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
|
||||
then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall()
|
||||
else result instanceof LocalCallContextAny
|
||||
private DataFlowCallable getNodeRegionEnclosingCallable(NodeRegion nr) {
|
||||
exists(Node n | nr.contains(n) | getNodeEnclosingCallable(n) = result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1842,17 +2132,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> 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 extends NodeFinal {
|
||||
ReturnNodeExt() { returnNodeExt(this, _) }
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKindExt getKind() { returnNodeExt(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -1930,20 +2209,34 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) {
|
||||
result = getNodeEnclosingCallable(ret)
|
||||
pragma[nomagic]
|
||||
private predicate hasValueReturnKindIn(ReturnNode ret, ReturnKindExt kind, DataFlowCallable c) {
|
||||
c = getNodeEnclosingCallable(ret) and
|
||||
valueReturnNode(ret, kind)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) {
|
||||
result.getCallable() = returnNodeGetEnclosingCallable(ret) and
|
||||
kind = result.getKind()
|
||||
pragma[nomagic]
|
||||
private predicate hasParamReturnKindIn(
|
||||
PostUpdateNode n, ParamNode p, ReturnKindExt kind, DataFlowCallable c
|
||||
) {
|
||||
c = getNodeEnclosingCallable(n) and
|
||||
paramReturnNode(n, p, _, kind)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
ReturnPosition getReturnPosition(ReturnNodeExt ret) {
|
||||
result = getReturnPosition0(ret, ret.getKind())
|
||||
pragma[nomagic]
|
||||
ReturnPosition getValueReturnPosition(ReturnNode ret) {
|
||||
exists(ReturnKindExt kind, DataFlowCallable c |
|
||||
hasValueReturnKindIn(ret, kind, c) and
|
||||
result = TReturnPosition0(c, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
ReturnPosition getParamReturnPosition(PostUpdateNode n, ParamNode p) {
|
||||
exists(ReturnKindExt kind, DataFlowCallable c |
|
||||
hasParamReturnKindIn(n, p, kind, c) and
|
||||
result = TReturnPosition0(c, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/** An optional Boolean value. */
|
||||
|
||||
@@ -187,8 +187,9 @@ module MakeConsistency<
|
||||
}
|
||||
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
exists(DataFlowCallable c, NodeRegion nr |
|
||||
isUnreachableInCall(nr, call) and
|
||||
nr.contains(n) and
|
||||
c = nodeGetEnclosingCallable(n) and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
private import codeql.dataflow.DataFlow as DF
|
||||
private import codeql.util.Location
|
||||
private import DataFlowImpl
|
||||
private import AccessPathSyntax as AccessPathSyntax
|
||||
|
||||
/**
|
||||
@@ -253,6 +252,13 @@ module Make<
|
||||
* that has provenance `provenance`.
|
||||
*/
|
||||
predicate hasProvenance(Provenance provenance) { provenance = "manual" }
|
||||
|
||||
/**
|
||||
* Holds if there exists a model for which this callable is an exact
|
||||
* match, that is, no overriding was used to identify this callable from
|
||||
* the model.
|
||||
*/
|
||||
predicate hasExactModel() { none() }
|
||||
}
|
||||
|
||||
final private class NeutralCallableFinal = NeutralCallable;
|
||||
@@ -264,6 +270,20 @@ module Make<
|
||||
NeutralSummaryCallable() { this.getKind() = "summary" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable that has a neutral source model.
|
||||
*/
|
||||
class NeutralSourceCallable extends NeutralCallableFinal {
|
||||
NeutralSourceCallable() { this.getKind() = "source" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable that has a neutral sink model.
|
||||
*/
|
||||
class NeutralSinkCallable extends NeutralCallableFinal {
|
||||
NeutralSinkCallable() { this.getKind() = "sink" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable that has a neutral model.
|
||||
*/
|
||||
@@ -292,6 +312,13 @@ module Make<
|
||||
* Gets the kind of the neutral.
|
||||
*/
|
||||
abstract string getKind();
|
||||
|
||||
/**
|
||||
* Holds if there exists a model for which this callable is an exact
|
||||
* match, that is, no overriding was used to identify this callable from
|
||||
* the model.
|
||||
*/
|
||||
predicate hasExactModel() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1286,10 +1313,8 @@ module Make<
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStepValue(ArgNode arg, Node out, SummarizedCallable sc) {
|
||||
exists(ReturnKind rk, SummaryNode ret, DataFlowCall call |
|
||||
summaryLocalStep(summaryArgParam(call, arg, sc), ret, true, _) and
|
||||
summaryReturnNode(ret, pragma[only_bind_into](rk)) and
|
||||
out = getAnOutNode(call, pragma[only_bind_into](rk))
|
||||
exists(SummaryNode ret |
|
||||
summaryLocalStep(summaryArgParamRetOut(arg, ret, out, sc), ret, true, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1694,10 +1719,11 @@ module Make<
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ReturnNodeExt ret |
|
||||
exists(ReturnNode ret, ValueReturnKind kind |
|
||||
c = "ReturnValue" and
|
||||
ret = node.asNode() and
|
||||
ret.getKind().(ValueReturnKind).getKind() = getStandardReturnValueKind() and
|
||||
valueReturnNode(ret, kind) and
|
||||
kind.getKind() = getStandardReturnValueKind() and
|
||||
mid.asCallable() = getNodeEnclosingCallable(ret)
|
||||
)
|
||||
or
|
||||
@@ -1727,6 +1753,37 @@ module Make<
|
||||
)
|
||||
}
|
||||
|
||||
final private class SourceOrSinkElementFinal = SourceOrSinkElement;
|
||||
|
||||
bindingset[this]
|
||||
abstract private class SourceSinkModelCallableBase extends SourceOrSinkElementFinal {
|
||||
/**
|
||||
* Holds if there exists a manual model that applies to this.
|
||||
*/
|
||||
final predicate hasManualModel() { any(Provenance p | this.hasProvenance(p)).isManual() }
|
||||
|
||||
/**
|
||||
* Holds if this has provenance `p`.
|
||||
*/
|
||||
abstract predicate hasProvenance(Provenance p);
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable that has a source model.
|
||||
*/
|
||||
abstract class SourceModelCallable extends SourceSinkModelCallableBase {
|
||||
bindingset[this]
|
||||
SourceModelCallable() { exists(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable that has a sink model.
|
||||
*/
|
||||
abstract class SinkModelCallable extends SourceSinkModelCallableBase {
|
||||
bindingset[this]
|
||||
SinkModelCallable() { exists(this) }
|
||||
}
|
||||
|
||||
/** A source or sink relevant for testing. */
|
||||
signature class RelevantSourceOrSinkElementSig extends SourceOrSinkElement {
|
||||
/** Gets the string representation of this callable used by `source/1` or `sink/1`. */
|
||||
|
||||
85
shared/dataflow/codeql/dataflow/test/ProvenancePathGraph.qll
Normal file
85
shared/dataflow/codeql/dataflow/test/ProvenancePathGraph.qll
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Provides a module for renumbering MaD IDs in data flow path explanations in
|
||||
* order to produce more stable test output.
|
||||
*
|
||||
* In addition to the `PathGraph`, a `query predicate models` is provided to
|
||||
* list the contents of the referenced MaD rows.
|
||||
*/
|
||||
module;
|
||||
|
||||
signature predicate interpretModelForTestSig(QlBuiltins::ExtensionId madId, string model);
|
||||
|
||||
signature class PathNodeSig {
|
||||
string toString();
|
||||
}
|
||||
|
||||
signature module PathGraphSig<PathNodeSig PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
predicate edges(PathNode a, PathNode b, string key, string val);
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
predicate nodes(PathNode n, string key, string val);
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out);
|
||||
}
|
||||
|
||||
/** Transforms a `PathGraph` by printing the provenance information. */
|
||||
module ShowProvenance<
|
||||
interpretModelForTestSig/2 interpretModelForTest, PathNodeSig PathNode,
|
||||
PathGraphSig<PathNode> PathGraph>
|
||||
{
|
||||
private predicate madIds(string madId) {
|
||||
exists(string model |
|
||||
PathGraph::edges(_, _, _, model) and
|
||||
model.regexpFind("(?<=MaD:)[0-9]*", _, _) = madId
|
||||
)
|
||||
}
|
||||
|
||||
private predicate rankedMadIds(string madId, int r) {
|
||||
madId = rank[r](string madId0 | madIds(madId0) | madId0 order by madId0.toInt())
|
||||
}
|
||||
|
||||
query predicate models(int r, string model) {
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
rankedMadIds(madId.toString(), r) and interpretModelForTest(madId, model)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate translateModelsPart(string model1, string model2, int i) {
|
||||
PathGraph::edges(_, _, _, model1) and
|
||||
exists(string s | model1.splitAt("MaD:", i) = s |
|
||||
model2 = s and i = 0
|
||||
or
|
||||
exists(string part, string madId, string rest, int r |
|
||||
translateModelsPart(model1, part, i - 1) and
|
||||
madId = s.regexpCapture("([0-9]*)(.*)", 1) and
|
||||
rest = s.regexpCapture("([0-9]*)(.*)", 2) and
|
||||
rankedMadIds(madId, r) and
|
||||
model2 = part + "MaD:" + r + rest
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate translateModels(string model1, string model2) {
|
||||
exists(int i |
|
||||
translateModelsPart(model1, model2, i) and
|
||||
not translateModelsPart(model1, _, i + 1)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate edges(PathNode a, PathNode b, string key, string val) {
|
||||
exists(string model |
|
||||
PathGraph::edges(a, b, key, model) and
|
||||
translateModels(model, val)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nodes = PathGraph::nodes/3;
|
||||
|
||||
query predicate subpaths = PathGraph::subpaths/4;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/dataflow
|
||||
version: 0.2.7-dev
|
||||
version: 1.0.2-dev
|
||||
groups: shared
|
||||
library: true
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user