Data flow: Track precise types during field flow

cf https://github.com/github/codeql/pull/3456
This commit is contained in:
Max Schaefer
2020-06-22 10:52:59 +01:00
parent d8374adbde
commit d3e6e5c0b3
3 changed files with 392 additions and 325 deletions

View File

@@ -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(

View File

@@ -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 }
}

View File

@@ -77,12 +77,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 DataFlowType getContainerType();
/** Gets the type of this content. */
abstract DataFlowType getType();
}
private class FieldContent extends Content, TFieldContent {
@@ -95,34 +89,18 @@ private class FieldContent extends Content, TFieldContent {
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
f.getDeclaration().hasLocationInfo(path, sl, sc, el, ec)
}
override DataFlowType getContainerType() { result = f.getDeclaringType() }
override DataFlowType getType() { result = f.getType() }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
override DataFlowType getContainerType() { none() }
override DataFlowType getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "array" }
override DataFlowType getContainerType() { none() }
override DataFlowType getType() { none() }
}
private class PointerContent extends Content, TPointerContent {
override string toString() { result = "pointer" }
override DataFlowType getContainerType() { this = TPointerContent(result) }
override DataFlowType getType() { result = getContainerType().(PointerType).getBaseType() }
}
/**