Compare commits

...

6 Commits

Author SHA1 Message Date
Tom Hvitved
9926e2a097 refactor2 2024-08-05 11:26:41 +02:00
Tom Hvitved
2a98ad4c55 refactor 2024-08-05 11:25:54 +02:00
Tom Hvitved
4afd06da46 Data flow: Experiment 2024-08-05 11:25:52 +02:00
Asger F
6539a35b2d JS: Update expected output with provenance 2024-08-05 10:44:12 +02:00
Asger F
a2cec8017f JS: Do not include type in path explanation 2024-08-02 14:17:56 +02:00
Asger F
8c88ad2ad0 JS: Update VariableCapture instantiation after merge 2024-08-02 13:19:37 +02:00
7 changed files with 860 additions and 57 deletions

View File

@@ -301,7 +301,7 @@ newtype TDataFlowType =
TAnyType() TAnyType()
class DataFlowType extends TDataFlowType { class DataFlowType extends TDataFlowType {
string toString() { string toDebugString() {
this instanceof TFunctionType and this instanceof TFunctionType and
result = result =
"TFunctionType(" + this.asFunction().toString() + ") at line " + "TFunctionType(" + this.asFunction().toString() + ") at line " +
@@ -310,6 +310,10 @@ class DataFlowType extends TDataFlowType {
this instanceof TAnyType and result = "TAnyType" this instanceof TAnyType and result = "TAnyType"
} }
string toString() {
result = "" // Must be the empty string to prevent this from showing up in path explanations
}
Function asFunction() { this = TFunctionType(result) } Function asFunction() { this = TFunctionType(result) }
} }

View File

@@ -126,6 +126,8 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
) )
} }
class ControlFlowNode = js::ControlFlowNode;
class BasicBlock extends js::BasicBlock { class BasicBlock extends js::BasicBlock {
Callable getEnclosingCallable() { result = this.getContainer().getFunctionBoundary() } Callable getEnclosingCallable() { result = this.getContainer().getFunctionBoundary() }
} }

View File

@@ -104,14 +104,14 @@ edges
| exception-xss.js:33:19:33:21 | foo | exception-xss.js:33:11:33:22 | ["bar", foo] | provenance | | | exception-xss.js:33:19:33:21 | foo | exception-xss.js:33:11:33:22 | ["bar", foo] | provenance | |
| exception-xss.js:34:11:34:11 | e | exception-xss.js:35:18:35:18 | e | provenance | | | exception-xss.js:34:11:34:11 | e | exception-xss.js:35:18:35:18 | e | provenance | |
| exception-xss.js:38:16:38:16 | x | exception-xss.js:39:9:39:9 | x | provenance | | | exception-xss.js:38:16:38:16 | x | exception-xss.js:39:9:39:9 | x | provenance | |
| exception-xss.js:39:9:39:9 | x | exception-xss.js:39:3:39:10 | exceptional return of deep2(x) | provenance | | | exception-xss.js:39:9:39:9 | x | exception-xss.js:39:3:39:10 | exceptional return of deep2(x) | provenance | Config |
| exception-xss.js:39:9:39:9 | x | exception-xss.js:41:17:41:17 | x | provenance | | | exception-xss.js:39:9:39:9 | x | exception-xss.js:41:17:41:17 | x | provenance | |
| exception-xss.js:41:17:41:17 | x | exception-xss.js:42:9:42:9 | x | provenance | | | exception-xss.js:41:17:41:17 | x | exception-xss.js:42:9:42:9 | x | provenance | |
| exception-xss.js:42:9:42:9 | x | exception-xss.js:4:17:4:17 | x | provenance | | | exception-xss.js:42:9:42:9 | x | exception-xss.js:4:17:4:17 | x | provenance | |
| exception-xss.js:42:9:42:9 | x | exception-xss.js:42:3:42:10 | exceptional return of inner(x) | provenance | Config | | exception-xss.js:42:9:42:9 | x | exception-xss.js:42:3:42:10 | exceptional return of inner(x) | provenance | Config |
| exception-xss.js:46:3:46:19 | exceptional return of deep("bar" + foo) | exception-xss.js:47:11:47:11 | e | provenance | | | exception-xss.js:46:3:46:19 | exceptional return of deep("bar" + foo) | exception-xss.js:47:11:47:11 | e | provenance | |
| exception-xss.js:46:8:46:18 | "bar" + foo | exception-xss.js:38:16:38:16 | x | provenance | | | exception-xss.js:46:8:46:18 | "bar" + foo | exception-xss.js:38:16:38:16 | x | provenance | |
| exception-xss.js:46:8:46:18 | "bar" + foo | exception-xss.js:46:3:46:19 | exceptional return of deep("bar" + foo) | provenance | | | exception-xss.js:46:8:46:18 | "bar" + foo | exception-xss.js:46:3:46:19 | exceptional return of deep("bar" + foo) | provenance | Config |
| exception-xss.js:46:16:46:18 | foo | exception-xss.js:46:8:46:18 | "bar" + foo | provenance | | | exception-xss.js:46:16:46:18 | foo | exception-xss.js:46:8:46:18 | "bar" + foo | provenance | |
| exception-xss.js:47:11:47:11 | e | exception-xss.js:48:18:48:18 | e | provenance | | | exception-xss.js:47:11:47:11 | e | exception-xss.js:48:18:48:18 | e | provenance | |
| exception-xss.js:74:28:74:28 | x | exception-xss.js:75:10:75:10 | x | provenance | | | exception-xss.js:74:28:74:28 | x | exception-xss.js:75:10:75:10 | x | provenance | |
@@ -119,7 +119,7 @@ edges
| exception-xss.js:75:10:75:10 | x | exception-xss.js:75:4:75:11 | exceptional return of inner(x) | provenance | Config | | exception-xss.js:75:10:75:10 | x | exception-xss.js:75:4:75:11 | exceptional return of inner(x) | provenance | Config |
| exception-xss.js:81:3:81:19 | exceptional return of myWeirdInner(foo) | exception-xss.js:82:11:82:11 | e | provenance | | | exception-xss.js:81:3:81:19 | exceptional return of myWeirdInner(foo) | exception-xss.js:82:11:82:11 | e | provenance | |
| exception-xss.js:81:16:81:18 | foo | exception-xss.js:74:28:74:28 | x | provenance | | | exception-xss.js:81:16:81:18 | foo | exception-xss.js:74:28:74:28 | x | provenance | |
| exception-xss.js:81:16:81:18 | foo | exception-xss.js:81:3:81:19 | exceptional return of myWeirdInner(foo) | provenance | | | exception-xss.js:81:16:81:18 | foo | exception-xss.js:81:3:81:19 | exceptional return of myWeirdInner(foo) | provenance | Config |
| exception-xss.js:82:11:82:11 | e | exception-xss.js:83:18:83:18 | e | provenance | | | exception-xss.js:82:11:82:11 | e | exception-xss.js:83:18:83:18 | e | provenance | |
| exception-xss.js:89:11:89:13 | foo | exception-xss.js:89:11:89:26 | foo.match(/foo/) | provenance | | | exception-xss.js:89:11:89:13 | foo | exception-xss.js:89:11:89:26 | foo.match(/foo/) | provenance | |
| exception-xss.js:89:11:89:26 | foo.match(/foo/) | exception-xss.js:90:11:90:11 | e | provenance | Config | | exception-xss.js:89:11:89:26 | foo.match(/foo/) | exception-xss.js:90:11:90:11 | e | provenance | Config |

View File

@@ -19,10 +19,10 @@ edges
| build-leaks.js:21:11:26:5 | stringifed [process.env] | build-leaks.js:30:22:30:31 | stringifed [process.env] | provenance | | | build-leaks.js:21:11:26:5 | stringifed [process.env] | build-leaks.js:30:22:30:31 | stringifed [process.env] | provenance | |
| build-leaks.js:21:24:26:5 | {\\n ... )\\n } [process.env] | build-leaks.js:21:11:26:5 | stringifed [process.env] | provenance | | | build-leaks.js:21:24:26:5 | {\\n ... )\\n } [process.env] | build-leaks.js:21:11:26:5 | stringifed [process.env] | provenance | |
| build-leaks.js:22:24:25:14 | Object. ... }, {}) | build-leaks.js:21:24:26:5 | {\\n ... )\\n } [process.env] | provenance | | | build-leaks.js:22:24:25:14 | Object. ... }, {}) | build-leaks.js:21:24:26:5 | {\\n ... )\\n } [process.env] | provenance | |
| build-leaks.js:22:36:22:38 | raw | build-leaks.js:22:24:25:14 | Object. ... }, {}) | provenance | | | build-leaks.js:22:36:22:38 | raw | build-leaks.js:22:24:25:14 | Object. ... }, {}) | provenance | Config |
| build-leaks.js:22:36:22:38 | raw | build-leaks.js:22:49:22:51 | env | provenance | Config | | build-leaks.js:22:36:22:38 | raw | build-leaks.js:22:49:22:51 | env | provenance | Config |
| build-leaks.js:22:36:22:38 | raw | build-leaks.js:23:39:23:41 | raw | provenance | | | build-leaks.js:22:36:22:38 | raw | build-leaks.js:23:39:23:41 | raw | provenance | |
| build-leaks.js:22:36:22:38 | raw | build-leaks.js:25:12:25:13 | [post update] {} | provenance | | | build-leaks.js:22:36:22:38 | raw | build-leaks.js:25:12:25:13 | [post update] {} | provenance | Config |
| build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env | provenance | | | build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env | provenance | |
| build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env | provenance | | | build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env | provenance | |
| build-leaks.js:23:13:23:15 | [post update] env | build-leaks.js:22:49:22:51 | env | provenance | | | build-leaks.js:23:13:23:15 | [post update] env | build-leaks.js:22:49:22:51 | env | provenance | |

View File

@@ -265,6 +265,7 @@ signature module InputSig<LocationSig Location> {
*/ */
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call); predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call);
/** Gets the access path limit. A maximum limit of 5 is allowed. */
default int accessPathLimit() { result = 5 } default int accessPathLimit() { result = 5 }
/** /**
@@ -401,7 +402,7 @@ module Configs<LocationSig Location, InputSig<Location> Lang> {
*/ */
default int fieldFlowBranchLimit() { result = 2 } default int fieldFlowBranchLimit() { result = 2 }
/** Gets the access path limit. */ /** Gets the access path limit. A maximum limit of 5 is allowed. */
default int accessPathLimit() { result = Lang::accessPathLimit() } default int accessPathLimit() { result = Lang::accessPathLimit() }
/** /**
@@ -523,7 +524,7 @@ module Configs<LocationSig Location, InputSig<Location> Lang> {
*/ */
default int fieldFlowBranchLimit() { result = 2 } default int fieldFlowBranchLimit() { result = 2 }
/** Gets the access path limit. */ /** Gets the access path limit. A maximum limit of 5 is allowed. */
default int accessPathLimit() { result = Lang::accessPathLimit() } default int accessPathLimit() { result = Lang::accessPathLimit() }
/** /**

View File

@@ -94,7 +94,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
*/ */
int fieldFlowBranchLimit(); int fieldFlowBranchLimit();
/** Gets the access path limit. */ /** Gets the access path limit. A maximum limit of 5 is allowed. */
int accessPathLimit(); int accessPathLimit();
/** /**
@@ -553,6 +553,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class Ap = Unit; class Ap = Unit;
Content getAHeadContent(Ap ap) { any() }
private class Cc = boolean; private class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
@@ -1019,6 +1021,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
callEdgeReturn(call, c, _, _, _, _, _) callEdgeReturn(call, c, _, _, _, _, _)
} }
predicate storeMayReachRead(NodeEx storeSource, Content c, NodeEx readTarget) { none() }
predicate providesStoreReadMatching() { none() }
additional predicate stats( additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges
) { ) {
@@ -1279,7 +1285,11 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
} }
private signature module StageSig { private signature module StageSig {
class Ap; class Ap {
string toString();
}
Content getAHeadContent(Ap ap);
predicate revFlow(NodeEx node); predicate revFlow(NodeEx node);
@@ -1314,6 +1324,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c); predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c);
predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c); predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c);
predicate storeMayReachRead(NodeEx storeSource, Content c, NodeEx readTarget);
default predicate providesStoreReadMatching() { any() }
} }
private module MkStage<StageSig PrevStage> { private module MkStage<StageSig PrevStage> {
@@ -1330,6 +1344,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
class ApNil extends Ap; class ApNil extends Ap;
Content getAHeadContent(Ap ap);
bindingset[result, ap] bindingset[result, ap]
ApApprox getApprox(Ap ap); ApApprox getApprox(Ap ap);
@@ -1402,11 +1418,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
bindingset[cc] bindingset[cc]
LocalCc getLocalCc(Cc cc); LocalCc getLocalCc(Cc cc);
predicate localValueStep(NodeEx node1, NodeEx node2, LocalCc lcc);
bindingset[node1, state1] bindingset[node1, state1]
bindingset[node2, state2] bindingset[node2, state2]
predicate localStep( predicate localTaintStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Typ t, LocalCc lcc
Typ t, LocalCc lcc
); );
bindingset[node, state, t0, ap] bindingset[node, state, t0, ap]
@@ -1469,6 +1486,52 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
compatibleContainer0(apc, containerType) compatibleContainer0(apc, containerType)
} }
private class NodeExAlias = NodeEx;
final private class ApFinal = Ap;
private module StoreReadMatchingInput implements StoreReadMatchingInputSig {
class NodeEx = NodeExAlias;
class Ap extends ApFinal {
Content getHead() { result = getAHeadContent(this) }
}
predicate nodeApRange(NodeEx node, Ap ap) { revFlowAp(node, ap) }
predicate localValueStep(NodeEx node1, NodeEx node2) {
exists(FlowState state, Ap ap, ApOption returnAp |
revFlow(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](returnAp),
pragma[only_bind_into](ap)) and
revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](returnAp),
pragma[only_bind_into](ap)) and
localValueStep(node1, node2, _)
)
}
predicate jumpValueStep(NodeEx node1, NodeEx node2) { jumpStepEx(node1, node2) }
predicate callEdgeArgParam(NodeEx arg, NodeEx param) {
callEdgeArgParam(_, _, arg, param, true, _)
}
predicate callEdgeReturn(NodeEx ret, NodeEx out) {
callEdgeReturn(_, _, ret, _, out, true, _)
}
predicate readContentStep(NodeEx node1, Content c, NodeEx node2) {
readStepCand0(node1, c, node2)
}
predicate storeContentStep(NodeEx node1, Content c, NodeEx node2) {
storeStepCand0(node1, _, c, node2, _, _)
}
int accessPathConfigLimit() { result = Config::accessPathLimit() }
}
import StoreReadMatching<StoreReadMatchingInput>
/** /**
* Holds if `node` is reachable with access path `ap` from a source. * Holds if `node` is reachable with access path `ap` from a source.
* *
@@ -1506,6 +1569,29 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
} }
bindingset[storeSource, c, readTarget]
pragma[inline_late]
private predicate storeMayReachReadInlineLate(
NodeEx storeSource, Content c, NodeEx readTarget
) {
PrevStage::storeMayReachRead(storeSource, c, readTarget)
}
bindingset[node1, state1]
bindingset[node2, state2]
private predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
Typ t, LocalCc lcc
) {
localValueStep(node1, node2, lcc) and
state1 = state2 and
preservesValue = true and
t = getNodeTyp(node1)
or
localTaintStep(node1, state1, node2, state2, t, lcc) and
preservesValue = false
}
pragma[nomagic] pragma[nomagic]
private predicate fwdFlow0( private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
@@ -1547,8 +1633,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
// read // read
exists(Typ t0, Ap ap0, Content c | exists(Typ t0, Ap ap0, Content c |
fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and
fwdFlowConsCand(t0, ap0, c, t, ap) and
apa = getApprox(ap) apa = getApprox(ap)
|
exists(NodeEx storeSource |
fwdFlowConsCandStoreReadMatchingEnabled(storeSource, t0, ap0, c, t, ap) and
storeMayReachReadInlineLate(storeSource, c, node)
)
or
fwdFlowConsCandStoreReadMatchingDisabled(t0, ap0, c, t, ap)
) )
or or
// flow into a callable // flow into a callable
@@ -1616,16 +1708,44 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
/** /**
* Holds if forward flow with access path `tail` and type `t1` reaches a * Holds if forward flow with access path `tail` and type `t1` reaches a
* store of `c` on a container of type `t2` resulting in access path * store of `c` on a container of type `t2` resulting in access path
* `cons`. * `cons`. `storeSource` is a relevant store source.
*
* This predicate is only evaluated when `PrevStage::providesStoreReadMatching()`
* holds.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { private predicate fwdFlowConsCandStoreReadMatchingEnabled(
NodeEx storeSource, Typ t2, Ap cons, Content c, Typ t1, Ap tail
) {
PrevStage::providesStoreReadMatching() and
fwdFlowStore(storeSource, t1, tail, c, t2, _, _, _, _, _, _) and
cons = apCons(c, t1, tail)
or
exists(Typ t0 |
typeStrengthen(t0, cons, t2) and
fwdFlowConsCandStoreReadMatchingEnabled(storeSource, t0, cons, c, t1, tail)
)
}
/**
* Holds if forward flow with access path `tail` and type `t1` reaches a
* store of `c` on a container of type `t2` resulting in access path
* `cons`.
*
* This predicate is only evaluated when `PrevStage::providesStoreReadMatching()`
* doesn't hold.
*/
pragma[nomagic]
private predicate fwdFlowConsCandStoreReadMatchingDisabled(
Typ t2, Ap cons, Content c, Typ t1, Ap tail
) {
not PrevStage::providesStoreReadMatching() and
fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and
cons = apCons(c, t1, tail) cons = apCons(c, t1, tail)
or or
exists(Typ t0 | exists(Typ t0 |
typeStrengthen(t0, cons, t2) and typeStrengthen(t0, cons, t2) and
fwdFlowConsCand(t0, cons, c, t1, tail) fwdFlowConsCandStoreReadMatchingDisabled(t0, cons, c, t1, tail)
) )
} }
@@ -1637,7 +1757,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
bindingset[node1, apc] bindingset[node1, apc]
pragma[inline_late] pragma[inline_late]
private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { private predicate readStepCandInlineLate(
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2
) {
readStepCand(node1, apc, c, node2) readStepCand(node1, apc, c, node2)
} }
@@ -1649,7 +1771,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
exists(ApHeadContent apc | exists(ApHeadContent apc |
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and
apc = getHeadContent(ap) and apc = getHeadContent(ap) and
readStepCand0(node1, apc, c, node2) readStepCandInlineLate(node1, apc, c, node2)
) )
} }
@@ -2073,9 +2195,13 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
pragma[nomagic] pragma[nomagic]
private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) {
exists(Typ t1 | exists(Typ t1 | fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) |
fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and exists(NodeEx storeSource |
fwdFlowConsCand(t1, ap1, c, _, ap2) fwdFlowConsCandStoreReadMatchingEnabled(storeSource, t1, ap1, c, _, ap2) and
storeMayReachReadInlineLate(storeSource, c, n2)
)
or
fwdFlowConsCandStoreReadMatchingDisabled(t1, ap1, c, _, ap2)
) )
} }
@@ -2190,8 +2316,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
or or
// store // store
exists(Ap ap0, Content c | exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp)
revFlowConsCand(ap0, c, ap) |
exists(NodeEx readTarget |
revFlowConsCand(readTarget, ap0, c, ap) and
storeMayReachReadInlineLate(node, c, readTarget)
)
or
revFlowConsCandStoreReadMatchingDisabled(ap0, c, ap)
) )
or or
// read // read
@@ -2257,14 +2389,24 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
* resulting in access path `cons`. * resulting in access path `cons`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { private predicate revFlowConsCand(NodeEx readTarget, Ap cons, Content c, Ap tail) {
exists(NodeEx mid, Ap tail0 | exists(Ap tail0 |
revFlow(mid, _, _, _, tail) and revFlow(readTarget, _, _, _, tail) and
tail = pragma[only_bind_into](tail0) and tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0) readStepFwd(_, cons, c, readTarget, tail0)
) )
} }
/**
* Holds if reverse flow with access path `tail` reaches a read of `c`
* resulting in access path `cons`.
*/
pragma[nomagic]
private predicate revFlowConsCandStoreReadMatchingDisabled(Ap cons, Content c, Ap tail) {
not PrevStage::providesStoreReadMatching() and
revFlowConsCand(_, cons, c, tail)
}
private module RevTypeFlowInput implements TypeFlowInput { private module RevTypeFlowInput implements TypeFlowInput {
predicate enableTypeFlow = Param::enableTypeFlow/0; predicate enableTypeFlow = Param::enableTypeFlow/0;
@@ -2382,18 +2524,19 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
} }
pragma[nomagic] pragma[nomagic]
predicate storeStepCand( private predicate storeStepCand0(
NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType,
DataFlowType containerType DataFlowType containerType
) { ) {
exists(Ap ap2 | exists(Ap ap2 |
PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and
revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and
revFlowConsCand(ap2, c, ap1) revFlowConsCand(_, ap2, c, ap1)
) )
} }
predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { pragma[nomagic]
private predicate readStepCand0(NodeEx node1, Content c, NodeEx node2) {
exists(Ap ap1, Ap ap2 | exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and
readStepFwd(node1, ap1, c, node2, ap2) and readStepFwd(node1, ap1, c, node2, ap2) and
@@ -2413,10 +2556,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) }
private predicate revConsCand(Content c, Typ t, Ap ap) { private predicate revConsCand(NodeEx readTarget, Content c, Typ t, Ap ap) {
exists(Ap ap2 | exists(Ap ap2 |
revFlowStore(ap2, c, ap, t, _, _, _, _, _) and revFlowStore(ap2, c, ap, t, _, _, _, _, _) and
revFlowConsCand(ap2, c, ap) revFlowConsCand(readTarget, ap2, c, ap)
) )
} }
@@ -2430,7 +2573,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
} }
additional predicate consCand(Content c, Typ t, Ap ap) { additional predicate consCand(Content c, Typ t, Ap ap) {
revConsCand(c, t, ap) and revConsCand(_, c, t, ap) and
validAp(ap) validAp(ap)
} }
@@ -2721,8 +2864,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
// read // read
exists(NodeEx mid, Typ t0, Ap ap0, Content c | exists(NodeEx mid, Typ t0, Ap ap0, Content c |
pn1 = TStagePathNodeMid(mid, state, cc, summaryCtx, argT, argAp, t0, ap0) and pn1 = TStagePathNodeMid(mid, state, cc, summaryCtx, argT, argAp, t0, ap0) and
fwdFlowRead(t0, ap0, c, mid, node, state, cc, summaryCtx, argT, argAp) and fwdFlowRead(t0, ap0, c, mid, node, state, cc, summaryCtx, argT, argAp)
fwdFlowConsCand(t0, ap0, c, t, ap) |
exists(NodeEx storeSource |
fwdFlowConsCandStoreReadMatchingEnabled(storeSource, t0, ap0, c, t, ap) and
storeMayReachReadInlineLate(storeSource, c, node)
)
or
fwdFlowConsCandStoreReadMatchingDisabled(t0, ap0, c, t, ap)
) )
or or
// flow into a callable // flow into a callable
@@ -2787,6 +2936,287 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
} }
} }
/**
* Provides logic for computing compatible and (likely) reachable store-read pairs.
*
* In order to determine whether a store can reach a compatible read, we first compute
* the set of contents that may be both stored and read.
*
* The implementation is based on `doublyBoundedFastTC`, and in order to avoid poor
* performance through recursion, we unroll the recursion manually 4 times, in order to
* be able to handle access paths of maximum length 5.
*/
private module StoreReadReachability {
private class ApCons extends ApFinal {
ApCons() { not this instanceof ApNil }
}
pragma[nomagic]
private predicate valueStep(NodeEx node1, NodeEx node2, ApCons ap) {
exists(FlowState state, ApOption returnAp1, ApOption returnAp2 |
revFlow(node1, pragma[only_bind_into](state), _, returnAp1, pragma[only_bind_into](ap)) and
revFlow(node2, pragma[only_bind_into](state), _, returnAp2, pragma[only_bind_into](ap))
|
localStep(node1, state, node2, state, true, _, _) and
returnAp1 = returnAp2
or
jumpStepEx(node1, node2)
or
callEdgeArgParam(_, _, node1, node2, _, ap)
or
callEdgeReturn(_, _, node1, _, node2, _, ap)
)
}
private signature module StoreReachesReadInputSig {
int iteration();
predicate storeMayReachReadDelta(NodeEx storeSource, Content c, NodeEx readTarget);
predicate storeMayReachReadPrev(NodeEx storeSource, Content c, NodeEx readTarget);
}
private signature class UsesPrevDeltaInfoSig {
string toString();
boolean toBoolean();
}
private module StoreReachesRead<
StoreReachesReadInputSig Prev, UsesPrevDeltaInfoSig UsesPrevDeltaInfo> implements
StoreReachesReadInputSig
{
int iteration() { result = Prev::iteration() + 1 }
private predicate enabled() { Config::accessPathLimit() > Prev::iteration() }
private newtype TNodeOrContent =
TNodeOrContentNode(
NodeEx n, FlowState state, ApCons ap, UsesPrevDeltaInfo usesPrevDelta
) {
enabled() and
revFlow(n, state, _, _, ap)
} or
TNodeOrContentContentStart(Content c) { enabled() and readStepCand0(_, c, _) } or
TNodeOrContentContentEnd(Content c) { enabled() and readStepCand0(_, c, _) }
private class NodeOrContent extends TNodeOrContent {
NodeEx asNodeEx(FlowState state, Ap ap, UsesPrevDeltaInfo usesPrevDelta) {
this = TNodeOrContentNode(result, state, ap, usesPrevDelta)
}
Content asContentStart() { this = TNodeOrContentContentStart(result) }
Content asContentEnd() { this = TNodeOrContentContentEnd(result) }
string toString() {
result = this.asContentStart().toString()
or
result = this.asContentEnd().toString()
or
result = this.asNodeEx(_, _, _).toString()
}
}
pragma[nomagic]
private predicate step(NodeOrContent node1, NodeOrContent node2) {
exists(
NodeEx n1, NodeEx n2, FlowState state, Ap ap, UsesPrevDeltaInfo usesPrevDelta1,
UsesPrevDeltaInfo usesPrevDelta2
|
n1 =
node1
.asNodeEx(pragma[only_bind_into](state), pragma[only_bind_into](ap),
usesPrevDelta1) and
n2 =
node2
.asNodeEx(pragma[only_bind_into](state), pragma[only_bind_into](ap),
usesPrevDelta2)
|
valueStep(n1, n2, ap) and
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta2)) =
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta1))
or
Prev::storeMayReachReadDelta(n1, _, n2) and usesPrevDelta2.toBoolean() = true
or
Prev::storeMayReachReadPrev(n1, _, n2) and
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta2)) =
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta1))
)
or
exists(NodeEx n2, Content c, ApCons ap2, UsesPrevDeltaInfo usesPrevDelta2 |
n2 = node2.asNodeEx(_, ap2, usesPrevDelta2) and
c = node1.asContentStart() and
projectToHeadContent(c) = getHeadContent(ap2) and
storeStepCand0(_, _, c, n2, _, _) and
usesPrevDelta2.toBoolean() = false
)
or
exists(NodeEx n1, Content c, ApCons ap1, UsesPrevDeltaInfo usesPrevDelta1 |
n1 = node1.asNodeEx(_, ap1, usesPrevDelta1) and
c = node2.asContentEnd() and
projectToHeadContent(c) = getHeadContent(ap1) and
readStepCand0(n1, c, _) and
usesPrevDelta1.toBoolean() = true
)
}
private predicate contentIsStored(NodeOrContent c) {
storeStepCand0(_, _, c.asContentStart(), _, _, _)
}
private predicate contentIsRead(NodeOrContent c) {
readStepCand0(_, c.asContentEnd(), _)
}
private predicate contentReachesReadTc(NodeOrContent node1, NodeOrContent node2) =
doublyBoundedFastTC(step/2, contentIsStored/1, contentIsRead/1)(node1, node2)
pragma[nomagic]
private predicate contentId(NodeOrContent c1, NodeOrContent c2, Content c) {
c1.asContentStart() = c and
c2.asContentEnd() = c
}
additional predicate contentIsReadAndStored(Content c) {
exists(NodeOrContent n1, NodeOrContent n2 |
contentReachesReadTc(n1, n2) and
contentId(n1, n2, c)
)
}
private predicate isStoreTarget(NodeOrContent node) {
exists(Content c, Ap ap, UsesPrevDeltaInfo usesPrevDelta |
contentIsReadAndStored(c) and
storeStepCand0(_, _, c, node.asNodeEx(_, ap, usesPrevDelta), _, _) and
projectToHeadContent(c) = getHeadContent(ap) and
usesPrevDelta.toBoolean() = false
)
}
private predicate isReadSource(NodeOrContent node) {
exists(Content c, Ap ap, UsesPrevDeltaInfo usesPrevDelta |
contentIsReadAndStored(c) and
readStepCand0(node.asNodeEx(_, ap, usesPrevDelta), c, _) and
projectToHeadContent(c) = getHeadContent(ap) and
usesPrevDelta.toBoolean() = true
)
}
private predicate storeMayReachReadTc(NodeOrContent node1, NodeOrContent node2) =
doublyBoundedFastTC(step/2, isStoreTarget/1, isReadSource/1)(node1, node2)
pragma[nomagic]
private predicate storeStepCandIsReadAndStored(
NodeEx node1, Content c, NodeOrContent node2
) {
exists(Ap ap, UsesPrevDeltaInfo usesPrevDelta |
contentIsReadAndStored(c) and
storeStepCand0(node1, _, c, node2.asNodeEx(_, ap, usesPrevDelta), _, _) and
projectToHeadContent(c) = getHeadContent(ap) and
usesPrevDelta.toBoolean() = false
)
}
pragma[nomagic]
private predicate readStepCandIsReadAndStored(
NodeOrContent node1, Content c, NodeEx node2
) {
exists(Ap ap, UsesPrevDeltaInfo usesPrevDelta |
contentIsReadAndStored(c) and
readStepCand0(node1.asNodeEx(_, ap, usesPrevDelta), c, node2) and
projectToHeadContent(c) = getHeadContent(ap) and
usesPrevDelta.toBoolean() = true
)
}
pragma[nomagic]
predicate storeMayReachReadPrev(NodeEx storeSource, Content c, NodeEx readTarget) {
Prev::storeMayReachReadPrev(storeSource, c, readTarget)
or
Prev::storeMayReachReadDelta(storeSource, c, readTarget)
}
pragma[nomagic]
predicate storeMayReachReadDelta(NodeEx storeSource, Content c, NodeEx readTarget) {
exists(NodeOrContent storeTarget, NodeOrContent readSource |
storeMayReachReadTc(storeTarget, readSource) and
storeStepCandIsReadAndStored(storeSource, c, storeTarget) and
readStepCandIsReadAndStored(readSource, c, readTarget)
) and
not Prev::storeMayReachReadPrev(storeSource, c, readTarget)
}
}
module Iteration0 implements StoreReachesReadInputSig {
int iteration() { result = 0 }
predicate storeMayReachReadDelta(NodeEx node1, Content c, NodeEx node2) { none() }
predicate storeMayReachReadPrev(NodeEx node1, Content c, NodeEx node2) { none() }
}
private class UsesPrevDeltaInfoUnit extends Unit {
boolean toBoolean() { result = [false, true] }
}
private module StoreReachesRead1 implements StoreReachesReadInputSig {
private module M = StoreReachesRead<Iteration0, UsesPrevDeltaInfoUnit>;
import M
predicate storeMayReachReadDelta(NodeEx storeSource, Content c, NodeEx readTarget) {
M::storeMayReachReadDelta(storeSource, c, readTarget)
or
// special case only needed for the first iteration: a store immediately followed by a read
exists(NodeEx storeTargetReadSource |
StoreReachesRead1::contentIsReadAndStored(c) and
storeStepCand0(storeSource, _, c, storeTargetReadSource, _, _) and
readStepCand0(storeTargetReadSource, c, readTarget)
)
}
}
private class UsesPrevDeltaInfoBoolean extends Boolean {
boolean toBoolean() { result = this }
}
private module StoreReachesRead2 =
StoreReachesRead<StoreReachesRead1, UsesPrevDeltaInfoBoolean>;
private module StoreReachesRead3 =
StoreReachesRead<StoreReachesRead2, UsesPrevDeltaInfoBoolean>;
private module StoreReachesRead4 =
StoreReachesRead<StoreReachesRead3, UsesPrevDeltaInfoBoolean>;
private module StoreReachesRead5 =
StoreReachesRead<StoreReachesRead4, UsesPrevDeltaInfoBoolean>;
predicate storeMayReachRead(NodeEx storeSource, Content c, NodeEx readTarget) {
StoreReachesRead5::storeMayReachReadDelta(storeSource, c, readTarget)
or
StoreReachesRead5::storeMayReachReadPrev(storeSource, c, readTarget)
}
}
predicate storeMayReachRead = StoreReadReachability::storeMayReachRead/3;
pragma[nomagic]
predicate storeStepCand(
NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType,
DataFlowType containerType
) {
storeStepCand0(node1, ap1, c, node2, contentType, containerType) and
storeMayReachRead(node1, c, _)
}
pragma[nomagic]
predicate readStepCand(NodeEx node1, Content c, NodeEx node2) {
readStepCand0(node1, c, node2) and
storeMayReachRead(_, c, node2)
}
additional predicate stats( additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges, boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges,
int tfnodes, int tftuples int tfnodes, int tftuples
@@ -2884,6 +3314,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
ApNil() { this = false } ApNil() { this = false }
} }
Content getAHeadContent(Ap ap) { ap = true and exists(result) }
bindingset[result, ap] bindingset[result, ap]
PrevStage::Ap getApprox(Ap ap) { any() } PrevStage::Ap getApprox(Ap ap) { any() }
@@ -2913,22 +3345,20 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
import CachedCallContextSensitivity import CachedCallContextSensitivity
import NoLocalCallContext import NoLocalCallContext
predicate localValueStep(NodeEx node1, NodeEx node2, LocalCc lcc) {
localFlowStepNodeCand1(node1, node2, _) and
exists(lcc)
}
bindingset[node1, state1] bindingset[node1, state1]
bindingset[node2, state2] bindingset[node2, state2]
predicate localStep( predicate localTaintStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Typ t, LocalCc lcc
Typ t, LocalCc lcc
) { ) {
( (
preservesValue = true and
localFlowStepNodeCand1(node1, node2, _) and
state1 = state2
or
preservesValue = false and
additionalLocalFlowStepNodeCand1(node1, node2, _) and additionalLocalFlowStepNodeCand1(node1, node2, _) and
state1 = state2 state1 = state2
or or
preservesValue = false and
additionalLocalStateStep(node1, state1, node2, state2) additionalLocalStateStep(node1, state1, node2, state2)
) and ) and
exists(t) and exists(t) and
@@ -3164,6 +3594,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
class Ap = ApproxAccessPathFront; class Ap = ApproxAccessPathFront;
Content getAHeadContent(Ap ap) { result = ap.getAHead() }
class ApNil = ApproxAccessPathFrontNil; class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
@@ -3201,11 +3633,15 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
import CallContextSensitivity<CallContextSensitivityInput> import CallContextSensitivity<CallContextSensitivityInput>
import NoLocalCallContext import NoLocalCallContext
predicate localStep( predicate localValueStep(NodeEx node1, NodeEx node2, LocalCc lcc) {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, localFlowBigStep(node1, _, node2, _, true, _, _, _) and
Typ t, LocalCc lcc exists(lcc)
}
predicate localTaintStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Typ t, LocalCc lcc
) { ) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, _) and localFlowBigStep(node1, state1, node2, state2, false, _, _, _) and
exists(t) and exists(t) and
exists(lcc) exists(lcc)
} }
@@ -3265,6 +3701,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
class ApNil = AccessPathFrontNil; class ApNil = AccessPathFrontNil;
Content getAHeadContent(Ap ap) { result = ap.getHead() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
Typ getTyp(DataFlowType t) { result = t } Typ getTyp(DataFlowType t) { result = t }
@@ -3288,11 +3726,20 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
import BooleanCallContext import BooleanCallContext
pragma[nomagic] pragma[nomagic]
predicate localStep( predicate localValueStep(NodeEx node1, NodeEx node2, LocalCc lcc) {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, exists(FlowState state |
Typ t, LocalCc lcc localFlowBigStep(node1, _, node2, _, true, _, _, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state), _) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and
exists(lcc)
)
}
pragma[nomagic]
predicate localTaintStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Typ t, LocalCc lcc
) { ) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _, _) and localFlowBigStep(node1, state1, node2, state2, false, t, _, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and
exists(lcc) exists(lcc)
@@ -3553,6 +4000,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
class Ap = AccessPathApprox; class Ap = AccessPathApprox;
Content getAHeadContent(Ap ap) { result = ap.getHead() }
class ApNil = AccessPathApproxNil; class ApNil = AccessPathApproxNil;
pragma[nomagic] pragma[nomagic]
@@ -3590,11 +4039,20 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
import CallContextSensitivity<CallContextSensitivityInput> import CallContextSensitivity<CallContextSensitivityInput>
import LocalCallContext import LocalCallContext
predicate localStep( pragma[nomagic]
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, predicate localValueStep(NodeEx node1, NodeEx node2, LocalCc lcc) {
Typ t, LocalCc lcc exists(FlowState state |
localFlowBigStep(node1, _, node2, _, true, _, lcc, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state), _) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _)
)
}
pragma[nomagic]
predicate localTaintStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Typ t, LocalCc lcc
) { ) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc, _) and localFlowBigStep(node1, state1, node2, state2, false, t, lcc, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) PrevStage::revFlow(node2, pragma[only_bind_into](state2), _)
} }

View File

@@ -2351,4 +2351,342 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString()))
} }
} }
signature module StoreReadMatchingInputSig {
class NodeEx {
string toString();
}
class Ap {
string toString();
Content getHead();
}
predicate nodeApRange(NodeEx node, Ap ap);
predicate localValueStep(NodeEx node1, NodeEx node2);
predicate jumpValueStep(NodeEx node1, NodeEx node2);
predicate callEdgeArgParam(NodeEx arg, NodeEx param);
predicate callEdgeReturn(NodeEx ret, NodeEx out);
predicate readContentStep(NodeEx node1, Content c, NodeEx node2);
predicate storeContentStep(NodeEx node1, Content c, NodeEx node2);
int accessPathConfigLimit();
}
/**
* Provides logic for computing compatible and (likely) matching store-read pairs.
*
* In order to determine whether a store can be matched with a compatible read, we
* check whether the target of a store may reach the source of a read, using simple
* data flow.
*
* The implementation is based on `doublyBoundedFastTC`, and in order to avoid poor
* performance through recursion, we unroll the recursion manually 4 times, in order to
* be able to handle access paths of maximum length 5.
*
* Additionally, in order to speed up the join with `doublyBoundedFastTC`, we first
* compute three pruning steps:
*
* 1. Which contents may have matching read-store pairs (called `contentIsReadAndStored` below).
* 2. Which store targets may have a matching read (called `storeMayReachARead` below).
* 3. Which reads may have a matching store (called `aStoreMayReachRead` below).
*/
module StoreReadMatching<StoreReadMatchingInputSig Input> {
private import Input
final private class ApFinal = Ap;
private class ApCons extends ApFinal {
ApCons() { exists(this.getHead()) }
}
pragma[nomagic]
private predicate valueStep(NodeEx node1, NodeEx node2) {
exists(ApCons ap |
nodeApRange(node1, pragma[only_bind_into](ap)) and
nodeApRange(node2, pragma[only_bind_into](ap))
|
localValueStep(node1, node2)
or
jumpValueStep(node1, node2)
or
callEdgeArgParam(node1, node2)
or
callEdgeReturn(node1, node2)
)
}
private signature module StoreReachesReadInputSig {
int iteration();
predicate storeMayReachReadDelta(NodeEx storeSource, Content c, NodeEx readTarget);
predicate storeMayReachReadPrev(NodeEx storeSource, Content c, NodeEx readTarget);
}
private signature class UsesPrevDeltaInfoSig {
string toString();
boolean toBoolean();
}
private module StoreReachesRead<
StoreReachesReadInputSig Prev, UsesPrevDeltaInfoSig UsesPrevDeltaInfo> implements
StoreReachesReadInputSig
{
int iteration() { result = Prev::iteration() + 1 }
private predicate enabled() { accessPathConfigLimit() > Prev::iteration() }
private newtype TNodeOrContent =
TNodeOrContentNode(NodeEx n, ApCons ap, UsesPrevDeltaInfo usesPrevDelta) {
enabled() and
nodeApRange(n, ap)
} or
TNodeOrContentContentStart(Content c) { enabled() and storeContentStep(_, c, _) } or
TNodeOrContentContentEnd(Content c) { enabled() and readContentStep(_, c, _) }
private class NodeOrContent extends TNodeOrContent {
NodeEx asNodeEx(Ap ap, UsesPrevDeltaInfo usesPrevDelta) {
this = TNodeOrContentNode(result, ap, usesPrevDelta)
}
Content asContentStart() { this = TNodeOrContentContentStart(result) }
Content asContentEnd() { this = TNodeOrContentContentEnd(result) }
string toString() {
result = this.asContentStart().toString()
or
result = this.asContentEnd().toString()
or
result = this.asNodeEx(_, _).toString()
}
}
bindingset[ap, result]
pragma[inline_late]
private Content getHeadInlineLate(Ap ap) { result = ap.getHead() }
pragma[nomagic]
private predicate step(NodeOrContent node1, NodeOrContent node2) {
exists(
NodeEx n1, NodeEx n2, Ap ap, UsesPrevDeltaInfo usesPrevDelta1,
UsesPrevDeltaInfo usesPrevDelta2
|
n1 = node1.asNodeEx(pragma[only_bind_into](ap), usesPrevDelta1) and
n2 = node2.asNodeEx(pragma[only_bind_into](ap), usesPrevDelta2)
|
valueStep(n1, n2) and
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta2)) =
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta1))
or
Prev::storeMayReachReadDelta(n1, _, n2) and usesPrevDelta2.toBoolean() = true
or
Prev::storeMayReachReadPrev(n1, _, n2) and
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta2)) =
pragma[only_bind_into](pragma[only_bind_out](usesPrevDelta1))
)
or
exists(NodeEx n2, Content c, ApCons ap2, UsesPrevDeltaInfo usesPrevDelta2 |
n2 = node2.asNodeEx(ap2, usesPrevDelta2) and
c = node1.asContentStart() and
c = ap2.getHead() and
storeContentStep(_, c, n2) and
usesPrevDelta2.toBoolean() = false
)
or
exists(NodeEx n1, Content c, ApCons ap1, UsesPrevDeltaInfo usesPrevDelta1 |
n1 = node1.asNodeEx(ap1, usesPrevDelta1) and
c = node2.asContentEnd() and
c = ap1.getHead() and
readContentStep(n1, c, _) and
usesPrevDelta1.toBoolean() = true
)
}
private predicate contentIsStored(NodeOrContent c) {
storeContentStep(_, c.asContentStart(), _)
}
private predicate contentIsRead(NodeOrContent c) { readContentStep(_, c.asContentEnd(), _) }
private predicate contentReachesReadTc(NodeOrContent node1, NodeOrContent node2) =
doublyBoundedFastTC(step/2, contentIsStored/1, contentIsRead/1)(node1, node2)
pragma[nomagic]
private predicate contentIsReadAndStoredJoin(NodeOrContent c1, NodeOrContent c2, Content c) {
c1.asContentStart() = c and
c2.asContentEnd() = c
}
additional predicate contentIsReadAndStored(Content c) {
exists(NodeOrContent n1, NodeOrContent n2 |
contentReachesReadTc(n1, n2) and
contentIsReadAndStoredJoin(n1, n2, c)
)
}
pragma[nomagic]
private predicate isStoreTarget0(NodeOrContent node, Content c) {
exists(Ap ap, UsesPrevDeltaInfo usesPrevDelta |
contentIsReadAndStored(c) and
storeContentStep(_, c, node.asNodeEx(ap, usesPrevDelta)) and
c = getHeadInlineLate(ap) and
usesPrevDelta.toBoolean() = false
)
}
private predicate isStoreTarget(NodeOrContent node) { isStoreTarget0(node, _) }
pragma[nomagic]
private predicate isReadSource0(NodeOrContent node, Content c) {
exists(Ap ap, UsesPrevDeltaInfo usesPrevDelta |
contentIsReadAndStored(c) and
readContentStep(node.asNodeEx(ap, usesPrevDelta), c, _) and
c = getHeadInlineLate(ap) and
usesPrevDelta.toBoolean() = true
)
}
private predicate isReadSource(NodeOrContent node) { isReadSource0(node, _) }
private predicate storeMayReachAReadTc(NodeOrContent node1, NodeOrContent node2) =
doublyBoundedFastTC(step/2, isStoreTarget/1, contentIsRead/1)(node1, node2)
pragma[nomagic]
private predicate storeMayReachAReadJoin(NodeOrContent n1, NodeOrContent n2, Content c) {
isStoreTarget0(n1, c) and
n2.asContentEnd() = c
}
private predicate storeMayReachARead(NodeOrContent node1, Content c) {
exists(NodeOrContent node2 |
storeMayReachAReadTc(node1, node2) and
storeMayReachAReadJoin(node1, node2, c)
)
}
private predicate aStoreMayReachReadTc(NodeOrContent node1, NodeOrContent node2) =
doublyBoundedFastTC(step/2, contentIsStored/1, isReadSource/1)(node1, node2)
pragma[nomagic]
private predicate aStoreMayReachReadJoin(NodeOrContent n1, NodeOrContent n2, Content c) {
n1.asContentStart() = c and
isReadSource0(n2, c)
}
additional predicate aStoreMayReachRead(NodeOrContent node2, Content c) {
exists(NodeOrContent node1 |
aStoreMayReachReadTc(node1, node2) and
aStoreMayReachReadJoin(node1, node2, c)
)
}
private predicate isStoreTargetPruned(NodeOrContent node) { storeMayReachARead(node, _) }
private predicate isReadSourcePruned(NodeOrContent node) { aStoreMayReachRead(node, _) }
private predicate storeMayReachReadTc(NodeOrContent node1, NodeOrContent node2) =
doublyBoundedFastTC(step/2, isStoreTargetPruned/1, isReadSourcePruned/1)(node1, node2)
pragma[nomagic]
private predicate storeMayReachReadDeltaJoinLeft(NodeEx node1, Content c, NodeOrContent node2) {
exists(Ap ap, UsesPrevDeltaInfo usesPrevDelta |
storeMayReachARead(node2, c) and
storeContentStep(node1, c, node2.asNodeEx(ap, usesPrevDelta)) and
c = getHeadInlineLate(ap) and
usesPrevDelta.toBoolean() = false
)
}
pragma[nomagic]
private predicate storeMayReachReadDeltaJoinRight(NodeOrContent node1, Content c, NodeEx node2) {
exists(Ap ap, UsesPrevDeltaInfo usesPrevDelta |
aStoreMayReachRead(node1, c) and
readContentStep(node1.asNodeEx(ap, usesPrevDelta), c, node2) and
c = getHeadInlineLate(ap) and
usesPrevDelta.toBoolean() = true
)
}
pragma[nomagic]
predicate storeMayReachReadDelta(NodeEx storeSource, Content c, NodeEx readTarget) {
exists(NodeOrContent storeTarget, NodeOrContent readSource |
storeMayReachReadTc(storeTarget, readSource) and
storeMayReachReadDeltaJoinLeft(storeSource, c, storeTarget) and
storeMayReachReadDeltaJoinRight(readSource, c, readTarget)
) and
not Prev::storeMayReachReadPrev(storeSource, c, readTarget)
}
pragma[nomagic]
predicate storeMayReachReadPrev(NodeEx storeSource, Content c, NodeEx readTarget) {
Prev::storeMayReachReadPrev(storeSource, c, readTarget)
or
Prev::storeMayReachReadDelta(storeSource, c, readTarget)
}
}
module Iteration0 implements StoreReachesReadInputSig {
int iteration() { result = 0 }
predicate storeMayReachReadDelta(NodeEx node1, Content c, NodeEx node2) { none() }
predicate storeMayReachReadPrev(NodeEx node1, Content c, NodeEx node2) { none() }
}
// in the first iteration there is no previous delta to use
private class UsesPrevDeltaInfoUnit extends Unit {
boolean toBoolean() { result = [false, true] }
}
private module StoreReachesRead1 implements StoreReachesReadInputSig {
private module M = StoreReachesRead<Iteration0, UsesPrevDeltaInfoUnit>;
import M
predicate storeMayReachReadDelta(NodeEx storeSource, Content c, NodeEx readTarget) {
M::storeMayReachReadDelta(storeSource, c, readTarget)
or
// special case only needed for the first iteration: a store immediately followed by a read
exists(NodeEx storeTargetReadSource |
StoreReachesRead1::contentIsReadAndStored(c) and
storeContentStep(storeSource, c, storeTargetReadSource) and
readContentStep(storeTargetReadSource, c, readTarget)
)
}
}
private import codeql.util.Boolean
private class UsesPrevDeltaInfoBoolean extends Boolean {
boolean toBoolean() { result = this }
}
private module StoreReachesRead2 =
StoreReachesRead<StoreReachesRead1, UsesPrevDeltaInfoBoolean>;
private module StoreReachesRead3 =
StoreReachesRead<StoreReachesRead2, UsesPrevDeltaInfoBoolean>;
private module StoreReachesRead4 =
StoreReachesRead<StoreReachesRead3, UsesPrevDeltaInfoBoolean>;
private module StoreReachesRead5 =
StoreReachesRead<StoreReachesRead4, UsesPrevDeltaInfoBoolean>;
predicate storeMayReachRead(NodeEx storeSource, Content c, NodeEx readTarget) {
StoreReachesRead5::storeMayReachReadDelta(storeSource, c, readTarget)
or
StoreReachesRead5::storeMayReachReadPrev(storeSource, c, readTarget)
}
}
} }