mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'master' into models5
This commit is contained in:
@@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering
|
||||
class NullInstruction extends ConstantValueInstruction {
|
||||
NullInstruction() {
|
||||
this.getValue() = "0" and
|
||||
this.getResultType().getUnspecifiedType() instanceof PointerType
|
||||
this.getResultIRType() instanceof IRAddressType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
bool =
|
||||
any(ConvertInstruction convert |
|
||||
checked = convert.getUnary() and
|
||||
convert.getResultType() instanceof BoolType and
|
||||
checked.getResultType() instanceof PointerType
|
||||
convert.getResultIRType() instanceof IRBooleanType and
|
||||
checked.getResultIRType() instanceof IRAddressType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
- qlpack: codeql-cpp
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/excluded-slow-queries.yml
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -198,123 +198,130 @@ private module Cached {
|
||||
/**
|
||||
* The final flow-through calculation:
|
||||
*
|
||||
* - Input access paths are abstracted with a `ContentOption` parameter
|
||||
* that represents the head of the access path. `TContentNone()` means that
|
||||
* the access path is unrestricted.
|
||||
* - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
|
||||
* or summarized as a single read step with before and after types recorded
|
||||
* in the `ReadStepTypesOption` parameter.
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
*/
|
||||
private module Final {
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to `node`
|
||||
* (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
parameterValueFlow0(p, node, contentIn) and
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
parameterValueFlow0(p, node, read) and
|
||||
if node instanceof CastingNode
|
||||
then
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
p = node and
|
||||
Cand::cand(p, _) and
|
||||
contentIn = TContentNone()
|
||||
read = TReadStepTypesNone()
|
||||
or
|
||||
// local flow
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, contentIn) and
|
||||
parameterValueFlow(p, mid, read) and
|
||||
LocalFlowBigStep::localFlowBigStep(mid, node)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
parameterValueFlow(p, mid, TContentNone()) and
|
||||
readStep(mid, f, node) and
|
||||
contentIn.getContent() = f and
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, TReadStepTypesNone()) and
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
|
||||
compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, TContentNone()) and
|
||||
argumentValueFlowsThrough(arg, contentIn, node)
|
||||
parameterValueFlowArg(p, arg, TReadStepTypesNone()) and
|
||||
argumentValueFlowsThrough(arg, read, node)
|
||||
)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, contentIn) and
|
||||
argumentValueFlowsThrough(arg, TContentNone(), node)
|
||||
parameterValueFlowArg(p, arg, read) and
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesNone(), node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArg(
|
||||
ParameterNode p, ArgumentNode arg, ContentOption contentIn
|
||||
ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
|
||||
) {
|
||||
parameterValueFlow(p, arg, contentIn) and
|
||||
parameterValueFlow(p, arg, read) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturn(param, kind, contentIn)
|
||||
parameterValueFlowReturn(param, kind, read)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `arg` that can flow to `out` (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, contentIn) and
|
||||
argumentValueFlowsThrough0(call, arg, kind, read) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and a single read step, not taking call
|
||||
* contexts into account, thus representing a getter-step.
|
||||
*/
|
||||
predicate getterStep(ArgumentNode arg, Content c, Node out) {
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps.
|
||||
* callable using only value-preserving steps and possibly a single read
|
||||
* step.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to the return
|
||||
* node (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
private predicate parameterValueFlowReturn(
|
||||
ParameterNode p, ReturnKind kind, ContentOption contentIn
|
||||
ParameterNode p, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlow(p, ret, contentIn) and
|
||||
parameterValueFlow(p, ret, read) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
}
|
||||
@@ -329,7 +336,27 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TContentNone())
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
readStep(_, c, _) and
|
||||
contentType = getErasedNodeTypeBound(node1) and
|
||||
containerType = getErasedNodeTypeBound(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readStep(n2, c, n1) and
|
||||
contentType = getErasedNodeTypeBound(n1) and
|
||||
containerType = getErasedNodeTypeBound(n2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,17 +367,8 @@ private module Cached {
|
||||
* been stored into, in order to handle cases like `x.f1.f2 = y`.
|
||||
*/
|
||||
cached
|
||||
predicate store(Node node1, Content f, Node node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TContentSome(f), n1)
|
||||
or
|
||||
readStep(n2, f, n1)
|
||||
)
|
||||
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
}
|
||||
|
||||
import FlowThrough
|
||||
@@ -397,10 +415,13 @@ private module Cached {
|
||||
TBooleanNone() or
|
||||
TBooleanSome(boolean b) { b = true or b = false }
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
@@ -415,25 +436,38 @@ class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNodeExt
|
||||
this instanceof OutNodeExt or
|
||||
// For reads, `x.f`, we want to check that the tracked type after the read (which
|
||||
// is obtained by popping the head of the access path stack) is compatible with
|
||||
// the type of `x.f`.
|
||||
readStep(_, _, this)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TContentOption =
|
||||
TContentNone() or
|
||||
TContentSome(Content f)
|
||||
private predicate readStepWithTypes(
|
||||
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
|
||||
) {
|
||||
readStep(n1, c, n2) and
|
||||
container = getErasedNodeTypeBound(n1) and
|
||||
content = getErasedNodeTypeBound(n2)
|
||||
}
|
||||
|
||||
private class ContentOption extends TContentOption {
|
||||
Content getContent() { this = TContentSome(result) }
|
||||
|
||||
predicate hasContent() { exists(this.getContent()) }
|
||||
|
||||
string toString() {
|
||||
result = this.getContent().toString()
|
||||
or
|
||||
not this.hasContent() and
|
||||
result = "<none>"
|
||||
private newtype TReadStepTypesOption =
|
||||
TReadStepTypesNone() or
|
||||
TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
|
||||
readStepWithTypes(_, container, c, _, content)
|
||||
}
|
||||
|
||||
private class ReadStepTypesOption extends TReadStepTypesOption {
|
||||
predicate isSome() { this instanceof TReadStepTypesSome }
|
||||
|
||||
DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
|
||||
|
||||
Content getContent() { this = TReadStepTypesSome(_, result, _) }
|
||||
|
||||
DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
|
||||
|
||||
string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,6 +726,23 @@ class BooleanOption extends TBooleanOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** Content tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContent() { this = MkTypedContent(c, t) }
|
||||
|
||||
/** Gets the content. */
|
||||
Content getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an access path. This is either a head or a nil.
|
||||
*/
|
||||
@@ -702,25 +753,29 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
}
|
||||
private DataFlowType t;
|
||||
|
||||
override DataFlowType getType() { this = TFrontNil(result) }
|
||||
AccessPathFrontNil() { this = TFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
|
||||
private TypedContent tc;
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
AccessPathFrontHead() { this = TFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -148,12 +148,6 @@ class Content extends TContent {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract Type getContainerType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
@@ -168,26 +162,14 @@ private class FieldContent extends Content, TFieldContent {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
|
||||
override Type getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -198,123 +198,130 @@ private module Cached {
|
||||
/**
|
||||
* The final flow-through calculation:
|
||||
*
|
||||
* - Input access paths are abstracted with a `ContentOption` parameter
|
||||
* that represents the head of the access path. `TContentNone()` means that
|
||||
* the access path is unrestricted.
|
||||
* - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
|
||||
* or summarized as a single read step with before and after types recorded
|
||||
* in the `ReadStepTypesOption` parameter.
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
*/
|
||||
private module Final {
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to `node`
|
||||
* (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
parameterValueFlow0(p, node, contentIn) and
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
parameterValueFlow0(p, node, read) and
|
||||
if node instanceof CastingNode
|
||||
then
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
p = node and
|
||||
Cand::cand(p, _) and
|
||||
contentIn = TContentNone()
|
||||
read = TReadStepTypesNone()
|
||||
or
|
||||
// local flow
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, contentIn) and
|
||||
parameterValueFlow(p, mid, read) and
|
||||
LocalFlowBigStep::localFlowBigStep(mid, node)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
parameterValueFlow(p, mid, TContentNone()) and
|
||||
readStep(mid, f, node) and
|
||||
contentIn.getContent() = f and
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, TReadStepTypesNone()) and
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
|
||||
compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, TContentNone()) and
|
||||
argumentValueFlowsThrough(arg, contentIn, node)
|
||||
parameterValueFlowArg(p, arg, TReadStepTypesNone()) and
|
||||
argumentValueFlowsThrough(arg, read, node)
|
||||
)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, contentIn) and
|
||||
argumentValueFlowsThrough(arg, TContentNone(), node)
|
||||
parameterValueFlowArg(p, arg, read) and
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesNone(), node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArg(
|
||||
ParameterNode p, ArgumentNode arg, ContentOption contentIn
|
||||
ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
|
||||
) {
|
||||
parameterValueFlow(p, arg, contentIn) and
|
||||
parameterValueFlow(p, arg, read) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturn(param, kind, contentIn)
|
||||
parameterValueFlowReturn(param, kind, read)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `arg` that can flow to `out` (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, contentIn) and
|
||||
argumentValueFlowsThrough0(call, arg, kind, read) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and a single read step, not taking call
|
||||
* contexts into account, thus representing a getter-step.
|
||||
*/
|
||||
predicate getterStep(ArgumentNode arg, Content c, Node out) {
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps.
|
||||
* callable using only value-preserving steps and possibly a single read
|
||||
* step.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to the return
|
||||
* node (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
private predicate parameterValueFlowReturn(
|
||||
ParameterNode p, ReturnKind kind, ContentOption contentIn
|
||||
ParameterNode p, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlow(p, ret, contentIn) and
|
||||
parameterValueFlow(p, ret, read) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
}
|
||||
@@ -329,7 +336,27 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TContentNone())
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
readStep(_, c, _) and
|
||||
contentType = getErasedNodeTypeBound(node1) and
|
||||
containerType = getErasedNodeTypeBound(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readStep(n2, c, n1) and
|
||||
contentType = getErasedNodeTypeBound(n1) and
|
||||
containerType = getErasedNodeTypeBound(n2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,17 +367,8 @@ private module Cached {
|
||||
* been stored into, in order to handle cases like `x.f1.f2 = y`.
|
||||
*/
|
||||
cached
|
||||
predicate store(Node node1, Content f, Node node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TContentSome(f), n1)
|
||||
or
|
||||
readStep(n2, f, n1)
|
||||
)
|
||||
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
}
|
||||
|
||||
import FlowThrough
|
||||
@@ -397,10 +415,13 @@ private module Cached {
|
||||
TBooleanNone() or
|
||||
TBooleanSome(boolean b) { b = true or b = false }
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
@@ -415,25 +436,38 @@ class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNodeExt
|
||||
this instanceof OutNodeExt or
|
||||
// For reads, `x.f`, we want to check that the tracked type after the read (which
|
||||
// is obtained by popping the head of the access path stack) is compatible with
|
||||
// the type of `x.f`.
|
||||
readStep(_, _, this)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TContentOption =
|
||||
TContentNone() or
|
||||
TContentSome(Content f)
|
||||
private predicate readStepWithTypes(
|
||||
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
|
||||
) {
|
||||
readStep(n1, c, n2) and
|
||||
container = getErasedNodeTypeBound(n1) and
|
||||
content = getErasedNodeTypeBound(n2)
|
||||
}
|
||||
|
||||
private class ContentOption extends TContentOption {
|
||||
Content getContent() { this = TContentSome(result) }
|
||||
|
||||
predicate hasContent() { exists(this.getContent()) }
|
||||
|
||||
string toString() {
|
||||
result = this.getContent().toString()
|
||||
or
|
||||
not this.hasContent() and
|
||||
result = "<none>"
|
||||
private newtype TReadStepTypesOption =
|
||||
TReadStepTypesNone() or
|
||||
TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
|
||||
readStepWithTypes(_, container, c, _, content)
|
||||
}
|
||||
|
||||
private class ReadStepTypesOption extends TReadStepTypesOption {
|
||||
predicate isSome() { this instanceof TReadStepTypesSome }
|
||||
|
||||
DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
|
||||
|
||||
Content getContent() { this = TReadStepTypesSome(_, result, _) }
|
||||
|
||||
DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
|
||||
|
||||
string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,6 +726,23 @@ class BooleanOption extends TBooleanOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** Content tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContent() { this = MkTypedContent(c, t) }
|
||||
|
||||
/** Gets the content. */
|
||||
Content getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an access path. This is either a head or a nil.
|
||||
*/
|
||||
@@ -702,25 +753,29 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
}
|
||||
private DataFlowType t;
|
||||
|
||||
override DataFlowType getType() { this = TFrontNil(result) }
|
||||
AccessPathFrontNil() { this = TFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
|
||||
private TypedContent tc;
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
AccessPathFrontHead() { this = TFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
@@ -128,8 +128,13 @@ private class SideEffectOutNode extends OutNode {
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result.getCall() = call and
|
||||
result.getReturnKind() = kind
|
||||
// There should be only one `OutNode` for a given `(call, kind)` pair. Showing the optimizer that
|
||||
// this is true helps it make better decisions downstream, especially in virtual dispatch.
|
||||
result =
|
||||
unique(OutNode outNode |
|
||||
outNode.getCall() = call and
|
||||
outNode.getReturnKind() = kind
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,12 +160,6 @@ class Content extends TContent {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract Type getContainerType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
@@ -175,26 +174,14 @@ private class FieldContent extends Content, TFieldContent {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
|
||||
override Type getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,6 +111,8 @@ private class IRSizedType extends IRType {
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +130,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* `IRFloatingPointType`.
|
||||
*/
|
||||
class IRNumericType extends IRSizedType {
|
||||
@@ -137,13 +139,27 @@ class IRNumericType extends IRSizedType {
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize, _, _)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`.
|
||||
*/
|
||||
class IRIntegerType extends IRNumericType {
|
||||
IRIntegerType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
|
||||
* integer, as well as character types whose representation is signed.
|
||||
*/
|
||||
class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType {
|
||||
final override string toString() { result = "int" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
@@ -158,7 +174,7 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
|
||||
* unsigned integer, as well as character types whose representation is unsigned.
|
||||
*/
|
||||
class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
|
||||
class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType {
|
||||
final override string toString() { result = "uint" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstructionImports
|
||||
private import SSAConstructionImports as Imports
|
||||
private import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -10,54 +16,47 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasPhiInstructionCached(
|
||||
OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
blockStartInstr = oldBlock.getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
cached
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
// This is just a type cast. Both classes derive from the same newtype.
|
||||
result = var
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
WrappedInstruction(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
|
||||
definitionHasPhiNode(defLocation, block)
|
||||
} or
|
||||
Chi(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
@@ -73,7 +72,7 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
@@ -81,7 +80,7 @@ private module Cached {
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
@@ -128,7 +127,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -172,13 +171,15 @@ private module Cached {
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
|
||||
instr = Phi(phiBlock, useLocation) and
|
||||
instr = getPhi(phiBlock, useLocation) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
@@ -191,7 +192,7 @@ private module Cached {
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = Chi(oldInstr) and
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
@@ -203,21 +204,11 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
@@ -228,20 +219,20 @@ private module Cached {
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = Chi(getOldInstruction(instruction)) and
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
|
||||
then result = Unreached(instruction.getEnclosingFunction())
|
||||
then result = unreachedInstruction(instruction.getEnclosingIRFunction())
|
||||
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
@@ -260,137 +251,73 @@ private module Cached {
|
||||
// `oldInstruction`, in which case the back edge should come out of the
|
||||
// chi node instead.
|
||||
if hasChiNode(_, oldInstruction)
|
||||
then instruction = Chi(oldInstruction)
|
||||
then instruction = getChi(oldInstruction)
|
||||
else instruction = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result = oldInstruction.getAST()
|
||||
Language::AST getInstructionAST(Instruction instr) {
|
||||
result = getOldInstruction(instr).getAST()
|
||||
or
|
||||
exists(RawIR::Instruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getAST()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result = block.getFirstInstruction().getAST()
|
||||
exists(RawIR::Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
result = primaryInstr.getAST()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result)
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
Language::LanguageType getInstructionResultType(Instruction instr) {
|
||||
result = instr.(RawIR::Instruction).getResultLanguageType()
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
instr = phiInstruction(_, defLocation) and
|
||||
result = defLocation.getType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instr) {
|
||||
result = getOldInstruction(instr).getOpcode()
|
||||
or
|
||||
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||
or
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
|
||||
result = getOldInstruction(instr).getEnclosingIRFunction()
|
||||
or
|
||||
exists(OldInstruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result =
|
||||
getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
derivedClass = oldInstr.getDerivedClass()
|
||||
)
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +328,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +336,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
|
||||
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
|
||||
@@ -588,7 +523,7 @@ module DefUse {
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = Chi(oldInstr) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
|
||||
@@ -607,7 +542,7 @@ module DefUse {
|
||||
or
|
||||
defOffset = -1 and
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = Phi(defBlock, defLocation) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
}
|
||||
|
||||
@@ -891,7 +826,7 @@ private module CachedForDebugging {
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = Phi(phiBlock, location) and
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
@@ -901,7 +836,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +896,19 @@ module SSAConsistency {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the SSA stages
|
||||
* of the IR. The raw stage of the IR does not expose these predicates.
|
||||
* These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
|
||||
* that all of SSA construction will be evaluated in the same stage.
|
||||
*/
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
|
||||
@@ -2,5 +2,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`.
|
||||
*/
|
||||
|
||||
private import IRFunctionBaseInternal
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* The IR for a function. This base class contains only the predicates that are the same between all
|
||||
* phases of the IR. Each instantiation of `IRFunction` extends this class.
|
||||
*/
|
||||
class IRFunctionBase extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunctionBase() { this = MkIRFunction(func) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/** Gets the function whose IR is represented. */
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/** Gets the location of the function. */
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
@@ -1,5 +1,5 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Construction
|
||||
private import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
private import TInstructionInternal
|
||||
private import IRFunctionBase
|
||||
private import TInstructionImports as Imports
|
||||
private import Imports::IRType
|
||||
private import Imports::Opcode
|
||||
|
||||
/**
|
||||
* An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual
|
||||
* branches of this type for instructions created directly from the AST (`TRawInstruction`) and for
|
||||
* instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`,
|
||||
* `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of
|
||||
* all of the branches that can appear in that particular stage. The public `Instruction` class for
|
||||
* each phase extends the `TStageInstruction` type for that stage.
|
||||
*/
|
||||
cached
|
||||
newtype TInstruction =
|
||||
TRawInstruction(
|
||||
IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
|
||||
) {
|
||||
IRConstruction::Raw::hasInstruction(tag1, tag2)
|
||||
} or
|
||||
TUnaliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or
|
||||
TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) {
|
||||
AliasedSSA::SSA::hasChiInstruction(primaryInstruction)
|
||||
} or
|
||||
TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
AliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* unaliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module UnaliasedSSAInstructions {
|
||||
class TPhiInstruction = TUnaliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TUnaliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TUnaliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TUnaliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* aliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module AliasedSSAInstructions {
|
||||
class TPhiInstruction = TAliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TAliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TAliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TAliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
@@ -0,0 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase
|
||||
private import semmle.code.cpp.ir.implementation.internal.TInstruction
|
||||
private import semmle.code.cpp.ir.implementation.internal.TIRVariable
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
@@ -12,34 +15,36 @@ private import TranslatedStmt
|
||||
private import TranslatedFunction
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = MkInstruction(result, _)
|
||||
instruction = TRawInstruction(result, _)
|
||||
}
|
||||
|
||||
InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) }
|
||||
|
||||
import Cached
|
||||
InstructionTag getInstructionTag(Instruction instruction) {
|
||||
instruction = TRawInstruction(_, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the initial
|
||||
* "raw" stage of the IR. The other stages of the IR do not expose these predicates.
|
||||
*/
|
||||
cached
|
||||
private module Cached {
|
||||
module Raw {
|
||||
class InstructionTag1 = TranslatedElement;
|
||||
|
||||
class InstructionTag2 = InstructionTag;
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) }
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
MkInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUserVariable(Function func, Variable var, CppType type) {
|
||||
getTranslatedFunction(func).hasUserVariable(var, type)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasThisVariable(Function func, CppType type) {
|
||||
type = getTypeForGLValue(getTranslatedFunction(func).getThisType())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
|
||||
exists(TranslatedElement element |
|
||||
@@ -64,232 +69,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult() and
|
||||
// Only associate `instruction` with this expression if the translated
|
||||
// expression actually produced the instruction; not if it merely
|
||||
// forwarded the result of another translated expression.
|
||||
instruction = translatedExpr.getInstruction(_)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionRegisterOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-phi instruction that defines an operand of `instr` but only if
|
||||
* both `instr` and the result have neighbor on the other side of the edge
|
||||
* between them. This is a necessary condition for being in a cycle, and it
|
||||
* removes about two thirds of the tuples that would otherwise be in this
|
||||
* predicate.
|
||||
*/
|
||||
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
|
||||
result = getNonPhiOperandDef(instr) and
|
||||
exists(getNonPhiOperandDef(result)) and
|
||||
instr = getNonPhiOperandDef(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
*
|
||||
* If such cycles are present, either due to a programming error in the IR
|
||||
* generation or due to a malformed database, it can cause infinite loops in
|
||||
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
|
||||
* better to remove these operands than to leave cycles in the operand graph.
|
||||
*/
|
||||
pragma[noopt]
|
||||
cached
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDefOfIntermediate+(instr) = instr
|
||||
}
|
||||
|
||||
cached
|
||||
CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
|
||||
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
|
||||
// the result type of the load.
|
||||
tag instanceof LoadOperandTag and
|
||||
result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
or
|
||||
not instruction instanceof LoadInstruction and
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionSuccessor(getInstructionTag(instruction), kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
|
||||
* `targetInstruction` is a back edge under the condition that
|
||||
* `requiredAncestor` is an ancestor of `sourceElement`.
|
||||
*/
|
||||
private predicate backEdgeCandidate(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
|
||||
Instruction targetInstruction, EdgeKind kind
|
||||
) {
|
||||
// While loop:
|
||||
// Any edge from within the body of the loop to the condition of the loop
|
||||
// is a back edge. This includes edges from `continue` and the fall-through
|
||||
// edge(s) after the last instruction(s) in the body.
|
||||
exists(TranslatedWhileStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
or
|
||||
// Do-while loop:
|
||||
// The back edge should be the edge(s) from the condition to the
|
||||
// body. This ensures that it's the back edge that will be pruned in a `do
|
||||
// { ... } while (0)` statement. Note that all `continue` statements in a
|
||||
// do-while loop produce forward edges.
|
||||
exists(TranslatedDoStmt s |
|
||||
targetInstruction = s.getBody().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getCondition()
|
||||
)
|
||||
or
|
||||
// For loop:
|
||||
// Any edge from within the body or update of the loop to the condition of
|
||||
// the loop is a back edge. When there is no loop update expression, this
|
||||
// includes edges from `continue` and the fall-through edge(s) after the
|
||||
// last instruction(s) in the body. A for loop may not have a condition, in
|
||||
// which case `getFirstConditionInstruction` returns the body instead.
|
||||
exists(TranslatedForStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
(
|
||||
requiredAncestor = s.getUpdate()
|
||||
or
|
||||
not exists(s.getUpdate()) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
)
|
||||
or
|
||||
// Range-based for loop:
|
||||
// Any edge from within the update of the loop to the condition of
|
||||
// the loop is a back edge.
|
||||
exists(TranslatedRangeBasedForStmt s |
|
||||
targetInstruction = s.getCondition().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getUpdate()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
|
||||
backEdgeCandidate(jumpSource, _, _, _, _) and
|
||||
ancestor = jumpSource
|
||||
or
|
||||
// For performance, we don't want a fastTC here
|
||||
jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
|
||||
|
|
||||
backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
|
||||
jumpSourceHasAncestor(sourceElement, requiredAncestor) and
|
||||
instruction = sourceElement.getInstruction(sourceTag)
|
||||
)
|
||||
or
|
||||
// Goto statement:
|
||||
// As a conservative approximation, any edge out of `goto` is a back edge
|
||||
// unless it goes strictly forward in the program text. A `goto` whose
|
||||
// source and target are both inside a macro will be seen as having the
|
||||
// same location for source and target, so we conservatively assume that
|
||||
// such a `goto` creates a back edge.
|
||||
exists(TranslatedElement s, GotoStmt goto |
|
||||
not isStrictlyForwardGoto(goto) and
|
||||
goto = s.getAST() and
|
||||
exists(InstructionTag tag |
|
||||
result = s.getInstructionSuccessor(tag, kind) and
|
||||
instruction = s.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `goto` jumps strictly forward in the program text. */
|
||||
private predicate isStrictlyForwardGoto(GotoStmt goto) {
|
||||
goto.getLocation().isBefore(goto.getTarget().getLocation())
|
||||
}
|
||||
|
||||
cached
|
||||
Locatable getInstructionAST(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getAST()
|
||||
}
|
||||
|
||||
cached
|
||||
CppType getInstructionResultType(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(_, getInstructionTag(instruction), result)
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _)
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
TIRVariable getInstructionVariable(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction) and
|
||||
@@ -302,10 +82,9 @@ private module Cached {
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionField(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionField(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -324,10 +103,9 @@ private module Cached {
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionIndex(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionIndex(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -350,20 +128,11 @@ private module Cached {
|
||||
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionOrigin(
|
||||
Instruction instruction, TranslatedElement element, InstructionTag tag
|
||||
) {
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionElementSize(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionElementSize(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -372,22 +141,225 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionResultSize(tag)
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult() and
|
||||
// Only associate `instruction` with this expression if the translated
|
||||
// expression actually produced the instruction; not if it merely
|
||||
// forwarded the result of another translated expression.
|
||||
instruction = translatedExpr.getInstruction(_)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getPrimaryInstructionForSideEffect(tag)
|
||||
)
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
class TStageInstruction = TRawInstruction;
|
||||
|
||||
predicate hasInstruction(TRawInstruction instr) { any() }
|
||||
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionRegisterOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-phi instruction that defines an operand of `instr` but only if
|
||||
* both `instr` and the result have neighbor on the other side of the edge
|
||||
* between them. This is a necessary condition for being in a cycle, and it
|
||||
* removes about two thirds of the tuples that would otherwise be in this
|
||||
* predicate.
|
||||
*/
|
||||
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
|
||||
result = getNonPhiOperandDef(instr) and
|
||||
exists(getNonPhiOperandDef(result)) and
|
||||
instr = getNonPhiOperandDef(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
*
|
||||
* If such cycles are present, either due to a programming error in the IR
|
||||
* generation or due to a malformed database, it can cause infinite loops in
|
||||
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
|
||||
* better to remove these operands than to leave cycles in the operand graph.
|
||||
*/
|
||||
pragma[noopt]
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDefOfIntermediate+(instr) = instr
|
||||
}
|
||||
|
||||
CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
|
||||
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
|
||||
// the result type of the load.
|
||||
tag instanceof LoadOperandTag and
|
||||
result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
or
|
||||
not instruction instanceof LoadInstruction and
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
|
||||
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionSuccessor(getInstructionTag(instruction), kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
|
||||
* `targetInstruction` is a back edge under the condition that
|
||||
* `requiredAncestor` is an ancestor of `sourceElement`.
|
||||
*/
|
||||
private predicate backEdgeCandidate(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
|
||||
Instruction targetInstruction, EdgeKind kind
|
||||
) {
|
||||
// While loop:
|
||||
// Any edge from within the body of the loop to the condition of the loop
|
||||
// is a back edge. This includes edges from `continue` and the fall-through
|
||||
// edge(s) after the last instruction(s) in the body.
|
||||
exists(TranslatedWhileStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
or
|
||||
// Do-while loop:
|
||||
// The back edge should be the edge(s) from the condition to the
|
||||
// body. This ensures that it's the back edge that will be pruned in a `do
|
||||
// { ... } while (0)` statement. Note that all `continue` statements in a
|
||||
// do-while loop produce forward edges.
|
||||
exists(TranslatedDoStmt s |
|
||||
targetInstruction = s.getBody().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getCondition()
|
||||
)
|
||||
or
|
||||
// For loop:
|
||||
// Any edge from within the body or update of the loop to the condition of
|
||||
// the loop is a back edge. When there is no loop update expression, this
|
||||
// includes edges from `continue` and the fall-through edge(s) after the
|
||||
// last instruction(s) in the body. A for loop may not have a condition, in
|
||||
// which case `getFirstConditionInstruction` returns the body instead.
|
||||
exists(TranslatedForStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
(
|
||||
requiredAncestor = s.getUpdate()
|
||||
or
|
||||
not exists(s.getUpdate()) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
)
|
||||
or
|
||||
// Range-based for loop:
|
||||
// Any edge from within the update of the loop to the condition of
|
||||
// the loop is a back edge.
|
||||
exists(TranslatedRangeBasedForStmt s |
|
||||
targetInstruction = s.getCondition().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getUpdate()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
|
||||
backEdgeCandidate(jumpSource, _, _, _, _) and
|
||||
ancestor = jumpSource
|
||||
or
|
||||
// For performance, we don't want a fastTC here
|
||||
jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
|
||||
}
|
||||
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
|
||||
|
|
||||
backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
|
||||
jumpSourceHasAncestor(sourceElement, requiredAncestor) and
|
||||
instruction = sourceElement.getInstruction(sourceTag)
|
||||
)
|
||||
or
|
||||
// Goto statement:
|
||||
// As a conservative approximation, any edge out of `goto` is a back edge
|
||||
// unless it goes strictly forward in the program text. A `goto` whose
|
||||
// source and target are both inside a macro will be seen as having the
|
||||
// same location for source and target, so we conservatively assume that
|
||||
// such a `goto` creates a back edge.
|
||||
exists(TranslatedElement s, GotoStmt goto |
|
||||
not isStrictlyForwardGoto(goto) and
|
||||
goto = s.getAST() and
|
||||
exists(InstructionTag tag |
|
||||
result = s.getInstructionSuccessor(tag, kind) and
|
||||
instruction = s.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `goto` jumps strictly forward in the program text. */
|
||||
private predicate isStrictlyForwardGoto(GotoStmt goto) {
|
||||
goto.getLocation().isBefore(goto.getTarget().getLocation())
|
||||
}
|
||||
|
||||
Locatable getInstructionAST(TStageInstruction instr) {
|
||||
result = getInstructionTranslatedElement(instr).getAST()
|
||||
}
|
||||
|
||||
CppType getInstructionResultType(TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
|
||||
}
|
||||
|
||||
Opcode getInstructionOpcode(TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _)
|
||||
}
|
||||
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
|
||||
}
|
||||
|
||||
Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getPrimaryInstructionForSideEffect(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
cached
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import IRConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import IRConstruction::Raw as Raw
|
||||
|
||||
@@ -705,12 +705,8 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
int getInstructionElementSize(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` has a result of type `UnknownType`,
|
||||
* gets the size of the result in bytes. If the result does not have a knonwn
|
||||
* constant size, this predicate does not hold.
|
||||
* Holds if the generated IR refers to an opaque type with size `byteSize`.
|
||||
*/
|
||||
int getInstructionResultSize(InstructionTag tag) { none() }
|
||||
|
||||
predicate needsUnknownOpaqueType(int byteSize) { none() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -415,17 +415,6 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
)
|
||||
}
|
||||
|
||||
override int getInstructionResultSize(InstructionTag tag) {
|
||||
exists(int elementCount |
|
||||
zeroInitRange(_, elementCount) and
|
||||
(
|
||||
tag = ZeroPadStringConstantTag() or
|
||||
tag = ZeroPadStringStoreTag()
|
||||
) and
|
||||
result = elementCount * getElementType().getSize()
|
||||
)
|
||||
}
|
||||
|
||||
private Type getElementType() {
|
||||
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
|
||||
}
|
||||
@@ -772,15 +761,6 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
result = getZeroValue(getElementType())
|
||||
}
|
||||
|
||||
override int getInstructionResultSize(InstructionTag tag) {
|
||||
elementCount > 1 and
|
||||
(
|
||||
tag = getElementDefaultValueTag() or
|
||||
tag = getElementDefaultValueStoreTag()
|
||||
) and
|
||||
result = elementCount * getElementType().getSize()
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag)
|
||||
or
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstructionImports
|
||||
private import SSAConstructionImports as Imports
|
||||
private import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -10,54 +16,47 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasPhiInstructionCached(
|
||||
OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
blockStartInstr = oldBlock.getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
cached
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
// This is just a type cast. Both classes derive from the same newtype.
|
||||
result = var
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
WrappedInstruction(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
|
||||
definitionHasPhiNode(defLocation, block)
|
||||
} or
|
||||
Chi(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
@@ -73,7 +72,7 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
@@ -81,7 +80,7 @@ private module Cached {
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
@@ -128,7 +127,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -172,13 +171,15 @@ private module Cached {
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
|
||||
instr = Phi(phiBlock, useLocation) and
|
||||
instr = getPhi(phiBlock, useLocation) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
@@ -191,7 +192,7 @@ private module Cached {
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = Chi(oldInstr) and
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
@@ -203,21 +204,11 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
@@ -228,20 +219,20 @@ private module Cached {
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = Chi(getOldInstruction(instruction)) and
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
|
||||
then result = Unreached(instruction.getEnclosingFunction())
|
||||
then result = unreachedInstruction(instruction.getEnclosingIRFunction())
|
||||
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
@@ -260,137 +251,73 @@ private module Cached {
|
||||
// `oldInstruction`, in which case the back edge should come out of the
|
||||
// chi node instead.
|
||||
if hasChiNode(_, oldInstruction)
|
||||
then instruction = Chi(oldInstruction)
|
||||
then instruction = getChi(oldInstruction)
|
||||
else instruction = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result = oldInstruction.getAST()
|
||||
Language::AST getInstructionAST(Instruction instr) {
|
||||
result = getOldInstruction(instr).getAST()
|
||||
or
|
||||
exists(RawIR::Instruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getAST()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result = block.getFirstInstruction().getAST()
|
||||
exists(RawIR::Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
result = primaryInstr.getAST()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result)
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
Language::LanguageType getInstructionResultType(Instruction instr) {
|
||||
result = instr.(RawIR::Instruction).getResultLanguageType()
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
instr = phiInstruction(_, defLocation) and
|
||||
result = defLocation.getType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instr) {
|
||||
result = getOldInstruction(instr).getOpcode()
|
||||
or
|
||||
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||
or
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
|
||||
result = getOldInstruction(instr).getEnclosingIRFunction()
|
||||
or
|
||||
exists(OldInstruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result =
|
||||
getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
derivedClass = oldInstr.getDerivedClass()
|
||||
)
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +328,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +336,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
|
||||
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
|
||||
@@ -588,7 +523,7 @@ module DefUse {
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = Chi(oldInstr) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
|
||||
@@ -607,7 +542,7 @@ module DefUse {
|
||||
or
|
||||
defOffset = -1 and
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = Phi(defBlock, defLocation) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
}
|
||||
|
||||
@@ -891,7 +826,7 @@ private module CachedForDebugging {
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = Phi(phiBlock, location) and
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
@@ -901,7 +836,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +896,19 @@ module SSAConsistency {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the SSA stages
|
||||
* of the IR. The raw stage of the IR does not expose these predicates.
|
||||
* These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
|
||||
* that all of SSA construction will be evaluated in the same stage.
|
||||
*/
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
|
||||
@@ -2,5 +2,7 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.Print
|
||||
private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
private int getPointerSize() { result = max(any(NullPointerType t).getSize()) }
|
||||
|
||||
@@ -143,7 +143,7 @@ private predicate isOpaqueType(Type type) {
|
||||
predicate hasOpaqueType(Type tag, int byteSize) {
|
||||
isOpaqueType(tag) and byteSize = getTypeSize(tag)
|
||||
or
|
||||
tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize)
|
||||
tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +191,7 @@ private newtype TCppType =
|
||||
TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or
|
||||
TFunctionGLValueType() or
|
||||
TGLValueAddressType(Type type) or
|
||||
TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or
|
||||
TUnknownOpaqueType(int byteSize) { Raw::needsUnknownOpaqueType(byteSize) } or
|
||||
TUnknownType()
|
||||
|
||||
/**
|
||||
|
||||
159
cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp
Normal file
159
cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
typedef size_t streamsize;
|
||||
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||
class basic_string {
|
||||
public:
|
||||
explicit basic_string(const Allocator& a = Allocator());
|
||||
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||
|
||||
const charT* c_str() const;
|
||||
};
|
||||
|
||||
typedef basic_string<char> string;
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
basic_istream<charT,traits>& operator>>(int& n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
|
||||
|
||||
basic_ostream<charT, traits>& operator<<(int n);
|
||||
};
|
||||
|
||||
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||
|
||||
template<class charT, class traits = char_traits<charT>>
|
||||
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||
public:
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||
public:
|
||||
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||
|
||||
basic_string<charT, traits, Allocator> str() const;
|
||||
};
|
||||
|
||||
using stringstream = basic_stringstream<char>;
|
||||
}
|
||||
|
||||
char *source() { return getenv("USERDATA"); }
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const std::stringstream &s) {};
|
||||
|
||||
void test_string()
|
||||
{
|
||||
char *a = source();
|
||||
std::string b("123");
|
||||
std::string c(source());
|
||||
|
||||
sink(a); // tainted
|
||||
sink(b);
|
||||
sink(c); // tainted [NOT DETECTED]
|
||||
sink(b.c_str());
|
||||
sink(c.c_str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_stringstream()
|
||||
{
|
||||
std::stringstream ss1, ss2, ss3, ss4, ss5;
|
||||
std::string t(source());
|
||||
|
||||
ss1 << "1234";
|
||||
ss2 << source();
|
||||
ss3 << "123" << source();
|
||||
ss4 << source() << "456";
|
||||
ss5 << t;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss3); // tainted [NOT DETECTED]
|
||||
sink(ss4); // tainted [NOT DETECTED]
|
||||
sink(ss5); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
sink(ss3.str()); // tainted [NOT DETECTED]
|
||||
sink(ss4.str()); // tainted [NOT DETECTED]
|
||||
sink(ss5.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_stringstream_int(int source)
|
||||
{
|
||||
std::stringstream ss1, ss2;
|
||||
|
||||
ss1 << 1234;
|
||||
ss2 << source;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *user_input() {
|
||||
return source();
|
||||
}
|
||||
|
||||
void sink(const char *filename, const char *mode);
|
||||
|
||||
void test_strings2()
|
||||
{
|
||||
string path1 = user_input();
|
||||
sink(path1.c_str(), "r"); // tainted [NOT DETECTED]
|
||||
|
||||
string path2;
|
||||
path2 = user_input();
|
||||
sink(path2.c_str(), "r"); // tainted
|
||||
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted [NOT DETECTED]
|
||||
sink(ss); // tainted [NOT DETECTED]
|
||||
}
|
||||
@@ -153,6 +153,50 @@
|
||||
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:15:13:20 | call to getenv |
|
||||
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 |
|
||||
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | shared.h:5:23:5:31 | sinkparam |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:21:29:21:29 | s |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:114:43:118 | p#1 |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:25:62:30 | call to getenv |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:12:68:17 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:21 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:23 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | a |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:21 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:23 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:14 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:16 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:23 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:25 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:14 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:118:10:118:15 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:26 | call to user_input |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:28 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:19 | call to user_input |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string<char, char_traits<char>, allocator<char>>)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:24 | call to user_input |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:26 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:14:138:15 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:24 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:26 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:18 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:143:7:143:8 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:14:149:15 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:24 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:26 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:18 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string |
|
||||
| test_diff.cpp:92:10:92:13 | argv | shared.h:5:23:5:31 | sinkparam |
|
||||
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |
|
||||
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | (const char *)... |
|
||||
|
||||
@@ -31,6 +31,20 @@
|
||||
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... | IR only |
|
||||
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
|
||||
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:7:62:12 | source | AST only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:117:7:117:16 | user_input | AST only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string<char, char_traits<char>, allocator<char>>)... | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:157:7:157:8 | cs | AST only |
|
||||
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
|
||||
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only |
|
||||
| test_diff.cpp:111:10:111:13 | argv | shared.h:5:23:5:31 | sinkparam | AST only |
|
||||
|
||||
@@ -312,31 +312,42 @@
|
||||
| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT |
|
||||
| stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | |
|
||||
| stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT |
|
||||
| stl.cpp:138:18:138:24 | hello | stl.cpp:138:18:138:25 | call to basic_string | TAINT |
|
||||
| stl.cpp:138:18:138:25 | call to basic_string | stl.cpp:143:8:143:9 | s1 | |
|
||||
| stl.cpp:139:19:139:26 | call to basic_string | stl.cpp:144:8:144:9 | s2 | |
|
||||
| stl.cpp:139:20:139:26 | hello | stl.cpp:139:19:139:26 | call to basic_string | TAINT |
|
||||
| stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:141:3:141:14 | ... = ... | |
|
||||
| stl.cpp:141:8:141:14 | call to basic_string | stl.cpp:145:8:145:9 | s3 | |
|
||||
| stl.cpp:141:8:141:14 | hello | stl.cpp:141:8:141:14 | call to basic_string | TAINT |
|
||||
| stl.cpp:149:18:149:23 | call to source | stl.cpp:149:18:149:26 | call to basic_string | TAINT |
|
||||
| stl.cpp:149:18:149:26 | call to basic_string | stl.cpp:154:8:154:9 | s1 | |
|
||||
| stl.cpp:150:19:150:27 | call to basic_string | stl.cpp:155:8:155:9 | s2 | |
|
||||
| stl.cpp:150:20:150:25 | call to source | stl.cpp:150:19:150:27 | call to basic_string | TAINT |
|
||||
| stl.cpp:152:8:152:13 | call to source | stl.cpp:152:8:152:15 | call to basic_string | TAINT |
|
||||
| stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:152:3:152:15 | ... = ... | |
|
||||
| stl.cpp:152:8:152:15 | call to basic_string | stl.cpp:156:8:156:9 | s3 | |
|
||||
| stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:161:20:161:21 | s1 | |
|
||||
| stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:163:8:163:9 | s1 | |
|
||||
| stl.cpp:160:15:160:16 | call to basic_string | stl.cpp:165:8:165:9 | s1 | |
|
||||
| stl.cpp:161:20:161:21 | s1 | stl.cpp:166:8:166:9 | s2 | |
|
||||
| stl.cpp:163:8:163:9 | s1 | stl.cpp:163:3:163:9 | ... = ... | |
|
||||
| stl.cpp:163:8:163:9 | s1 | stl.cpp:167:8:167:9 | s3 | |
|
||||
| stl.cpp:171:19:171:40 | call to basic_string | stl.cpp:175:8:175:9 | s1 | |
|
||||
| stl.cpp:171:32:171:37 | call to source | stl.cpp:171:19:171:40 | call to basic_string | TAINT |
|
||||
| stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:173:3:173:28 | ... = ... | |
|
||||
| stl.cpp:173:8:173:28 | call to basic_string | stl.cpp:176:8:176:9 | s2 | |
|
||||
| stl.cpp:173:20:173:25 | call to source | stl.cpp:173:8:173:28 | call to basic_string | TAINT |
|
||||
| stl.cpp:137:19:137:24 | call to source | stl.cpp:140:17:140:18 | cs | |
|
||||
| stl.cpp:137:19:137:24 | call to source | stl.cpp:142:7:142:8 | cs | |
|
||||
| stl.cpp:140:17:140:18 | cs | stl.cpp:140:17:140:19 | call to basic_string | TAINT |
|
||||
| stl.cpp:140:17:140:19 | call to basic_string | stl.cpp:143:7:143:8 | ss | |
|
||||
| stl.cpp:148:19:148:24 | call to source | stl.cpp:151:17:151:18 | cs | |
|
||||
| stl.cpp:151:17:151:18 | cs | stl.cpp:151:17:151:19 | call to basic_string | TAINT |
|
||||
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:154:7:154:8 | ss | |
|
||||
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:157:7:157:8 | ss | |
|
||||
| stl.cpp:154:7:154:8 | ss | stl.cpp:154:10:154:14 | call to c_str | TAINT |
|
||||
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:154:2:154:16 | ... = ... | |
|
||||
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:156:7:156:8 | cs | |
|
||||
| stl.cpp:163:18:163:24 | hello | stl.cpp:163:18:163:25 | call to basic_string | TAINT |
|
||||
| stl.cpp:163:18:163:25 | call to basic_string | stl.cpp:168:8:168:9 | s1 | |
|
||||
| stl.cpp:164:19:164:26 | call to basic_string | stl.cpp:169:8:169:9 | s2 | |
|
||||
| stl.cpp:164:20:164:26 | hello | stl.cpp:164:19:164:26 | call to basic_string | TAINT |
|
||||
| stl.cpp:166:8:166:14 | call to basic_string | stl.cpp:166:3:166:14 | ... = ... | |
|
||||
| stl.cpp:166:8:166:14 | call to basic_string | stl.cpp:170:8:170:9 | s3 | |
|
||||
| stl.cpp:166:8:166:14 | hello | stl.cpp:166:8:166:14 | call to basic_string | TAINT |
|
||||
| stl.cpp:174:18:174:23 | call to source | stl.cpp:174:18:174:26 | call to basic_string | TAINT |
|
||||
| stl.cpp:174:18:174:26 | call to basic_string | stl.cpp:179:8:179:9 | s1 | |
|
||||
| stl.cpp:175:19:175:27 | call to basic_string | stl.cpp:180:8:180:9 | s2 | |
|
||||
| stl.cpp:175:20:175:25 | call to source | stl.cpp:175:19:175:27 | call to basic_string | TAINT |
|
||||
| stl.cpp:177:8:177:13 | call to source | stl.cpp:177:8:177:15 | call to basic_string | TAINT |
|
||||
| stl.cpp:177:8:177:15 | call to basic_string | stl.cpp:177:3:177:15 | ... = ... | |
|
||||
| stl.cpp:177:8:177:15 | call to basic_string | stl.cpp:181:8:181:9 | s3 | |
|
||||
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:186:20:186:21 | s1 | |
|
||||
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:188:8:188:9 | s1 | |
|
||||
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:190:8:190:9 | s1 | |
|
||||
| stl.cpp:186:20:186:21 | s1 | stl.cpp:191:8:191:9 | s2 | |
|
||||
| stl.cpp:188:8:188:9 | s1 | stl.cpp:188:3:188:9 | ... = ... | |
|
||||
| stl.cpp:188:8:188:9 | s1 | stl.cpp:192:8:192:9 | s3 | |
|
||||
| stl.cpp:196:19:196:40 | call to basic_string | stl.cpp:200:8:200:9 | s1 | |
|
||||
| stl.cpp:196:32:196:37 | call to source | stl.cpp:196:19:196:40 | call to basic_string | TAINT |
|
||||
| stl.cpp:198:8:198:28 | call to basic_string | stl.cpp:198:3:198:28 | ... = ... | |
|
||||
| stl.cpp:198:8:198:28 | call to basic_string | stl.cpp:201:8:201:9 | s2 | |
|
||||
| stl.cpp:198:20:198:25 | call to source | stl.cpp:198:8:198:28 | call to basic_string | TAINT |
|
||||
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
|
||||
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
|
||||
| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | |
|
||||
|
||||
@@ -132,6 +132,31 @@ void test_strings2()
|
||||
sink(path3.c_str(), "r"); // tainted
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string_constructors_assignments()
|
||||
{
|
||||
{
|
||||
@@ -176,3 +201,4 @@ void test_string_constructors_assignments()
|
||||
sink(s2); // tainted
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,15 @@
|
||||
| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:154:8:154:9 | s1 | stl.cpp:149:18:149:23 | call to source |
|
||||
| stl.cpp:155:8:155:9 | s2 | stl.cpp:150:20:150:25 | call to source |
|
||||
| stl.cpp:156:8:156:9 | s3 | stl.cpp:152:8:152:13 | call to source |
|
||||
| stl.cpp:175:8:175:9 | s1 | stl.cpp:171:32:171:37 | call to source |
|
||||
| stl.cpp:176:8:176:9 | s2 | stl.cpp:173:20:173:25 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:143:7:143:8 | ss | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:156:7:156:8 | cs | stl.cpp:148:19:148:24 | call to source |
|
||||
| stl.cpp:157:7:157:8 | ss | stl.cpp:148:19:148:24 | call to source |
|
||||
| stl.cpp:179:8:179:9 | s1 | stl.cpp:174:18:174:23 | call to source |
|
||||
| stl.cpp:180:8:180:9 | s2 | stl.cpp:175:20:175:25 | call to source |
|
||||
| stl.cpp:181:8:181:9 | s3 | stl.cpp:177:8:177:13 | call to source |
|
||||
| stl.cpp:200:8:200:9 | s1 | stl.cpp:196:32:196:37 | call to source |
|
||||
| stl.cpp:201:8:201:9 | s2 | stl.cpp:198:20:198:25 | call to source |
|
||||
| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source |
|
||||
| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source |
|
||||
| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source |
|
||||
|
||||
@@ -27,11 +27,15 @@
|
||||
| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:154:8:154:9 | stl.cpp:149:18:149:23 | AST only |
|
||||
| stl.cpp:155:8:155:9 | stl.cpp:150:20:150:25 | AST only |
|
||||
| stl.cpp:156:8:156:9 | stl.cpp:152:8:152:13 | AST only |
|
||||
| stl.cpp:175:8:175:9 | stl.cpp:171:32:171:37 | AST only |
|
||||
| stl.cpp:176:8:176:9 | stl.cpp:173:20:173:25 | AST only |
|
||||
| stl.cpp:142:7:142:8 | stl.cpp:137:19:137:26 | IR only |
|
||||
| stl.cpp:143:7:143:8 | stl.cpp:137:19:137:24 | AST only |
|
||||
| stl.cpp:156:7:156:8 | stl.cpp:148:19:148:24 | AST only |
|
||||
| stl.cpp:157:7:157:8 | stl.cpp:148:19:148:24 | AST only |
|
||||
| stl.cpp:179:8:179:9 | stl.cpp:174:18:174:23 | AST only |
|
||||
| stl.cpp:180:8:180:9 | stl.cpp:175:20:175:25 | AST only |
|
||||
| stl.cpp:181:8:181:9 | stl.cpp:177:8:177:13 | AST only |
|
||||
| stl.cpp:200:8:200:9 | stl.cpp:196:32:196:37 | AST only |
|
||||
| stl.cpp:201:8:201:9 | stl.cpp:198:20:198:25 | AST only |
|
||||
| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:22:29:27 | AST only |
|
||||
| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:30:24:30:29 | AST only |
|
||||
| structlikeclass.cpp:37:8:37:9 | structlikeclass.cpp:29:22:29:27 | AST only |
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
|
||||
| stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source |
|
||||
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:26 | (const char *)... |
|
||||
| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source |
|
||||
| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source |
|
||||
| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source |
|
||||
|
||||
Reference in New Issue
Block a user