diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md index 01d35a12c2d..245d3a3130e 100644 --- a/change-notes/1.26/analysis-javascript.md +++ b/change-notes/1.26/analysis-javascript.md @@ -2,7 +2,10 @@ ## General improvements +* Angular-specific taint sources and sinks are now recognized by the security queries. + * Support for the following frameworks and libraries has been improved: + - [@angular/*](https://www.npmjs.com/package/@angular/core) - [AWS Serverless](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) - [Alibaba Serverless](https://www.alibabacloud.com/help/doc-detail/156876.htm) - [bluebird](https://www.npmjs.com/package/bluebird) diff --git a/cpp/ql/src/semmle/code/cpp/Include.qll b/cpp/ql/src/semmle/code/cpp/Include.qll index 11702ce1bf6..f21edb2651d 100644 --- a/cpp/ql/src/semmle/code/cpp/Include.qll +++ b/cpp/ql/src/semmle/code/cpp/Include.qll @@ -21,7 +21,7 @@ class Include extends PreprocessorDirective, @ppd_include { /** * Gets the token which occurs after `#include`, for example `"filename"` - * or `<filename>`. + * or ``. */ string getIncludeText() { result = getHead() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll new file mode 100644 index 00000000000..337dc71a3ca --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -0,0 +1,169 @@ +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil + +/** + * Gets a short ID for an IR dataflow node. + * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). + * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the + * instruction and a dot (e.g. `m128.left`). + * - For `Variable`s, this is the qualified name of the variable. + */ +private string nodeId(DataFlow::Node node, int order1, int order2) { + exists(Instruction instruction | instruction = node.asInstruction() | + result = instruction.getResultId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + exists(Operand operand, Instruction instruction | + operand = node.asOperand() and + instruction = operand.getUse() + | + result = instruction.getResultId() + "." + operand.getDumpId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + result = "var(" + node.asVariable().getQualifiedName() + ")" and + order1 = 1000000 and + order2 = 0 +} + +/** + * Gets the local dataflow from other nodes in the same function to this node. + */ +private string getFromFlow(DataFlow::Node useNode, int order1, int order2) { + exists(DataFlow::Node defNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if defNode.asInstruction() = useNode.asOperand().getAnyDef() + then + // Shorthand for flow from the def of this operand. + result = prefix + "def" and + order1 = -1 and + order2 = 0 + else + if defNode.asOperand().getUse() = useNode.asInstruction() + then + // Shorthand for flow from an operand of this instruction + result = prefix + defNode.asOperand().getDumpId() and + order1 = -1 and + order2 = defNode.asOperand().getDumpSortOrder() + else result = prefix + nodeId(defNode, order1, order2) + ) +} + +/** + * Gets the local dataflow from this node to other nodes in the same function. + */ +private string getToFlow(DataFlow::Node defNode, int order1, int order2) { + exists(DataFlow::Node useNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if useNode.asInstruction() = defNode.asOperand().getUse() + then + // Shorthand for flow to this operand's instruction. + result = prefix + "result" and + order1 = -1 and + order2 = 0 + else result = prefix + nodeId(useNode, order1, order2) + ) +} + +/** + * Gets the properties of the dataflow node `node`. + */ +private string getNodeProperty(DataFlow::Node node, string key) { + // List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow + // out of this node is printed as `@->dest`. + key = "flow" and + result = + strictconcat(string flow, boolean to, int order1, int order2 | + flow = getFromFlow(node, order1, order2) + "->@" and to = false + or + flow = "@->" + getToFlow(node, order1, order2) and to = true + | + flow, ", " order by to, order1, order2, flow + ) + or + // Is this node a dataflow sink? + key = "sink" and + any(DataFlow::Configuration cfg).isSink(node) and + result = "true" + or + // Is this node a dataflow source? + key = "source" and + any(DataFlow::Configuration cfg).isSource(node) and + result = "true" + or + // Is this node a dataflow barrier, and if so, what kind? + key = "barrier" and + result = + strictconcat(string kind | + any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full" + or + any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in" + or + any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out" + or + exists(DataFlow::BarrierGuard guard | + any(DataFlow::Configuration cfg).isBarrierGuard(guard) and + node = guard.getAGuardedNode() and + kind = "guard(" + guard.getResultId() + ")" + ) + | + kind, ", " + ) + or + // Is there partial flow from a source to this node? + // This property will only be emitted if partial flow is enabled by overriding + // `DataFlow::Configration::explorationLimit()`. + key = "pflow" and + result = + strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist, + int order1, int order2 | + any(DataFlow::Configuration cfg).hasPartialFlow(sourceNode, destNode, dist) and + destNode.getNode() = node and + // Only print flow from a source in the same function. + sourceNode.getNode().getEnclosingCallable() = node.getEnclosingCallable() + | + nodeId(sourceNode.getNode(), order1, order2) + "+" + dist.toString(), ", " + order by + order1, order2, dist desc + ) +} + +/** + * Property provider for local IR dataflow. + */ +class LocalFlowPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + exists(DataFlow::Node node | + operand = node.asOperand() and + result = getNodeProperty(node, key) + ) + } + + override string getInstructionProperty(Instruction instruction, string key) { + exists(DataFlow::Node node | + instruction = node.asInstruction() and + result = getNodeProperty(node, key) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index e476aec60af..a12e35d471b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index b3e3a5b1195..59dadee7154 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -50,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +221,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +241,22 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll index ac284440648..21dfedd95cd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll @@ -40,7 +40,17 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - string getLabel() { result = "" } + final string getLabel() { if alwaysPrintLabel() then result = getId() + ":" else result = "" } + + /** + * Gets an identifier that uniquely identifies this operand within its instruction. + */ + abstract string getId(); + + /** + * Holds if the operand should always be prefixed with its label in the dump of its instruction. + */ + predicate alwaysPrintLabel() { none() } } /** @@ -69,7 +79,9 @@ class AddressOperandTag extends RegisterOperandTag, TAddressOperand { final override int getSortOrder() { result = 0 } - final override string getLabel() { result = "&:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "&" } } AddressOperandTag addressOperand() { result = TAddressOperand() } @@ -82,6 +94,8 @@ class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand { final override string toString() { result = "BufferSize" } final override int getSortOrder() { result = 1 } + + final override string getId() { result = "size" } } BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() } @@ -93,6 +107,8 @@ class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand { final override string toString() { result = "SideEffect" } final override int getSortOrder() { result = 2 } + + final override string getId() { result = "side_effect" } } SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() } @@ -105,6 +121,8 @@ class LoadOperandTag extends TypedOperandTag, TLoadOperand { final override string toString() { result = "Load" } final override int getSortOrder() { result = 3 } + + final override string getId() { result = "load" } } LoadOperandTag loadOperand() { result = TLoadOperand() } @@ -116,6 +134,8 @@ class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand { final override string toString() { result = "StoreValue" } final override int getSortOrder() { result = 4 } + + final override string getId() { result = "store" } } StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() } @@ -127,6 +147,8 @@ class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand { final override string toString() { result = "Unary" } final override int getSortOrder() { result = 5 } + + final override string getId() { result = "unary" } } UnaryOperandTag unaryOperand() { result = TUnaryOperand() } @@ -138,6 +160,8 @@ class LeftOperandTag extends RegisterOperandTag, TLeftOperand { final override string toString() { result = "Left" } final override int getSortOrder() { result = 6 } + + final override string getId() { result = "left" } } LeftOperandTag leftOperand() { result = TLeftOperand() } @@ -149,6 +173,8 @@ class RightOperandTag extends RegisterOperandTag, TRightOperand { final override string toString() { result = "Right" } final override int getSortOrder() { result = 7 } + + final override string getId() { result = "right" } } RightOperandTag rightOperand() { result = TRightOperand() } @@ -160,6 +186,8 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand { final override string toString() { result = "Condition" } final override int getSortOrder() { result = 8 } + + final override string getId() { result = "cond" } } ConditionOperandTag conditionOperand() { result = TConditionOperand() } @@ -172,7 +200,9 @@ class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand { final override int getSortOrder() { result = 10 } - final override string getLabel() { result = "func:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "func" } } CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() } @@ -195,7 +225,9 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand { final override int getSortOrder() { result = 11 } - final override string getLabel() { result = "this:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "this" } } ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() } @@ -212,9 +244,11 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgume final override int getSortOrder() { result = 12 + argIndex } - final override string getLabel() { result = argIndex.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } final int getArgIndex() { result = argIndex } + + final override string getId() { result = argIndex.toString() } } PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) { @@ -228,7 +262,9 @@ class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand { final override int getSortOrder() { result = 13 } - final override string getLabel() { result = "total:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "total" } } ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() } @@ -238,7 +274,9 @@ class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand { final override int getSortOrder() { result = 14 } - final override string getLabel() { result = "partial:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "partial" } } ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() } @@ -252,7 +290,9 @@ class AsmOperandTag extends RegisterOperandTag, TAsmOperand { final override int getSortOrder() { result = 15 + index } - final override string getLabel() { result = index.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = index.toString() } } AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index e476aec60af..a12e35d471b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index b3e3a5b1195..59dadee7154 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -50,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +221,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +241,22 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index e476aec60af..a12e35d471b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index b3e3a5b1195..59dadee7154 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -50,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +221,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +241,22 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp index 13769912d30..cb55bdd2863 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp @@ -39,17 +39,8 @@ void sink(int x) void bar(Outer &b) { - // The library correctly finds that the four `user_input` sources can make it - // to the `sink` calls, but it also finds some source/sink combinations that - // are impossible. Those false positives here are a consequence of how the - // shared data flow library overapproximates field flow. The library only - // tracks the final two fields (`f` and `inner`) and the length (3) of the field - // access path, and then it tracks that both `a_` and `b_` have followed `f.inner` - // in _some_ access path somewhere in the search. That makes the library conclude - // that there could be flow to `b.inner.f.a_` even when the flow was actually to - // `b.inner.f.b_`. - sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 - sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 + sink(b.inner.f.a()); // $ast=53:19 $ast=55:19 $ir=53:19 $ir=55:19 + sink(b.inner.f.b()); // $ast=54:19 $ast=56:19 $ir=54:19 $ir=56:19 } void foo() diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected index 2686f48f236..242c09f3a3b 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected @@ -34,10 +34,6 @@ | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | AST only | | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | AST only | | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | AST only | -| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | AST only | -| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | AST only | -| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | AST only | -| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | AST only | | qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:23:23:23:23 | a | AST only | | qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:28:23:28:23 | a | AST only | | qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:33:23:33:23 | a | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected index d24c311a65b..e69de29bb2d 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected @@ -1,4 +0,0 @@ -| complex.cpp:51:24:51:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=63:19 | -| complex.cpp:51:24:51:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=65:19 | -| complex.cpp:52:24:52:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=62:19 | -| complex.cpp:52:24:52:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=64:19 | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index ed18d6a633e..61770c0aca3 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -140,20 +140,20 @@ edges | by_reference.cpp:128:15:128:23 | Chi | by_reference.cpp:128:15:128:23 | Chi [a] | | by_reference.cpp:128:15:128:23 | Chi [a] | by_reference.cpp:136:16:136:16 | a | | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | by_reference.cpp:128:15:128:23 | Chi | -| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:51:18:51:18 | call to a | -| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:51:16:51:16 | a output argument [b_] | -| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:51:16:51:16 | a output argument [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:62:12:62:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | -| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | setA output argument [a_] | -| complex.cpp:63:12:63:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | -| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | setB output argument [b_] | -| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | -| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:65:12:65:12 | setB output argument [a_] | -| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | setA output argument [a_] | -| complex.cpp:65:12:65:12 | setB output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | -| complex.cpp:65:12:65:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | -| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | setB output argument [b_] | +| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:18:42:18 | call to a | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | a output argument [b_] | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] | +| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] | +| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] | +| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] | +| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] | | constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:12:28:12 | call to a | | constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] | | constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:12:29:12 | call to b | @@ -371,18 +371,18 @@ nodes | by_reference.cpp:136:16:136:16 | a | semmle.label | a | | complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] | | complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] | -| complex.cpp:51:16:51:16 | a output argument [b_] | semmle.label | a output argument [b_] | -| complex.cpp:51:18:51:18 | call to a | semmle.label | call to a | -| complex.cpp:52:18:52:18 | call to b | semmle.label | call to b | -| complex.cpp:62:12:62:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | -| complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:63:12:63:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | -| complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:64:12:64:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | -| complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:65:12:65:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | -| complex.cpp:65:12:65:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | -| complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] | +| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | +| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | +| complex.cpp:53:12:53:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:54:12:54:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:55:12:55:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:56:12:56:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | +| complex.cpp:56:12:56:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | | constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | | constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | | constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] | @@ -478,10 +478,10 @@ nodes | by_reference.cpp:132:14:132:14 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:132:14:132:14 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | | constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected index a311a77a196..51b43e6be1c 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -293,28 +293,28 @@ | by_reference.cpp:136:16:136:16 | a | AST only | | complex.cpp:11:22:11:23 | a_ | AST only | | complex.cpp:12:22:12:23 | b_ | AST only | -| complex.cpp:51:8:51:8 | b | AST only | -| complex.cpp:51:10:51:14 | inner | AST only | -| complex.cpp:51:16:51:16 | f | AST only | -| complex.cpp:52:8:52:8 | b | AST only | -| complex.cpp:52:10:52:14 | inner | AST only | -| complex.cpp:52:16:52:16 | f | AST only | -| complex.cpp:62:3:62:4 | b1 | AST only | -| complex.cpp:62:6:62:10 | inner | AST only | -| complex.cpp:62:12:62:12 | f | AST only | -| complex.cpp:63:3:63:4 | b2 | AST only | -| complex.cpp:63:6:63:10 | inner | AST only | -| complex.cpp:63:12:63:12 | f | AST only | -| complex.cpp:64:3:64:4 | b3 | AST only | -| complex.cpp:64:6:64:10 | inner | AST only | -| complex.cpp:64:12:64:12 | f | AST only | -| complex.cpp:65:3:65:4 | b3 | AST only | -| complex.cpp:65:6:65:10 | inner | AST only | -| complex.cpp:65:12:65:12 | f | AST only | -| complex.cpp:68:7:68:8 | b1 | AST only | -| complex.cpp:71:7:71:8 | b2 | AST only | -| complex.cpp:74:7:74:8 | b3 | AST only | -| complex.cpp:77:7:77:8 | b4 | AST only | +| complex.cpp:42:8:42:8 | b | AST only | +| complex.cpp:42:10:42:14 | inner | AST only | +| complex.cpp:42:16:42:16 | f | AST only | +| complex.cpp:43:8:43:8 | b | AST only | +| complex.cpp:43:10:43:14 | inner | AST only | +| complex.cpp:43:16:43:16 | f | AST only | +| complex.cpp:53:3:53:4 | b1 | AST only | +| complex.cpp:53:6:53:10 | inner | AST only | +| complex.cpp:53:12:53:12 | f | AST only | +| complex.cpp:54:3:54:4 | b2 | AST only | +| complex.cpp:54:6:54:10 | inner | AST only | +| complex.cpp:54:12:54:12 | f | AST only | +| complex.cpp:55:3:55:4 | b3 | AST only | +| complex.cpp:55:6:55:10 | inner | AST only | +| complex.cpp:55:12:55:12 | f | AST only | +| complex.cpp:56:3:56:4 | b3 | AST only | +| complex.cpp:56:6:56:10 | inner | AST only | +| complex.cpp:56:12:56:12 | f | AST only | +| complex.cpp:59:7:59:8 | b1 | AST only | +| complex.cpp:62:7:62:8 | b2 | AST only | +| complex.cpp:65:7:65:8 | b3 | AST only | +| complex.cpp:68:7:68:8 | b4 | AST only | | constructors.cpp:20:24:20:25 | a_ | AST only | | constructors.cpp:21:24:21:25 | b_ | AST only | | constructors.cpp:28:10:28:10 | f | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected index 707b15b7f7d..6c58d51ff74 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -344,28 +344,28 @@ | complex.cpp:11:22:11:23 | this | | complex.cpp:12:22:12:23 | b_ | | complex.cpp:12:22:12:23 | this | -| complex.cpp:51:8:51:8 | b | -| complex.cpp:51:10:51:14 | inner | -| complex.cpp:51:16:51:16 | f | -| complex.cpp:52:8:52:8 | b | -| complex.cpp:52:10:52:14 | inner | -| complex.cpp:52:16:52:16 | f | -| complex.cpp:62:3:62:4 | b1 | -| complex.cpp:62:6:62:10 | inner | -| complex.cpp:62:12:62:12 | f | -| complex.cpp:63:3:63:4 | b2 | -| complex.cpp:63:6:63:10 | inner | -| complex.cpp:63:12:63:12 | f | -| complex.cpp:64:3:64:4 | b3 | -| complex.cpp:64:6:64:10 | inner | -| complex.cpp:64:12:64:12 | f | -| complex.cpp:65:3:65:4 | b3 | -| complex.cpp:65:6:65:10 | inner | -| complex.cpp:65:12:65:12 | f | -| complex.cpp:68:7:68:8 | b1 | -| complex.cpp:71:7:71:8 | b2 | -| complex.cpp:74:7:74:8 | b3 | -| complex.cpp:77:7:77:8 | b4 | +| complex.cpp:42:8:42:8 | b | +| complex.cpp:42:10:42:14 | inner | +| complex.cpp:42:16:42:16 | f | +| complex.cpp:43:8:43:8 | b | +| complex.cpp:43:10:43:14 | inner | +| complex.cpp:43:16:43:16 | f | +| complex.cpp:53:3:53:4 | b1 | +| complex.cpp:53:6:53:10 | inner | +| complex.cpp:53:12:53:12 | f | +| complex.cpp:54:3:54:4 | b2 | +| complex.cpp:54:6:54:10 | inner | +| complex.cpp:54:12:54:12 | f | +| complex.cpp:55:3:55:4 | b3 | +| complex.cpp:55:6:55:10 | inner | +| complex.cpp:55:12:55:12 | f | +| complex.cpp:56:3:56:4 | b3 | +| complex.cpp:56:6:56:10 | inner | +| complex.cpp:56:12:56:12 | f | +| complex.cpp:59:7:59:8 | b1 | +| complex.cpp:62:7:62:8 | b2 | +| complex.cpp:65:7:65:8 | b3 | +| complex.cpp:68:7:68:8 | b4 | | constructors.cpp:20:24:20:25 | a_ | | constructors.cpp:20:24:20:25 | this | | constructors.cpp:21:24:21:25 | b_ | diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected index bfab3380b4e..b932e0395f4 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected @@ -51,14 +51,14 @@ edges | A.cpp:160:29:160:29 | b | A.cpp:160:18:160:60 | call to MyList [head] | | A.cpp:161:18:161:40 | call to MyList [next, head] | A.cpp:162:38:162:39 | l2 [next, head] | | A.cpp:161:38:161:39 | l1 [head] | A.cpp:161:18:161:40 | call to MyList [next, head] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | A.cpp:167:44:167:44 | l [next, next, ... (3)] | -| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | -| A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | A.cpp:165:14:165:17 | next [next, head] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:165:10:165:11 | l3 [next, next, head] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:167:44:167:44 | l [next, next, head] | +| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] | +| A.cpp:165:10:165:11 | l3 [next, next, head] | A.cpp:165:14:165:17 | next [next, head] | | A.cpp:165:14:165:17 | next [next, head] | A.cpp:165:20:165:23 | next [head] | | A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head | | A.cpp:167:44:167:44 | l [next, head] | A.cpp:167:47:167:50 | next [head] | -| A.cpp:167:44:167:44 | l [next, next, ... (3)] | A.cpp:167:47:167:50 | next [next, head] | +| A.cpp:167:44:167:44 | l [next, next, head] | A.cpp:167:47:167:50 | next [next, head] | | A.cpp:167:47:167:50 | next [head] | A.cpp:169:12:169:12 | l [head] | | A.cpp:167:47:167:50 | next [next, head] | A.cpp:167:44:167:44 | l [next, head] | | A.cpp:169:12:169:12 | l [head] | A.cpp:169:15:169:18 | head | @@ -113,14 +113,14 @@ edges | D.cpp:51:27:51:27 | e | D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] | | D.cpp:52:14:52:14 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] | | D.cpp:56:15:56:24 | new | D.cpp:58:5:58:27 | ... = ... | -| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | -| D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | +| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | +| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | D.cpp:59:5:59:7 | this [boxfield, box, elem] | | D.cpp:58:5:58:27 | ... = ... | D.cpp:58:15:58:17 | box [post update] [elem] | | D.cpp:58:15:58:17 | box [post update] [elem] | D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | -| D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | -| D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | +| D.cpp:59:5:59:7 | this [boxfield, box, elem] | D.cpp:63:8:63:10 | this [boxfield, box, elem] | +| D.cpp:63:8:63:10 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | this [boxfield, box, elem] | | D.cpp:64:10:64:17 | boxfield [box, elem] | D.cpp:64:20:64:22 | box [elem] | -| D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | D.cpp:64:10:64:17 | boxfield [box, elem] | +| D.cpp:64:10:64:17 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | boxfield [box, elem] | | D.cpp:64:20:64:22 | box [elem] | D.cpp:64:25:64:28 | elem | | E.cpp:19:27:19:27 | p [data, buffer] | E.cpp:21:10:21:10 | p [data, buffer] | | E.cpp:21:10:21:10 | p [data, buffer] | E.cpp:21:13:21:16 | data [buffer] | @@ -193,33 +193,33 @@ edges | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array | | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | -| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | arrays.cpp:37:8:37:8 | o [nested, arr, ... (3)] | -| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | arrays.cpp:38:8:38:8 | o [nested, arr, ... (3)] | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:37:8:37:8 | o [nested, arr, data] | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [nested, arr, data] | | arrays.cpp:36:3:36:17 | access to array [post update] [data] | arrays.cpp:36:12:36:14 | arr [inner post update] [data] | | arrays.cpp:36:3:36:37 | ... = ... | arrays.cpp:36:3:36:17 | access to array [post update] [data] | -| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | +| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | | arrays.cpp:36:12:36:14 | arr [inner post update] [data] | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:36:3:36:37 | ... = ... | -| arrays.cpp:37:8:37:8 | o [nested, arr, ... (3)] | arrays.cpp:37:10:37:15 | nested [arr, data] | +| arrays.cpp:37:8:37:8 | o [nested, arr, data] | arrays.cpp:37:10:37:15 | nested [arr, data] | | arrays.cpp:37:8:37:22 | access to array [data] | arrays.cpp:37:24:37:27 | data | | arrays.cpp:37:10:37:15 | nested [arr, data] | arrays.cpp:37:17:37:19 | arr [data] | | arrays.cpp:37:17:37:19 | arr [data] | arrays.cpp:37:8:37:22 | access to array [data] | -| arrays.cpp:38:8:38:8 | o [nested, arr, ... (3)] | arrays.cpp:38:10:38:15 | nested [arr, data] | +| arrays.cpp:38:8:38:8 | o [nested, arr, data] | arrays.cpp:38:10:38:15 | nested [arr, data] | | arrays.cpp:38:8:38:22 | access to array [data] | arrays.cpp:38:24:38:27 | data | | arrays.cpp:38:10:38:15 | nested [arr, data] | arrays.cpp:38:17:38:19 | arr [data] | | arrays.cpp:38:17:38:19 | arr [data] | arrays.cpp:38:8:38:22 | access to array [data] | -| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | arrays.cpp:43:8:43:8 | o [indirect, arr, ... (3)] | -| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | arrays.cpp:44:8:44:8 | o [indirect, arr, ... (3)] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:43:8:43:8 | o [indirect, arr, data] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [indirect, arr, data] | | arrays.cpp:42:3:42:20 | access to array [post update] [data] | arrays.cpp:42:15:42:17 | arr [inner post update] [data] | | arrays.cpp:42:3:42:40 | ... = ... | arrays.cpp:42:3:42:20 | access to array [post update] [data] | -| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | +| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | | arrays.cpp:42:15:42:17 | arr [inner post update] [data] | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:42:3:42:40 | ... = ... | -| arrays.cpp:43:8:43:8 | o [indirect, arr, ... (3)] | arrays.cpp:43:10:43:17 | indirect [arr, data] | +| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | arrays.cpp:43:10:43:17 | indirect [arr, data] | | arrays.cpp:43:8:43:25 | access to array [data] | arrays.cpp:43:27:43:30 | data | | arrays.cpp:43:10:43:17 | indirect [arr, data] | arrays.cpp:43:20:43:22 | arr [data] | | arrays.cpp:43:20:43:22 | arr [data] | arrays.cpp:43:8:43:25 | access to array [data] | -| arrays.cpp:44:8:44:8 | o [indirect, arr, ... (3)] | arrays.cpp:44:10:44:17 | indirect [arr, data] | +| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | arrays.cpp:44:10:44:17 | indirect [arr, data] | | arrays.cpp:44:8:44:25 | access to array [data] | arrays.cpp:44:27:44:30 | data | | arrays.cpp:44:10:44:17 | indirect [arr, data] | arrays.cpp:44:20:44:22 | arr [data] | | arrays.cpp:44:20:44:22 | arr [data] | arrays.cpp:44:8:44:25 | access to array [data] | @@ -308,33 +308,34 @@ edges | by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] | | by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | -| complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | complex.cpp:51:10:51:14 | inner [f, a_] | -| complex.cpp:51:10:51:14 | inner [f, a_] | complex.cpp:51:16:51:16 | f [a_] | -| complex.cpp:51:16:51:16 | f [a_] | complex.cpp:51:18:51:18 | call to a | -| complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | complex.cpp:52:10:52:14 | inner [f, b_] | -| complex.cpp:52:10:52:14 | inner [f, b_] | complex.cpp:52:16:52:16 | f [b_] | -| complex.cpp:52:16:52:16 | f [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | -| complex.cpp:62:6:62:10 | inner [post update] [f, a_] | complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | -| complex.cpp:62:12:62:12 | ref arg f [a_] | complex.cpp:62:6:62:10 | inner [post update] [f, a_] | -| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | ref arg f [a_] | -| complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | -| complex.cpp:63:6:63:10 | inner [post update] [f, b_] | complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | -| complex.cpp:63:12:63:12 | ref arg f [b_] | complex.cpp:63:6:63:10 | inner [post update] [f, b_] | -| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | ref arg f [b_] | -| complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | -| complex.cpp:64:6:64:10 | inner [post update] [f, a_] | complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | ref arg f [a_] | complex.cpp:64:6:64:10 | inner [post update] [f, a_] | -| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | ref arg f [a_] | -| complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | -| complex.cpp:65:6:65:10 | inner [post update] [f, b_] | complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | ref arg f [b_] | complex.cpp:65:6:65:10 | inner [post update] [f, b_] | -| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | ref arg f [b_] | -| complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | -| complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | +| complex.cpp:40:17:40:17 | b [inner, f, a_] | complex.cpp:42:8:42:8 | b [inner, f, a_] | +| complex.cpp:40:17:40:17 | b [inner, f, b_] | complex.cpp:43:8:43:8 | b [inner, f, b_] | +| complex.cpp:42:8:42:8 | b [inner, f, a_] | complex.cpp:42:10:42:14 | inner [f, a_] | +| complex.cpp:42:10:42:14 | inner [f, a_] | complex.cpp:42:16:42:16 | f [a_] | +| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:42:18:42:18 | call to a | +| complex.cpp:43:8:43:8 | b [inner, f, b_] | complex.cpp:43:10:43:14 | inner [f, b_] | +| complex.cpp:43:10:43:14 | inner [f, b_] | complex.cpp:43:16:43:16 | f [b_] | +| complex.cpp:43:16:43:16 | f [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | complex.cpp:59:7:59:8 | b1 [inner, f, a_] | +| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | +| complex.cpp:53:12:53:12 | ref arg f [a_] | complex.cpp:53:6:53:10 | inner [post update] [f, a_] | +| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | ref arg f [a_] | +| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | complex.cpp:62:7:62:8 | b2 [inner, f, b_] | +| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | +| complex.cpp:54:12:54:12 | ref arg f [b_] | complex.cpp:54:6:54:10 | inner [post update] [f, b_] | +| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | ref arg f [b_] | +| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | complex.cpp:65:7:65:8 | b3 [inner, f, a_] | +| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | +| complex.cpp:55:12:55:12 | ref arg f [a_] | complex.cpp:55:6:55:10 | inner [post update] [f, a_] | +| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | ref arg f [a_] | +| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | complex.cpp:65:7:65:8 | b3 [inner, f, b_] | +| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | +| complex.cpp:56:12:56:12 | ref arg f [b_] | complex.cpp:56:6:56:10 | inner [post update] [f, b_] | +| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | ref arg f [b_] | +| complex.cpp:59:7:59:8 | b1 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] | +| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] | | constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:10:28:10 | f [a_] | | constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:10:29:10 | f [b_] | | constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:28:12:28:12 | call to a | @@ -386,16 +387,16 @@ edges | qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:47:5:47:42 | ... = ... | | qualifiers.cpp:48:10:48:14 | outer [inner, a] | qualifiers.cpp:48:16:48:20 | inner [a] | | qualifiers.cpp:48:16:48:20 | inner [a] | qualifiers.cpp:48:23:48:23 | a | -| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, ... (4)] | realistic.cpp:61:21:61:23 | foo [bar, baz, ... (4)] | -| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, ... (3)] | realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | | realistic.cpp:53:9:53:66 | ... = ... | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | -| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, ... (3)] | realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, ... (4)] | -| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | | realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:53:9:53:66 | ... = ... | -| realistic.cpp:61:21:61:23 | foo [bar, baz, ... (4)] | realistic.cpp:61:25:61:27 | bar [baz, userInput, ... (3)] | -| realistic.cpp:61:21:61:30 | access to array [baz, userInput, ... (3)] | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | -| realistic.cpp:61:25:61:27 | bar [baz, userInput, ... (3)] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, ... (3)] | +| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | +| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | +| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | realistic.cpp:61:37:61:45 | userInput [bufferLen] | | realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen | | simple.cpp:26:15:26:15 | f [a_] | simple.cpp:28:10:28:10 | f [a_] | @@ -517,14 +518,14 @@ nodes | A.cpp:160:29:160:29 | b | semmle.label | b | | A.cpp:161:18:161:40 | call to MyList [next, head] | semmle.label | call to MyList [next, head] | | A.cpp:161:38:161:39 | l1 [head] | semmle.label | l1 [head] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | semmle.label | call to MyList [next, next, ... (3)] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | semmle.label | call to MyList [next, next, head] | | A.cpp:162:38:162:39 | l2 [next, head] | semmle.label | l2 [next, head] | -| A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | semmle.label | l3 [next, next, ... (3)] | +| A.cpp:165:10:165:11 | l3 [next, next, head] | semmle.label | l3 [next, next, head] | | A.cpp:165:14:165:17 | next [next, head] | semmle.label | next [next, head] | | A.cpp:165:20:165:23 | next [head] | semmle.label | next [head] | | A.cpp:165:26:165:29 | head | semmle.label | head | | A.cpp:167:44:167:44 | l [next, head] | semmle.label | l [next, head] | -| A.cpp:167:44:167:44 | l [next, next, ... (3)] | semmle.label | l [next, next, ... (3)] | +| A.cpp:167:44:167:44 | l [next, next, head] | semmle.label | l [next, next, head] | | A.cpp:167:47:167:50 | next [head] | semmle.label | next [head] | | A.cpp:167:47:167:50 | next [next, head] | semmle.label | next [next, head] | | A.cpp:169:12:169:12 | l [head] | semmle.label | l [head] | @@ -586,13 +587,13 @@ nodes | D.cpp:52:14:52:14 | b [box, elem] | semmle.label | b [box, elem] | | D.cpp:56:15:56:24 | new | semmle.label | new | | D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | semmle.label | boxfield [post update] [box, elem] | -| D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | semmle.label | this [post update] [boxfield, box, ... (3)] | +| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | semmle.label | this [post update] [boxfield, box, elem] | | D.cpp:58:5:58:27 | ... = ... | semmle.label | ... = ... | | D.cpp:58:15:58:17 | box [post update] [elem] | semmle.label | box [post update] [elem] | -| D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | -| D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | +| D.cpp:59:5:59:7 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | +| D.cpp:63:8:63:10 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | | D.cpp:64:10:64:17 | boxfield [box, elem] | semmle.label | boxfield [box, elem] | -| D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | +| D.cpp:64:10:64:17 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | | D.cpp:64:20:64:22 | box [elem] | semmle.label | box [elem] | | D.cpp:64:25:64:28 | elem | semmle.label | elem | | E.cpp:19:27:19:27 | p [data, buffer] | semmle.label | p [data, buffer] | @@ -675,34 +676,34 @@ nodes | arrays.cpp:15:14:15:23 | call to user_input | semmle.label | call to user_input | | arrays.cpp:16:8:16:13 | access to array | semmle.label | access to array | | arrays.cpp:17:8:17:13 | access to array | semmle.label | access to array | -| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | semmle.label | o [post update] [nested, arr, ... (3)] | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | semmle.label | o [post update] [nested, arr, data] | | arrays.cpp:36:3:36:17 | access to array [post update] [data] | semmle.label | access to array [post update] [data] | | arrays.cpp:36:3:36:37 | ... = ... | semmle.label | ... = ... | | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | semmle.label | nested [post update] [arr, data] | | arrays.cpp:36:12:36:14 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] | | arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input | -| arrays.cpp:37:8:37:8 | o [nested, arr, ... (3)] | semmle.label | o [nested, arr, ... (3)] | +| arrays.cpp:37:8:37:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] | | arrays.cpp:37:8:37:22 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:37:10:37:15 | nested [arr, data] | semmle.label | nested [arr, data] | | arrays.cpp:37:17:37:19 | arr [data] | semmle.label | arr [data] | | arrays.cpp:37:24:37:27 | data | semmle.label | data | -| arrays.cpp:38:8:38:8 | o [nested, arr, ... (3)] | semmle.label | o [nested, arr, ... (3)] | +| arrays.cpp:38:8:38:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] | | arrays.cpp:38:8:38:22 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:38:10:38:15 | nested [arr, data] | semmle.label | nested [arr, data] | | arrays.cpp:38:17:38:19 | arr [data] | semmle.label | arr [data] | | arrays.cpp:38:24:38:27 | data | semmle.label | data | -| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | semmle.label | o [post update] [indirect, arr, ... (3)] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | semmle.label | o [post update] [indirect, arr, data] | | arrays.cpp:42:3:42:20 | access to array [post update] [data] | semmle.label | access to array [post update] [data] | | arrays.cpp:42:3:42:40 | ... = ... | semmle.label | ... = ... | | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | semmle.label | indirect [post update] [arr, data] | | arrays.cpp:42:15:42:17 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] | | arrays.cpp:42:29:42:38 | call to user_input | semmle.label | call to user_input | -| arrays.cpp:43:8:43:8 | o [indirect, arr, ... (3)] | semmle.label | o [indirect, arr, ... (3)] | +| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] | | arrays.cpp:43:8:43:25 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:43:10:43:17 | indirect [arr, data] | semmle.label | indirect [arr, data] | | arrays.cpp:43:20:43:22 | arr [data] | semmle.label | arr [data] | | arrays.cpp:43:27:43:30 | data | semmle.label | data | -| arrays.cpp:44:8:44:8 | o [indirect, arr, ... (3)] | semmle.label | o [indirect, arr, ... (3)] | +| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] | | arrays.cpp:44:8:44:25 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:44:10:44:17 | indirect [arr, data] | semmle.label | indirect [arr, data] | | arrays.cpp:44:20:44:22 | arr [data] | semmle.label | arr [data] | @@ -796,34 +797,36 @@ nodes | by_reference.cpp:135:27:135:27 | a | semmle.label | a | | by_reference.cpp:136:8:136:13 | pouter [a] | semmle.label | pouter [a] | | by_reference.cpp:136:16:136:16 | a | semmle.label | a | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:51:10:51:14 | inner [f, a_] | semmle.label | inner [f, a_] | -| complex.cpp:51:16:51:16 | f [a_] | semmle.label | f [a_] | -| complex.cpp:51:18:51:18 | call to a | semmle.label | call to a | -| complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:52:10:52:14 | inner [f, b_] | semmle.label | inner [f, b_] | -| complex.cpp:52:16:52:16 | f [b_] | semmle.label | f [b_] | -| complex.cpp:52:18:52:18 | call to b | semmle.label | call to b | -| complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | semmle.label | b1 [post update] [inner, f, ... (3)] | -| complex.cpp:62:6:62:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | -| complex.cpp:62:12:62:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | -| complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | semmle.label | b2 [post update] [inner, f, ... (3)] | -| complex.cpp:63:6:63:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | -| complex.cpp:63:12:63:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | -| complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | semmle.label | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:64:6:64:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | -| complex.cpp:64:12:64:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | -| complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | semmle.label | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:65:6:65:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | -| complex.cpp:65:12:65:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | -| complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | semmle.label | b1 [inner, f, ... (3)] | -| complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | semmle.label | b2 [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | semmle.label | b3 [inner, f, ... (3)] | +| complex.cpp:40:17:40:17 | b [inner, f, a_] | semmle.label | b [inner, f, a_] | +| complex.cpp:40:17:40:17 | b [inner, f, b_] | semmle.label | b [inner, f, b_] | +| complex.cpp:42:8:42:8 | b [inner, f, a_] | semmle.label | b [inner, f, a_] | +| complex.cpp:42:10:42:14 | inner [f, a_] | semmle.label | inner [f, a_] | +| complex.cpp:42:16:42:16 | f [a_] | semmle.label | f [a_] | +| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | +| complex.cpp:43:8:43:8 | b [inner, f, b_] | semmle.label | b [inner, f, b_] | +| complex.cpp:43:10:43:14 | inner [f, b_] | semmle.label | inner [f, b_] | +| complex.cpp:43:16:43:16 | f [b_] | semmle.label | f [b_] | +| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | +| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | semmle.label | b1 [post update] [inner, f, a_] | +| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | +| complex.cpp:53:12:53:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | +| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | semmle.label | b2 [post update] [inner, f, b_] | +| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | +| complex.cpp:54:12:54:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | +| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | semmle.label | b3 [post update] [inner, f, a_] | +| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | +| complex.cpp:55:12:55:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | +| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | semmle.label | b3 [post update] [inner, f, b_] | +| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | +| complex.cpp:56:12:56:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | +| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:59:7:59:8 | b1 [inner, f, a_] | semmle.label | b1 [inner, f, a_] | +| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | semmle.label | b2 [inner, f, b_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | semmle.label | b3 [inner, f, a_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | semmle.label | b3 [inner, f, b_] | | constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] | | constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] | | constructors.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] | @@ -883,16 +886,16 @@ nodes | qualifiers.cpp:48:10:48:14 | outer [inner, a] | semmle.label | outer [inner, a] | | qualifiers.cpp:48:16:48:20 | inner [a] | semmle.label | inner [a] | | qualifiers.cpp:48:23:48:23 | a | semmle.label | a | -| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, ... (4)] | semmle.label | foo [post update] [bar, baz, ... (4)] | -| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, ... (3)] | semmle.label | access to array [post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | semmle.label | foo [post update] [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | semmle.label | access to array [post update] [baz, userInput, bufferLen] | | realistic.cpp:53:9:53:66 | ... = ... | semmle.label | ... = ... | -| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, ... (3)] | semmle.label | bar [inner post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | semmle.label | bar [inner post update] [baz, userInput, bufferLen] | | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | semmle.label | baz [post update] [userInput, bufferLen] | | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] | | realistic.cpp:53:55:53:64 | call to user_input | semmle.label | call to user_input | -| realistic.cpp:61:21:61:23 | foo [bar, baz, ... (4)] | semmle.label | foo [bar, baz, ... (4)] | -| realistic.cpp:61:21:61:30 | access to array [baz, userInput, ... (3)] | semmle.label | access to array [baz, userInput, ... (3)] | -| realistic.cpp:61:25:61:27 | bar [baz, userInput, ... (3)] | semmle.label | bar [baz, userInput, ... (3)] | +| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | semmle.label | foo [bar, baz, userInput, bufferLen] | +| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | semmle.label | access to array [baz, userInput, bufferLen] | +| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | semmle.label | bar [baz, userInput, bufferLen] | | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | semmle.label | baz [userInput, bufferLen] | | realistic.cpp:61:37:61:45 | userInput [bufferLen] | semmle.label | userInput [bufferLen] | | realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen | @@ -1021,14 +1024,10 @@ nodes | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | | constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index a2be838ae48..1e2970cf2c4 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -40,7 +40,7 @@ bad_asts.cpp: # 16| r16_1(glval) = VariableAddress[s] : # 16| r16_2(glval) = FunctionAddress[MemberFunction] : # 16| r16_3(int) = Constant[1] : -# 16| r16_4(int) = Call : func:r16_2, this:r16_1, 0:r16_3 +# 16| r16_4(int) = Call[MemberFunction] : func:r16_2, this:r16_1, 0:r16_3 # 16| mu16_5(unknown) = ^CallSideEffect : ~m? # 16| v16_6(void) = ^BufferReadSideEffect[-1] : &:r16_1, ~m? # 16| mu16_7(S) = ^IndirectMayWriteSideEffect[-1] : &:r16_1 @@ -2304,7 +2304,7 @@ ir.cpp: # 372| mu372_2(unknown) = AliasedDefinition : # 372| mu372_3(unknown) = InitializeNonLocal : # 373| r373_1(glval) = FunctionAddress[VoidFunc] : -# 373| v373_2(void) = Call : func:r373_1 +# 373| v373_2(void) = Call[VoidFunc] : func:r373_1 # 373| mu373_3(unknown) = ^CallSideEffect : ~m? # 374| v374_1(void) = NoOp : # 372| v372_4(void) = ReturnVoid : @@ -2326,7 +2326,7 @@ ir.cpp: # 377| r377_4(int) = Load : &:r377_3, ~m? # 377| r377_5(glval) = VariableAddress[y] : # 377| r377_6(int) = Load : &:r377_5, ~m? -# 377| r377_7(int) = Call : func:r377_2, 0:r377_4, 1:r377_6 +# 377| r377_7(int) = Call[Add] : func:r377_2, 0:r377_4, 1:r377_6 # 377| mu377_8(unknown) = ^CallSideEffect : ~m? # 377| mu377_9(int) = Store : &:r377_1, r377_7 # 376| r376_8(glval) = VariableAddress[#return] : @@ -2345,14 +2345,14 @@ ir.cpp: # 380| mu380_7(int) = InitializeParameter[y] : &:r380_6 # 381| r381_1(glval) = VariableAddress[#return] : # 381| r381_2(glval) = FunctionAddress[VoidFunc] : -# 381| v381_3(void) = Call : func:r381_2 +# 381| v381_3(void) = Call[VoidFunc] : func:r381_2 # 381| mu381_4(unknown) = ^CallSideEffect : ~m? # 381| r381_5(glval) = FunctionAddress[CallAdd] : # 381| r381_6(glval) = VariableAddress[x] : # 381| r381_7(int) = Load : &:r381_6, ~m? # 381| r381_8(glval) = VariableAddress[y] : # 381| r381_9(int) = Load : &:r381_8, ~m? -# 381| r381_10(int) = Call : func:r381_5, 0:r381_7, 1:r381_9 +# 381| r381_10(int) = Call[CallAdd] : func:r381_5, 0:r381_7, 1:r381_9 # 381| mu381_11(unknown) = ^CallSideEffect : ~m? # 381| r381_12(int) = CopyValue : r381_10 # 381| mu381_13(int) = Store : &:r381_1, r381_12 @@ -2866,7 +2866,7 @@ ir.cpp: # 493| Block 1 # 493| r493_4(glval) = FunctionAddress[VoidFunc] : -# 493| v493_5(void) = Call : func:r493_4 +# 493| v493_5(void) = Call[VoidFunc] : func:r493_4 # 493| mu493_6(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 2 @@ -2878,7 +2878,7 @@ ir.cpp: # 493| Block 3 # 493| r493_7(glval) = FunctionAddress[VoidFunc] : -# 493| v493_8(void) = Call : func:r493_7 +# 493| v493_8(void) = Call[VoidFunc] : func:r493_7 # 493| mu493_9(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 2 @@ -3185,7 +3185,7 @@ ir.cpp: # 552| r552_2(glval<..(*)(..)>) = VariableAddress[pfn] : # 552| r552_3(..(*)(..)) = Load : &:r552_2, ~m? # 552| r552_4(int) = Constant[5] : -# 552| r552_5(int) = Call : func:r552_3, 0:r552_4 +# 552| r552_5(int) = Call[?] : func:r552_3, 0:r552_4 # 552| mu552_6(unknown) = ^CallSideEffect : ~m? # 552| mu552_7(int) = Store : &:r552_1, r552_5 # 551| r551_6(glval) = VariableAddress[#return] : @@ -3306,7 +3306,7 @@ ir.cpp: # 585| r585_4(int) = Constant[1] : # 585| r585_5(glval) = StringConstant["string"] : # 585| r585_6(char *) = Convert : r585_5 -# 585| v585_7(void) = Call : func:r585_1, 0:r585_3, 1:r585_4, 2:r585_6 +# 585| v585_7(void) = Call[VarArgFunction] : func:r585_1, 0:r585_3, 1:r585_4, 2:r585_6 # 585| mu585_8(unknown) = ^CallSideEffect : ~m? # 585| v585_9(void) = ^BufferReadSideEffect[0] : &:r585_3, ~m? # 585| v585_10(void) = ^BufferReadSideEffect[2] : &:r585_6, ~m? @@ -3353,7 +3353,7 @@ ir.cpp: # 616| r616_1(glval) = VariableAddress[s1] : # 616| mu616_2(String) = Uninitialized[s1] : &:r616_1 # 616| r616_3(glval) = FunctionAddress[String] : -# 616| v616_4(void) = Call : func:r616_3, this:r616_1 +# 616| v616_4(void) = Call[String] : func:r616_3, this:r616_1 # 616| mu616_5(unknown) = ^CallSideEffect : ~m? # 616| mu616_6(String) = ^IndirectMayWriteSideEffect[-1] : &:r616_1 # 617| r617_1(glval) = VariableAddress[s2] : @@ -3361,14 +3361,14 @@ ir.cpp: # 617| r617_3(glval) = FunctionAddress[String] : # 617| r617_4(glval) = StringConstant["hello"] : # 617| r617_5(char *) = Convert : r617_4 -# 617| v617_6(void) = Call : func:r617_3, this:r617_1, 0:r617_5 +# 617| v617_6(void) = Call[String] : func:r617_3, this:r617_1, 0:r617_5 # 617| mu617_7(unknown) = ^CallSideEffect : ~m? # 617| mu617_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r617_1 # 617| v617_9(void) = ^BufferReadSideEffect[0] : &:r617_5, ~m? # 617| mu617_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r617_5 # 618| r618_1(glval) = VariableAddress[s3] : # 618| r618_2(glval) = FunctionAddress[ReturnObject] : -# 618| r618_3(String) = Call : func:r618_2 +# 618| r618_3(String) = Call[ReturnObject] : func:r618_2 # 618| mu618_4(unknown) = ^CallSideEffect : ~m? # 618| mu618_5(String) = Store : &:r618_1, r618_3 # 619| r619_1(glval) = VariableAddress[s4] : @@ -3376,7 +3376,7 @@ ir.cpp: # 619| r619_3(glval) = FunctionAddress[String] : # 619| r619_4(glval) = StringConstant["test"] : # 619| r619_5(char *) = Convert : r619_4 -# 619| v619_6(void) = Call : func:r619_3, this:r619_1, 0:r619_5 +# 619| v619_6(void) = Call[String] : func:r619_3, this:r619_1, 0:r619_5 # 619| mu619_7(unknown) = ^CallSideEffect : ~m? # 619| mu619_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r619_1 # 619| v619_9(void) = ^BufferReadSideEffect[0] : &:r619_5, ~m? @@ -3406,7 +3406,7 @@ ir.cpp: # 623| r623_3(glval) = CopyValue : r623_2 # 623| r623_4(glval) = Convert : r623_3 # 623| r623_5(glval) = FunctionAddress[c_str] : -# 623| r623_6(char *) = Call : func:r623_5, this:r623_4 +# 623| r623_6(char *) = Call[c_str] : func:r623_5, this:r623_4 # 623| mu623_7(unknown) = ^CallSideEffect : ~m? # 623| v623_8(void) = ^BufferReadSideEffect[-1] : &:r623_4, ~m? # 623| mu623_9(String) = ^IndirectMayWriteSideEffect[-1] : &:r623_4 @@ -3414,14 +3414,14 @@ ir.cpp: # 624| r624_2(String *) = Load : &:r624_1, ~m? # 624| r624_3(String *) = Convert : r624_2 # 624| r624_4(glval) = FunctionAddress[c_str] : -# 624| r624_5(char *) = Call : func:r624_4, this:r624_3 +# 624| r624_5(char *) = Call[c_str] : func:r624_4, this:r624_3 # 624| mu624_6(unknown) = ^CallSideEffect : ~m? # 624| v624_7(void) = ^BufferReadSideEffect[-1] : &:r624_3, ~m? # 624| mu624_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r624_3 # 625| r625_1(glval) = VariableAddress[s] : # 625| r625_2(glval) = Convert : r625_1 # 625| r625_3(glval) = FunctionAddress[c_str] : -# 625| r625_4(char *) = Call : func:r625_3, this:r625_2 +# 625| r625_4(char *) = Call[c_str] : func:r625_3, this:r625_2 # 625| mu625_5(unknown) = ^CallSideEffect : ~m? # 625| v625_6(void) = ^BufferReadSideEffect[-1] : &:r625_2, ~m? # 625| mu625_7(String) = ^IndirectMayWriteSideEffect[-1] : &:r625_2 @@ -3444,11 +3444,11 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 628| r628_8(glval) = FieldAddress[m_f] : mu628_5 # 628| r628_9(glval) = FunctionAddress[~String] : -# 628| v628_10(void) = Call : func:r628_9, this:r628_8 +# 628| v628_10(void) = Call[~String] : func:r628_9, this:r628_8 # 628| mu628_11(unknown) = ^CallSideEffect : ~m? # 628| r628_12(glval) = FieldAddress[m_b] : mu628_5 # 628| r628_13(glval) = FunctionAddress[~String] : -# 628| v628_14(void) = Call : func:r628_13, this:r628_12 +# 628| v628_14(void) = Call[~String] : func:r628_13, this:r628_12 # 628| mu628_15(unknown) = ^CallSideEffect : ~m? # 628| v628_16(void) = ReturnIndirection[#this] : &:r628_6, ~m? # 628| v628_17(void) = ReturnVoid : @@ -3578,7 +3578,7 @@ ir.cpp: # 653| r653_2(C *) = Load : &:r653_1, ~m? # 653| r653_3(glval) = FunctionAddress[InstanceMemberFunction] : # 653| r653_4(int) = Constant[0] : -# 653| r653_5(int) = Call : func:r653_3, this:r653_2, 0:r653_4 +# 653| r653_5(int) = Call[InstanceMemberFunction] : func:r653_3, this:r653_2, 0:r653_4 # 653| mu653_6(unknown) = ^CallSideEffect : ~m? # 653| v653_7(void) = ^BufferReadSideEffect[-1] : &:r653_2, ~m? # 653| mu653_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r653_2 @@ -3587,7 +3587,7 @@ ir.cpp: # 654| r654_3(glval) = CopyValue : r654_2 # 654| r654_4(glval) = FunctionAddress[InstanceMemberFunction] : # 654| r654_5(int) = Constant[1] : -# 654| r654_6(int) = Call : func:r654_4, this:r654_3, 0:r654_5 +# 654| r654_6(int) = Call[InstanceMemberFunction] : func:r654_4, this:r654_3, 0:r654_5 # 654| mu654_7(unknown) = ^CallSideEffect : ~m? # 654| v654_8(void) = ^BufferReadSideEffect[-1] : &:r654_3, ~m? # 654| mu654_9(C) = ^IndirectMayWriteSideEffect[-1] : &:r654_3 @@ -3595,7 +3595,7 @@ ir.cpp: # 655| r655_2(C *) = Load : &:r655_1, ~m? # 655| r655_3(glval) = FunctionAddress[InstanceMemberFunction] : # 655| r655_4(int) = Constant[2] : -# 655| r655_5(int) = Call : func:r655_3, this:r655_2, 0:r655_4 +# 655| r655_5(int) = Call[InstanceMemberFunction] : func:r655_3, this:r655_2, 0:r655_4 # 655| mu655_6(unknown) = ^CallSideEffect : ~m? # 655| v655_7(void) = ^BufferReadSideEffect[-1] : &:r655_2, ~m? # 655| mu655_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r655_2 @@ -3619,7 +3619,7 @@ ir.cpp: # 659| mu659_3(int) = Store : &:r659_1, r659_2 # 663| r663_1(glval) = FieldAddress[m_b] : mu658_5 # 663| r663_2(glval) = FunctionAddress[String] : -# 663| v663_3(void) = Call : func:r663_2, this:r663_1 +# 663| v663_3(void) = Call[String] : func:r663_2, this:r663_1 # 663| mu663_4(unknown) = ^CallSideEffect : ~m? # 663| mu663_5(String) = ^IndirectMayWriteSideEffect[-1] : &:r663_1 # 660| r660_1(glval) = FieldAddress[m_c] : mu658_5 @@ -3632,7 +3632,7 @@ ir.cpp: # 662| r662_2(glval) = FunctionAddress[String] : # 662| r662_3(glval) = StringConstant["test"] : # 662| r662_4(char *) = Convert : r662_3 -# 662| v662_5(void) = Call : func:r662_2, this:r662_1, 0:r662_4 +# 662| v662_5(void) = Call[String] : func:r662_2, this:r662_1, 0:r662_4 # 662| mu662_6(unknown) = ^CallSideEffect : ~m? # 662| mu662_7(String) = ^IndirectMayWriteSideEffect[-1] : &:r662_1 # 662| v662_8(void) = ^BufferReadSideEffect[0] : &:r662_4, ~m? @@ -3696,7 +3696,7 @@ ir.cpp: # 687| mu687_6(int &) = Store : &:r687_1, r687_5 # 688| r688_1(glval) = VariableAddress[r3] : # 688| r688_2(glval) = FunctionAddress[ReturnReference] : -# 688| r688_3(String &) = Call : func:r688_2 +# 688| r688_3(String &) = Call[ReturnReference] : func:r688_2 # 688| mu688_4(unknown) = ^CallSideEffect : ~m? # 688| r688_5(glval) = CopyValue : r688_3 # 688| r688_6(glval) = Convert : r688_5 @@ -3750,7 +3750,7 @@ ir.cpp: # 700| r700_2(..(&)(..)) = Load : &:r700_1, ~m? # 700| r700_3(..(*)(..)) = CopyValue : r700_2 # 700| r700_4(int) = Constant[5] : -# 700| r700_5(int) = Call : func:r700_3, 0:r700_4 +# 700| r700_5(int) = Call[?] : func:r700_3, 0:r700_4 # 700| mu700_6(unknown) = ^CallSideEffect : ~m? # 701| v701_1(void) = NoOp : # 697| v697_4(void) = ReturnVoid : @@ -3814,7 +3814,7 @@ ir.cpp: # 709| r709_4(int) = Load : &:r709_3, ~m? # 709| r709_5(glval) = VariableAddress[y] : # 709| r709_6(int) = Load : &:r709_5, ~m? -# 709| r709_7(int) = Call : func:r709_2, 0:r709_4, 1:r709_6 +# 709| r709_7(int) = Call[min] : func:r709_2, 0:r709_4, 1:r709_6 # 709| mu709_8(unknown) = ^CallSideEffect : ~m? # 709| mu709_9(int) = Store : &:r709_1, r709_7 # 708| r708_8(glval) = VariableAddress[#return] : @@ -3851,7 +3851,7 @@ ir.cpp: # 721| r721_2(glval) = FunctionAddress[Func] : # 721| r721_3(void *) = Constant[0] : # 721| r721_4(char) = Constant[111] : -# 721| r721_5(long) = Call : func:r721_2, 0:r721_3, 1:r721_4 +# 721| r721_5(long) = Call[Func] : func:r721_2, 0:r721_3, 1:r721_4 # 721| mu721_6(unknown) = ^CallSideEffect : ~m? # 721| v721_7(void) = ^BufferReadSideEffect[0] : &:r721_3, ~m? # 721| mu721_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r721_3 @@ -3926,7 +3926,7 @@ ir.cpp: # 731| r731_13(glval) = FunctionAddress[String] : # 731| r731_14(glval) = StringConstant["String object"] : # 731| r731_15(char *) = Convert : r731_14 -# 731| v731_16(void) = Call : func:r731_13, this:r731_11, 0:r731_15 +# 731| v731_16(void) = Call[String] : func:r731_13, this:r731_11, 0:r731_15 # 731| mu731_17(unknown) = ^CallSideEffect : ~m? # 731| mu731_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r731_11 # 731| v731_19(void) = ^BufferReadSideEffect[0] : &:r731_15, ~m? @@ -3955,7 +3955,7 @@ ir.cpp: # 736| r736_3(glval) = FunctionAddress[String] : # 736| r736_4(glval) = VariableAddress[s] : # 736| r736_5(char *) = Load : &:r736_4, ~m? -# 736| v736_6(void) = Call : func:r736_3, this:r736_1, 0:r736_5 +# 736| v736_6(void) = Call[String] : func:r736_3, this:r736_1, 0:r736_5 # 736| mu736_7(unknown) = ^CallSideEffect : ~m? # 736| mu736_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r736_1 # 736| v736_9(void) = ^BufferReadSideEffect[0] : &:r736_5, ~m? @@ -4009,7 +4009,7 @@ ir.cpp: #-----| r0_5(glval) = CopyValue : r745_14 # 745| r745_15(glval) = FieldAddress[base_s] : r0_5 #-----| r0_6(String &) = CopyValue : r745_15 -# 745| r745_16(String &) = Call : func:r745_12, this:r745_11, 0:r0_6 +# 745| r745_16(String &) = Call[operator=] : func:r745_12, this:r745_11, 0:r0_6 # 745| mu745_17(unknown) = ^CallSideEffect : ~m? # 745| v745_18(void) = ^BufferReadSideEffect[-1] : &:r745_11, ~m? #-----| v0_7(void) = ^BufferReadSideEffect[0] : &:r0_6, ~m? @@ -4044,7 +4044,7 @@ ir.cpp: #-----| mu0_4(unknown) = InitializeIndirection[p#0] : &:r0_3 # 745| r745_8(glval) = FieldAddress[base_s] : mu745_5 # 745| r745_9(glval) = FunctionAddress[String] : -# 745| v745_10(void) = Call : func:r745_9, this:r745_8 +# 745| v745_10(void) = Call[String] : func:r745_9, this:r745_8 # 745| mu745_11(unknown) = ^CallSideEffect : ~m? # 745| mu745_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_8 # 745| v745_13(void) = NoOp : @@ -4065,7 +4065,7 @@ ir.cpp: # 748| mu748_7(Base) = InitializeIndirection[#this] : &:r748_6 # 748| r748_8(glval) = FieldAddress[base_s] : mu748_5 # 748| r748_9(glval) = FunctionAddress[String] : -# 748| v748_10(void) = Call : func:r748_9, this:r748_8 +# 748| v748_10(void) = Call[String] : func:r748_9, this:r748_8 # 748| mu748_11(unknown) = ^CallSideEffect : ~m? # 748| mu748_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r748_8 # 749| v749_1(void) = NoOp : @@ -4086,7 +4086,7 @@ ir.cpp: # 751| v751_1(void) = NoOp : # 751| r751_2(glval) = FieldAddress[base_s] : mu750_5 # 751| r751_3(glval) = FunctionAddress[~String] : -# 751| v751_4(void) = Call : func:r751_3, this:r751_2 +# 751| v751_4(void) = Call[~String] : func:r751_3, this:r751_2 # 751| mu751_5(unknown) = ^CallSideEffect : ~m? # 750| v750_8(void) = ReturnIndirection[#this] : &:r750_6, ~m? # 750| v750_9(void) = ReturnVoid : @@ -4117,7 +4117,7 @@ ir.cpp: #-----| r0_7(Base *) = ConvertToNonVirtualBase[Middle : Base] : r754_13 # 754| r754_14(glval) = CopyValue : r0_7 #-----| r0_8(Base &) = CopyValue : r754_14 -# 754| r754_15(Base &) = Call : func:r754_10, this:r0_5, 0:r0_8 +# 754| r754_15(Base &) = Call[operator=] : func:r754_10, this:r0_5, 0:r0_8 # 754| mu754_16(unknown) = ^CallSideEffect : ~m? #-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? @@ -4134,7 +4134,7 @@ ir.cpp: #-----| r0_14(glval) = CopyValue : r754_23 # 754| r754_24(glval) = FieldAddress[middle_s] : r0_14 #-----| r0_15(String &) = CopyValue : r754_24 -# 754| r754_25(String &) = Call : func:r754_21, this:r754_20, 0:r0_15 +# 754| r754_25(String &) = Call[operator=] : func:r754_21, this:r754_20, 0:r0_15 # 754| mu754_26(unknown) = ^CallSideEffect : ~m? # 754| v754_27(void) = ^BufferReadSideEffect[-1] : &:r754_20, ~m? #-----| v0_16(void) = ^BufferReadSideEffect[0] : &:r0_15, ~m? @@ -4165,12 +4165,12 @@ ir.cpp: # 757| mu757_7(Middle) = InitializeIndirection[#this] : &:r757_6 # 757| r757_8(glval) = ConvertToNonVirtualBase[Middle : Base] : mu757_5 # 757| r757_9(glval) = FunctionAddress[Base] : -# 757| v757_10(void) = Call : func:r757_9, this:r757_8 +# 757| v757_10(void) = Call[Base] : func:r757_9, this:r757_8 # 757| mu757_11(unknown) = ^CallSideEffect : ~m? # 757| mu757_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r757_8 # 757| r757_13(glval) = FieldAddress[middle_s] : mu757_5 # 757| r757_14(glval) = FunctionAddress[String] : -# 757| v757_15(void) = Call : func:r757_14, this:r757_13 +# 757| v757_15(void) = Call[String] : func:r757_14, this:r757_13 # 757| mu757_16(unknown) = ^CallSideEffect : ~m? # 757| mu757_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r757_13 # 758| v758_1(void) = NoOp : @@ -4191,11 +4191,11 @@ ir.cpp: # 760| v760_1(void) = NoOp : # 760| r760_2(glval) = FieldAddress[middle_s] : mu759_5 # 760| r760_3(glval) = FunctionAddress[~String] : -# 760| v760_4(void) = Call : func:r760_3, this:r760_2 +# 760| v760_4(void) = Call[~String] : func:r760_3, this:r760_2 # 760| mu760_5(unknown) = ^CallSideEffect : ~m? # 760| r760_6(glval) = ConvertToNonVirtualBase[Middle : Base] : mu759_5 # 760| r760_7(glval) = FunctionAddress[~Base] : -# 760| v760_8(void) = Call : func:r760_7, this:r760_6 +# 760| v760_8(void) = Call[~Base] : func:r760_7, this:r760_6 # 760| mu760_9(unknown) = ^CallSideEffect : ~m? # 759| v759_8(void) = ReturnIndirection[#this] : &:r759_6, ~m? # 759| v759_9(void) = ReturnVoid : @@ -4226,7 +4226,7 @@ ir.cpp: #-----| r0_7(Middle *) = ConvertToNonVirtualBase[Derived : Middle] : r763_13 # 763| r763_14(glval) = CopyValue : r0_7 #-----| r0_8(Middle &) = CopyValue : r763_14 -# 763| r763_15(Middle &) = Call : func:r763_10, this:r0_5, 0:r0_8 +# 763| r763_15(Middle &) = Call[operator=] : func:r763_10, this:r0_5, 0:r0_8 # 763| mu763_16(unknown) = ^CallSideEffect : ~m? #-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? @@ -4243,7 +4243,7 @@ ir.cpp: #-----| r0_14(glval) = CopyValue : r763_23 # 763| r763_24(glval) = FieldAddress[derived_s] : r0_14 #-----| r0_15(String &) = CopyValue : r763_24 -# 763| r763_25(String &) = Call : func:r763_21, this:r763_20, 0:r0_15 +# 763| r763_25(String &) = Call[operator=] : func:r763_21, this:r763_20, 0:r0_15 # 763| mu763_26(unknown) = ^CallSideEffect : ~m? # 763| v763_27(void) = ^BufferReadSideEffect[-1] : &:r763_20, ~m? #-----| v0_16(void) = ^BufferReadSideEffect[0] : &:r0_15, ~m? @@ -4274,12 +4274,12 @@ ir.cpp: # 766| mu766_7(Derived) = InitializeIndirection[#this] : &:r766_6 # 766| r766_8(glval) = ConvertToNonVirtualBase[Derived : Middle] : mu766_5 # 766| r766_9(glval) = FunctionAddress[Middle] : -# 766| v766_10(void) = Call : func:r766_9, this:r766_8 +# 766| v766_10(void) = Call[Middle] : func:r766_9, this:r766_8 # 766| mu766_11(unknown) = ^CallSideEffect : ~m? # 766| mu766_12(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r766_8 # 766| r766_13(glval) = FieldAddress[derived_s] : mu766_5 # 766| r766_14(glval) = FunctionAddress[String] : -# 766| v766_15(void) = Call : func:r766_14, this:r766_13 +# 766| v766_15(void) = Call[String] : func:r766_14, this:r766_13 # 766| mu766_16(unknown) = ^CallSideEffect : ~m? # 766| mu766_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r766_13 # 767| v767_1(void) = NoOp : @@ -4300,11 +4300,11 @@ ir.cpp: # 769| v769_1(void) = NoOp : # 769| r769_2(glval) = FieldAddress[derived_s] : mu768_5 # 769| r769_3(glval) = FunctionAddress[~String] : -# 769| v769_4(void) = Call : func:r769_3, this:r769_2 +# 769| v769_4(void) = Call[~String] : func:r769_3, this:r769_2 # 769| mu769_5(unknown) = ^CallSideEffect : ~m? # 769| r769_6(glval) = ConvertToNonVirtualBase[Derived : Middle] : mu768_5 # 769| r769_7(glval) = FunctionAddress[~Middle] : -# 769| v769_8(void) = Call : func:r769_7, this:r769_6 +# 769| v769_8(void) = Call[~Middle] : func:r769_7, this:r769_6 # 769| mu769_9(unknown) = ^CallSideEffect : ~m? # 768| v768_8(void) = ReturnIndirection[#this] : &:r768_6, ~m? # 768| v768_9(void) = ReturnVoid : @@ -4322,12 +4322,12 @@ ir.cpp: # 775| mu775_7(MiddleVB1) = InitializeIndirection[#this] : &:r775_6 # 775| r775_8(glval) = ConvertToNonVirtualBase[MiddleVB1 : Base] : mu775_5 # 775| r775_9(glval) = FunctionAddress[Base] : -# 775| v775_10(void) = Call : func:r775_9, this:r775_8 +# 775| v775_10(void) = Call[Base] : func:r775_9, this:r775_8 # 775| mu775_11(unknown) = ^CallSideEffect : ~m? # 775| mu775_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r775_8 # 775| r775_13(glval) = FieldAddress[middlevb1_s] : mu775_5 # 775| r775_14(glval) = FunctionAddress[String] : -# 775| v775_15(void) = Call : func:r775_14, this:r775_13 +# 775| v775_15(void) = Call[String] : func:r775_14, this:r775_13 # 775| mu775_16(unknown) = ^CallSideEffect : ~m? # 775| mu775_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r775_13 # 776| v776_1(void) = NoOp : @@ -4348,11 +4348,11 @@ ir.cpp: # 778| v778_1(void) = NoOp : # 778| r778_2(glval) = FieldAddress[middlevb1_s] : mu777_5 # 778| r778_3(glval) = FunctionAddress[~String] : -# 778| v778_4(void) = Call : func:r778_3, this:r778_2 +# 778| v778_4(void) = Call[~String] : func:r778_3, this:r778_2 # 778| mu778_5(unknown) = ^CallSideEffect : ~m? # 778| r778_6(glval) = ConvertToNonVirtualBase[MiddleVB1 : Base] : mu777_5 # 778| r778_7(glval) = FunctionAddress[~Base] : -# 778| v778_8(void) = Call : func:r778_7, this:r778_6 +# 778| v778_8(void) = Call[~Base] : func:r778_7, this:r778_6 # 778| mu778_9(unknown) = ^CallSideEffect : ~m? # 777| v777_8(void) = ReturnIndirection[#this] : &:r777_6, ~m? # 777| v777_9(void) = ReturnVoid : @@ -4370,12 +4370,12 @@ ir.cpp: # 784| mu784_7(MiddleVB2) = InitializeIndirection[#this] : &:r784_6 # 784| r784_8(glval) = ConvertToNonVirtualBase[MiddleVB2 : Base] : mu784_5 # 784| r784_9(glval) = FunctionAddress[Base] : -# 784| v784_10(void) = Call : func:r784_9, this:r784_8 +# 784| v784_10(void) = Call[Base] : func:r784_9, this:r784_8 # 784| mu784_11(unknown) = ^CallSideEffect : ~m? # 784| mu784_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r784_8 # 784| r784_13(glval) = FieldAddress[middlevb2_s] : mu784_5 # 784| r784_14(glval) = FunctionAddress[String] : -# 784| v784_15(void) = Call : func:r784_14, this:r784_13 +# 784| v784_15(void) = Call[String] : func:r784_14, this:r784_13 # 784| mu784_16(unknown) = ^CallSideEffect : ~m? # 784| mu784_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r784_13 # 785| v785_1(void) = NoOp : @@ -4396,11 +4396,11 @@ ir.cpp: # 787| v787_1(void) = NoOp : # 787| r787_2(glval) = FieldAddress[middlevb2_s] : mu786_5 # 787| r787_3(glval) = FunctionAddress[~String] : -# 787| v787_4(void) = Call : func:r787_3, this:r787_2 +# 787| v787_4(void) = Call[~String] : func:r787_3, this:r787_2 # 787| mu787_5(unknown) = ^CallSideEffect : ~m? # 787| r787_6(glval) = ConvertToNonVirtualBase[MiddleVB2 : Base] : mu786_5 # 787| r787_7(glval) = FunctionAddress[~Base] : -# 787| v787_8(void) = Call : func:r787_7, this:r787_6 +# 787| v787_8(void) = Call[~Base] : func:r787_7, this:r787_6 # 787| mu787_9(unknown) = ^CallSideEffect : ~m? # 786| v786_8(void) = ReturnIndirection[#this] : &:r786_6, ~m? # 786| v786_9(void) = ReturnVoid : @@ -4418,22 +4418,22 @@ ir.cpp: # 793| mu793_7(DerivedVB) = InitializeIndirection[#this] : &:r793_6 # 793| r793_8(glval) = ConvertToNonVirtualBase[DerivedVB : Base] : mu793_5 # 793| r793_9(glval) = FunctionAddress[Base] : -# 793| v793_10(void) = Call : func:r793_9, this:r793_8 +# 793| v793_10(void) = Call[Base] : func:r793_9, this:r793_8 # 793| mu793_11(unknown) = ^CallSideEffect : ~m? # 793| mu793_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r793_8 # 793| r793_13(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB1] : mu793_5 # 793| r793_14(glval) = FunctionAddress[MiddleVB1] : -# 793| v793_15(void) = Call : func:r793_14, this:r793_13 +# 793| v793_15(void) = Call[MiddleVB1] : func:r793_14, this:r793_13 # 793| mu793_16(unknown) = ^CallSideEffect : ~m? # 793| mu793_17(MiddleVB1) = ^IndirectMayWriteSideEffect[-1] : &:r793_13 # 793| r793_18(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB2] : mu793_5 # 793| r793_19(glval) = FunctionAddress[MiddleVB2] : -# 793| v793_20(void) = Call : func:r793_19, this:r793_18 +# 793| v793_20(void) = Call[MiddleVB2] : func:r793_19, this:r793_18 # 793| mu793_21(unknown) = ^CallSideEffect : ~m? # 793| mu793_22(MiddleVB2) = ^IndirectMayWriteSideEffect[-1] : &:r793_18 # 793| r793_23(glval) = FieldAddress[derivedvb_s] : mu793_5 # 793| r793_24(glval) = FunctionAddress[String] : -# 793| v793_25(void) = Call : func:r793_24, this:r793_23 +# 793| v793_25(void) = Call[String] : func:r793_24, this:r793_23 # 793| mu793_26(unknown) = ^CallSideEffect : ~m? # 793| mu793_27(String) = ^IndirectMayWriteSideEffect[-1] : &:r793_23 # 794| v794_1(void) = NoOp : @@ -4454,19 +4454,19 @@ ir.cpp: # 796| v796_1(void) = NoOp : # 796| r796_2(glval) = FieldAddress[derivedvb_s] : mu795_5 # 796| r796_3(glval) = FunctionAddress[~String] : -# 796| v796_4(void) = Call : func:r796_3, this:r796_2 +# 796| v796_4(void) = Call[~String] : func:r796_3, this:r796_2 # 796| mu796_5(unknown) = ^CallSideEffect : ~m? # 796| r796_6(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB2] : mu795_5 # 796| r796_7(glval) = FunctionAddress[~MiddleVB2] : -# 796| v796_8(void) = Call : func:r796_7, this:r796_6 +# 796| v796_8(void) = Call[~MiddleVB2] : func:r796_7, this:r796_6 # 796| mu796_9(unknown) = ^CallSideEffect : ~m? # 796| r796_10(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB1] : mu795_5 # 796| r796_11(glval) = FunctionAddress[~MiddleVB1] : -# 796| v796_12(void) = Call : func:r796_11, this:r796_10 +# 796| v796_12(void) = Call[~MiddleVB1] : func:r796_11, this:r796_10 # 796| mu796_13(unknown) = ^CallSideEffect : ~m? # 796| r796_14(glval) = ConvertToNonVirtualBase[DerivedVB : Base] : mu795_5 # 796| r796_15(glval) = FunctionAddress[~Base] : -# 796| v796_16(void) = Call : func:r796_15, this:r796_14 +# 796| v796_16(void) = Call[~Base] : func:r796_15, this:r796_14 # 796| mu796_17(unknown) = ^CallSideEffect : ~m? # 795| v795_8(void) = ReturnIndirection[#this] : &:r795_6, ~m? # 795| v795_9(void) = ReturnVoid : @@ -4481,19 +4481,19 @@ ir.cpp: # 800| r800_1(glval) = VariableAddress[b] : # 800| mu800_2(Base) = Uninitialized[b] : &:r800_1 # 800| r800_3(glval) = FunctionAddress[Base] : -# 800| v800_4(void) = Call : func:r800_3, this:r800_1 +# 800| v800_4(void) = Call[Base] : func:r800_3, this:r800_1 # 800| mu800_5(unknown) = ^CallSideEffect : ~m? # 800| mu800_6(Base) = ^IndirectMayWriteSideEffect[-1] : &:r800_1 # 801| r801_1(glval) = VariableAddress[m] : # 801| mu801_2(Middle) = Uninitialized[m] : &:r801_1 # 801| r801_3(glval) = FunctionAddress[Middle] : -# 801| v801_4(void) = Call : func:r801_3, this:r801_1 +# 801| v801_4(void) = Call[Middle] : func:r801_3, this:r801_1 # 801| mu801_5(unknown) = ^CallSideEffect : ~m? # 801| mu801_6(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r801_1 # 802| r802_1(glval) = VariableAddress[d] : # 802| mu802_2(Derived) = Uninitialized[d] : &:r802_1 # 802| r802_3(glval) = FunctionAddress[Derived] : -# 802| v802_4(void) = Call : func:r802_3, this:r802_1 +# 802| v802_4(void) = Call[Derived] : func:r802_3, this:r802_1 # 802| mu802_5(unknown) = ^CallSideEffect : ~m? # 802| mu802_6(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r802_1 # 804| r804_1(glval) = VariableAddress[pb] : @@ -4513,7 +4513,7 @@ ir.cpp: # 808| r808_3(glval) = VariableAddress[m] : # 808| r808_4(glval) = ConvertToNonVirtualBase[Middle : Base] : r808_3 # 808| r808_5(Base &) = CopyValue : r808_4 -# 808| r808_6(Base &) = Call : func:r808_2, this:r808_1, 0:r808_5 +# 808| r808_6(Base &) = Call[operator=] : func:r808_2, this:r808_1, 0:r808_5 # 808| mu808_7(unknown) = ^CallSideEffect : ~m? # 808| v808_8(void) = ^BufferReadSideEffect[-1] : &:r808_1, ~m? # 808| v808_9(void) = ^BufferReadSideEffect[0] : &:r808_5, ~m? @@ -4526,14 +4526,14 @@ ir.cpp: # 809| r809_4(glval) = VariableAddress[m] : # 809| r809_5(glval) = ConvertToNonVirtualBase[Middle : Base] : r809_4 # 809| r809_6(Base &) = CopyValue : r809_5 -# 809| v809_7(void) = Call : func:r809_3, 0:r809_6 +# 809| v809_7(void) = Call[Base] : func:r809_3, 0:r809_6 # 809| mu809_8(unknown) = ^CallSideEffect : ~m? # 809| mu809_9(Base) = ^IndirectMayWriteSideEffect[-1] : # 809| v809_10(void) = ^BufferReadSideEffect[0] : &:r809_6, ~m? # 809| mu809_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r809_6 # 809| r809_12(glval) = Convert : v809_7 # 809| r809_13(Base &) = CopyValue : r809_12 -# 809| r809_14(Base &) = Call : func:r809_2, this:r809_1, 0:r809_13 +# 809| r809_14(Base &) = Call[operator=] : func:r809_2, this:r809_1, 0:r809_13 # 809| mu809_15(unknown) = ^CallSideEffect : ~m? # 809| v809_16(void) = ^BufferReadSideEffect[-1] : &:r809_1, ~m? # 809| v809_17(void) = ^BufferReadSideEffect[0] : &:r809_13, ~m? @@ -4546,14 +4546,14 @@ ir.cpp: # 810| r810_4(glval) = VariableAddress[m] : # 810| r810_5(glval) = ConvertToNonVirtualBase[Middle : Base] : r810_4 # 810| r810_6(Base &) = CopyValue : r810_5 -# 810| v810_7(void) = Call : func:r810_3, 0:r810_6 +# 810| v810_7(void) = Call[Base] : func:r810_3, 0:r810_6 # 810| mu810_8(unknown) = ^CallSideEffect : ~m? # 810| mu810_9(Base) = ^IndirectMayWriteSideEffect[-1] : # 810| v810_10(void) = ^BufferReadSideEffect[0] : &:r810_6, ~m? # 810| mu810_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r810_6 # 810| r810_12(glval) = Convert : v810_7 # 810| r810_13(Base &) = CopyValue : r810_12 -# 810| r810_14(Base &) = Call : func:r810_2, this:r810_1, 0:r810_13 +# 810| r810_14(Base &) = Call[operator=] : func:r810_2, this:r810_1, 0:r810_13 # 810| mu810_15(unknown) = ^CallSideEffect : ~m? # 810| v810_16(void) = ^BufferReadSideEffect[-1] : &:r810_1, ~m? # 810| v810_17(void) = ^BufferReadSideEffect[0] : &:r810_13, ~m? @@ -4586,7 +4586,7 @@ ir.cpp: # 816| r816_4(glval) = ConvertToDerived[Middle : Base] : r816_3 # 816| r816_5(glval) = Convert : r816_4 # 816| r816_6(Middle &) = CopyValue : r816_5 -# 816| r816_7(Middle &) = Call : func:r816_2, this:r816_1, 0:r816_6 +# 816| r816_7(Middle &) = Call[operator=] : func:r816_2, this:r816_1, 0:r816_6 # 816| mu816_8(unknown) = ^CallSideEffect : ~m? # 816| v816_9(void) = ^BufferReadSideEffect[-1] : &:r816_1, ~m? # 816| v816_10(void) = ^BufferReadSideEffect[0] : &:r816_6, ~m? @@ -4599,7 +4599,7 @@ ir.cpp: # 817| r817_4(glval) = ConvertToDerived[Middle : Base] : r817_3 # 817| r817_5(glval) = Convert : r817_4 # 817| r817_6(Middle &) = CopyValue : r817_5 -# 817| r817_7(Middle &) = Call : func:r817_2, this:r817_1, 0:r817_6 +# 817| r817_7(Middle &) = Call[operator=] : func:r817_2, this:r817_1, 0:r817_6 # 817| mu817_8(unknown) = ^CallSideEffect : ~m? # 817| v817_9(void) = ^BufferReadSideEffect[-1] : &:r817_1, ~m? # 817| v817_10(void) = ^BufferReadSideEffect[0] : &:r817_6, ~m? @@ -4627,7 +4627,7 @@ ir.cpp: # 822| r822_4(glval) = ConvertToNonVirtualBase[Derived : Middle] : r822_3 # 822| r822_5(glval) = ConvertToNonVirtualBase[Middle : Base] : r822_4 # 822| r822_6(Base &) = CopyValue : r822_5 -# 822| r822_7(Base &) = Call : func:r822_2, this:r822_1, 0:r822_6 +# 822| r822_7(Base &) = Call[operator=] : func:r822_2, this:r822_1, 0:r822_6 # 822| mu822_8(unknown) = ^CallSideEffect : ~m? # 822| v822_9(void) = ^BufferReadSideEffect[-1] : &:r822_1, ~m? # 822| v822_10(void) = ^BufferReadSideEffect[0] : &:r822_6, ~m? @@ -4641,14 +4641,14 @@ ir.cpp: # 823| r823_5(glval) = ConvertToNonVirtualBase[Derived : Middle] : r823_4 # 823| r823_6(glval) = ConvertToNonVirtualBase[Middle : Base] : r823_5 # 823| r823_7(Base &) = CopyValue : r823_6 -# 823| v823_8(void) = Call : func:r823_3, 0:r823_7 +# 823| v823_8(void) = Call[Base] : func:r823_3, 0:r823_7 # 823| mu823_9(unknown) = ^CallSideEffect : ~m? # 823| mu823_10(Base) = ^IndirectMayWriteSideEffect[-1] : # 823| v823_11(void) = ^BufferReadSideEffect[0] : &:r823_7, ~m? # 823| mu823_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r823_7 # 823| r823_13(glval) = Convert : v823_8 # 823| r823_14(Base &) = CopyValue : r823_13 -# 823| r823_15(Base &) = Call : func:r823_2, this:r823_1, 0:r823_14 +# 823| r823_15(Base &) = Call[operator=] : func:r823_2, this:r823_1, 0:r823_14 # 823| mu823_16(unknown) = ^CallSideEffect : ~m? # 823| v823_17(void) = ^BufferReadSideEffect[-1] : &:r823_1, ~m? # 823| v823_18(void) = ^BufferReadSideEffect[0] : &:r823_14, ~m? @@ -4662,14 +4662,14 @@ ir.cpp: # 824| r824_5(glval) = ConvertToNonVirtualBase[Derived : Middle] : r824_4 # 824| r824_6(glval) = ConvertToNonVirtualBase[Middle : Base] : r824_5 # 824| r824_7(Base &) = CopyValue : r824_6 -# 824| v824_8(void) = Call : func:r824_3, 0:r824_7 +# 824| v824_8(void) = Call[Base] : func:r824_3, 0:r824_7 # 824| mu824_9(unknown) = ^CallSideEffect : ~m? # 824| mu824_10(Base) = ^IndirectMayWriteSideEffect[-1] : # 824| v824_11(void) = ^BufferReadSideEffect[0] : &:r824_7, ~m? # 824| mu824_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r824_7 # 824| r824_13(glval) = Convert : v824_8 # 824| r824_14(Base &) = CopyValue : r824_13 -# 824| r824_15(Base &) = Call : func:r824_2, this:r824_1, 0:r824_14 +# 824| r824_15(Base &) = Call[operator=] : func:r824_2, this:r824_1, 0:r824_14 # 824| mu824_16(unknown) = ^CallSideEffect : ~m? # 824| v824_17(void) = ^BufferReadSideEffect[-1] : &:r824_1, ~m? # 824| v824_18(void) = ^BufferReadSideEffect[0] : &:r824_14, ~m? @@ -4706,7 +4706,7 @@ ir.cpp: # 830| r830_5(glval) = ConvertToDerived[Derived : Middle] : r830_4 # 830| r830_6(glval) = Convert : r830_5 # 830| r830_7(Derived &) = CopyValue : r830_6 -# 830| r830_8(Derived &) = Call : func:r830_2, this:r830_1, 0:r830_7 +# 830| r830_8(Derived &) = Call[operator=] : func:r830_2, this:r830_1, 0:r830_7 # 830| mu830_9(unknown) = ^CallSideEffect : ~m? # 830| v830_10(void) = ^BufferReadSideEffect[-1] : &:r830_1, ~m? # 830| v830_11(void) = ^BufferReadSideEffect[0] : &:r830_7, ~m? @@ -4720,7 +4720,7 @@ ir.cpp: # 831| r831_5(glval) = ConvertToDerived[Derived : Middle] : r831_4 # 831| r831_6(glval) = Convert : r831_5 # 831| r831_7(Derived &) = CopyValue : r831_6 -# 831| r831_8(Derived &) = Call : func:r831_2, this:r831_1, 0:r831_7 +# 831| r831_8(Derived &) = Call[operator=] : func:r831_2, this:r831_1, 0:r831_7 # 831| mu831_9(unknown) = ^CallSideEffect : ~m? # 831| v831_10(void) = ^BufferReadSideEffect[-1] : &:r831_1, ~m? # 831| v831_11(void) = ^BufferReadSideEffect[0] : &:r831_7, ~m? @@ -4791,7 +4791,7 @@ ir.cpp: # 846| mu846_7(PolymorphicDerived) = InitializeIndirection[#this] : &:r846_6 # 846| r846_8(glval) = ConvertToNonVirtualBase[PolymorphicDerived : PolymorphicBase] : mu846_5 # 846| r846_9(glval) = FunctionAddress[PolymorphicBase] : -# 846| v846_10(void) = Call : func:r846_9, this:r846_8 +# 846| v846_10(void) = Call[PolymorphicBase] : func:r846_9, this:r846_8 # 846| mu846_11(unknown) = ^CallSideEffect : ~m? # 846| mu846_12(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r846_8 # 846| v846_13(void) = NoOp : @@ -4812,7 +4812,7 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 846| r846_8(glval) = ConvertToNonVirtualBase[PolymorphicDerived : PolymorphicBase] : mu846_5 # 846| r846_9(glval) = FunctionAddress[~PolymorphicBase] : -# 846| v846_10(void) = Call : func:r846_9, this:r846_8 +# 846| v846_10(void) = Call[~PolymorphicBase] : func:r846_9, this:r846_8 # 846| mu846_11(unknown) = ^CallSideEffect : ~m? # 846| v846_12(void) = ReturnIndirection[#this] : &:r846_6, ~m? # 846| v846_13(void) = ReturnVoid : @@ -4827,13 +4827,13 @@ ir.cpp: # 850| r850_1(glval) = VariableAddress[b] : # 850| mu850_2(PolymorphicBase) = Uninitialized[b] : &:r850_1 #-----| r0_1(glval) = FunctionAddress[PolymorphicBase] : -#-----| v0_2(void) = Call : func:r0_1, this:r850_1 +#-----| v0_2(void) = Call[PolymorphicBase] : func:r0_1, this:r850_1 #-----| mu0_3(unknown) = ^CallSideEffect : ~m? #-----| mu0_4(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r850_1 # 851| r851_1(glval) = VariableAddress[d] : # 851| mu851_2(PolymorphicDerived) = Uninitialized[d] : &:r851_1 #-----| r0_5(glval) = FunctionAddress[PolymorphicDerived] : -#-----| v0_6(void) = Call : func:r0_5, this:r851_1 +#-----| v0_6(void) = Call[PolymorphicDerived] : func:r0_5, this:r851_1 #-----| mu0_7(unknown) = ^CallSideEffect : ~m? #-----| mu0_8(PolymorphicDerived) = ^IndirectMayWriteSideEffect[-1] : &:r851_1 # 853| r853_1(glval) = VariableAddress[pb] : @@ -4891,7 +4891,7 @@ ir.cpp: # 868| r868_1(glval) = FunctionAddress[String] : # 868| r868_2(glval) = StringConstant[""] : # 868| r868_3(char *) = Convert : r868_2 -# 868| v868_4(void) = Call : func:r868_1, this:mu867_5, 0:r868_3 +# 868| v868_4(void) = Call[String] : func:r868_1, this:mu867_5, 0:r868_3 # 868| mu868_5(unknown) = ^CallSideEffect : ~m? # 868| mu868_6(String) = ^IndirectMayWriteSideEffect[-1] : &:mu867_5 # 868| v868_7(void) = ^BufferReadSideEffect[0] : &:r868_3, ~m? @@ -5082,7 +5082,7 @@ ir.cpp: # 905| r905_3(int) = Load : &:r905_2, ~m? # 905| r905_4(glval<__va_list_tag[1]>) = VariableAddress[args2] : # 905| r905_5(__va_list_tag *) = Convert : r905_4 -# 905| v905_6(void) = Call : func:r905_1, 0:r905_3, 1:r905_5 +# 905| v905_6(void) = Call[VAListUsage] : func:r905_1, 0:r905_3, 1:r905_5 # 905| mu905_7(unknown) = ^CallSideEffect : ~m? # 905| v905_8(void) = ^BufferReadSideEffect[1] : &:r905_5, ~m? # 905| mu905_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r905_5 @@ -5154,20 +5154,20 @@ ir.cpp: # 949| mu949_3(unknown) = InitializeNonLocal : # 950| r950_1(glval) = FunctionAddress[operator new] : # 950| r950_2(unsigned long) = Constant[4] : -# 950| r950_3(void *) = Call : func:r950_1, 0:r950_2 +# 950| r950_3(void *) = Call[operator new] : func:r950_1, 0:r950_2 # 950| mu950_4(unknown) = ^CallSideEffect : ~m? # 950| mu950_5(unknown) = ^InitializeDynamicAllocation : &:r950_3 # 950| r950_6(int *) = Convert : r950_3 # 951| r951_1(glval) = FunctionAddress[operator new] : # 951| r951_2(unsigned long) = Constant[4] : # 951| r951_3(float) = Constant[1.0] : -# 951| r951_4(void *) = Call : func:r951_1, 0:r951_2, 1:r951_3 +# 951| r951_4(void *) = Call[operator new] : func:r951_1, 0:r951_2, 1:r951_3 # 951| mu951_5(unknown) = ^CallSideEffect : ~m? # 951| mu951_6(unknown) = ^InitializeDynamicAllocation : &:r951_4 # 951| r951_7(int *) = Convert : r951_4 # 952| r952_1(glval) = FunctionAddress[operator new] : # 952| r952_2(unsigned long) = Constant[4] : -# 952| r952_3(void *) = Call : func:r952_1, 0:r952_2 +# 952| r952_3(void *) = Call[operator new] : func:r952_1, 0:r952_2 # 952| mu952_4(unknown) = ^CallSideEffect : ~m? # 952| mu952_5(unknown) = ^InitializeDynamicAllocation : &:r952_3 # 952| r952_6(int *) = Convert : r952_3 @@ -5175,25 +5175,25 @@ ir.cpp: # 952| mu952_8(int) = Store : &:r952_6, r952_7 # 953| r953_1(glval) = FunctionAddress[operator new] : # 953| r953_2(unsigned long) = Constant[8] : -# 953| r953_3(void *) = Call : func:r953_1, 0:r953_2 +# 953| r953_3(void *) = Call[operator new] : func:r953_1, 0:r953_2 # 953| mu953_4(unknown) = ^CallSideEffect : ~m? # 953| mu953_5(unknown) = ^InitializeDynamicAllocation : &:r953_3 # 953| r953_6(String *) = Convert : r953_3 # 953| r953_7(glval) = FunctionAddress[String] : -# 953| v953_8(void) = Call : func:r953_7, this:r953_6 +# 953| v953_8(void) = Call[String] : func:r953_7, this:r953_6 # 953| mu953_9(unknown) = ^CallSideEffect : ~m? # 953| mu953_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r953_6 # 954| r954_1(glval) = FunctionAddress[operator new] : # 954| r954_2(unsigned long) = Constant[8] : # 954| r954_3(float) = Constant[1.0] : -# 954| r954_4(void *) = Call : func:r954_1, 0:r954_2, 1:r954_3 +# 954| r954_4(void *) = Call[operator new] : func:r954_1, 0:r954_2, 1:r954_3 # 954| mu954_5(unknown) = ^CallSideEffect : ~m? # 954| mu954_6(unknown) = ^InitializeDynamicAllocation : &:r954_4 # 954| r954_7(String *) = Convert : r954_4 # 954| r954_8(glval) = FunctionAddress[String] : # 954| r954_9(glval) = StringConstant["hello"] : # 954| r954_10(char *) = Convert : r954_9 -# 954| v954_11(void) = Call : func:r954_8, this:r954_7, 0:r954_10 +# 954| v954_11(void) = Call[String] : func:r954_8, this:r954_7, 0:r954_10 # 954| mu954_12(unknown) = ^CallSideEffect : ~m? # 954| mu954_13(String) = ^IndirectMayWriteSideEffect[-1] : &:r954_7 # 954| v954_14(void) = ^BufferReadSideEffect[0] : &:r954_10, ~m? @@ -5201,7 +5201,7 @@ ir.cpp: # 955| r955_1(glval) = FunctionAddress[operator new] : # 955| r955_2(unsigned long) = Constant[256] : # 955| r955_3(align_val_t) = Constant[128] : -# 955| r955_4(void *) = Call : func:r955_1, 0:r955_2, 1:r955_3 +# 955| r955_4(void *) = Call[operator new] : func:r955_1, 0:r955_2, 1:r955_3 # 955| mu955_5(unknown) = ^CallSideEffect : ~m? # 955| mu955_6(unknown) = ^InitializeDynamicAllocation : &:r955_4 # 955| r955_7(Overaligned *) = Convert : r955_4 @@ -5209,7 +5209,7 @@ ir.cpp: # 956| r956_2(unsigned long) = Constant[256] : # 956| r956_3(align_val_t) = Constant[128] : # 956| r956_4(float) = Constant[1.0] : -# 956| r956_5(void *) = Call : func:r956_1, 0:r956_2, 1:r956_3, 2:r956_4 +# 956| r956_5(void *) = Call[operator new] : func:r956_1, 0:r956_2, 1:r956_3, 2:r956_4 # 956| mu956_6(unknown) = ^CallSideEffect : ~m? # 956| mu956_7(unknown) = ^InitializeDynamicAllocation : &:r956_5 # 956| r956_8(Overaligned *) = Convert : r956_5 @@ -5229,7 +5229,7 @@ ir.cpp: # 959| mu959_5(int) = InitializeParameter[n] : &:r959_4 # 960| r960_1(glval) = FunctionAddress[operator new[]] : # 960| r960_2(unsigned long) = Constant[40] : -# 960| r960_3(void *) = Call : func:r960_1, 0:r960_2 +# 960| r960_3(void *) = Call[operator new[]] : func:r960_1, 0:r960_2 # 960| mu960_4(unknown) = ^CallSideEffect : ~m? # 960| mu960_5(unknown) = ^InitializeDynamicAllocation : &:r960_3 # 960| r960_6(int *) = Convert : r960_3 @@ -5239,7 +5239,7 @@ ir.cpp: # 961| r961_4(unsigned long) = Convert : r961_3 # 961| r961_5(unsigned long) = Constant[4] : # 961| r961_6(unsigned long) = Mul : r961_4, r961_5 -# 961| r961_7(void *) = Call : func:r961_1, 0:r961_6 +# 961| r961_7(void *) = Call[operator new[]] : func:r961_1, 0:r961_6 # 961| mu961_8(unknown) = ^CallSideEffect : ~m? # 961| mu961_9(unknown) = ^InitializeDynamicAllocation : &:r961_7 # 961| r961_10(int *) = Convert : r961_7 @@ -5250,7 +5250,7 @@ ir.cpp: # 962| r962_5(unsigned long) = Constant[4] : # 962| r962_6(unsigned long) = Mul : r962_4, r962_5 # 962| r962_7(float) = Constant[1.0] : -# 962| r962_8(void *) = Call : func:r962_1, 0:r962_6, 1:r962_7 +# 962| r962_8(void *) = Call[operator new[]] : func:r962_1, 0:r962_6, 1:r962_7 # 962| mu962_9(unknown) = ^CallSideEffect : ~m? # 962| mu962_10(unknown) = ^InitializeDynamicAllocation : &:r962_8 # 962| r962_11(int *) = Convert : r962_8 @@ -5260,7 +5260,7 @@ ir.cpp: # 963| r963_4(unsigned long) = Convert : r963_3 # 963| r963_5(unsigned long) = Constant[8] : # 963| r963_6(unsigned long) = Mul : r963_4, r963_5 -# 963| r963_7(void *) = Call : func:r963_1, 0:r963_6 +# 963| r963_7(void *) = Call[operator new[]] : func:r963_1, 0:r963_6 # 963| mu963_8(unknown) = ^CallSideEffect : ~m? # 963| mu963_9(unknown) = ^InitializeDynamicAllocation : &:r963_7 # 963| r963_10(String *) = Convert : r963_7 @@ -5271,7 +5271,7 @@ ir.cpp: # 964| r964_5(unsigned long) = Constant[256] : # 964| r964_6(unsigned long) = Mul : r964_4, r964_5 # 964| r964_7(align_val_t) = Constant[128] : -# 964| r964_8(void *) = Call : func:r964_1, 0:r964_6, 1:r964_7 +# 964| r964_8(void *) = Call[operator new[]] : func:r964_1, 0:r964_6, 1:r964_7 # 964| mu964_9(unknown) = ^CallSideEffect : ~m? # 964| mu964_10(unknown) = ^InitializeDynamicAllocation : &:r964_8 # 964| r964_11(Overaligned *) = Convert : r964_8 @@ -5279,7 +5279,7 @@ ir.cpp: # 965| r965_2(unsigned long) = Constant[2560] : # 965| r965_3(align_val_t) = Constant[128] : # 965| r965_4(float) = Constant[1.0] : -# 965| r965_5(void *) = Call : func:r965_1, 0:r965_2, 1:r965_3, 2:r965_4 +# 965| r965_5(void *) = Call[operator new[]] : func:r965_1, 0:r965_2, 1:r965_3, 2:r965_4 # 965| mu965_6(unknown) = ^CallSideEffect : ~m? # 965| mu965_7(unknown) = ^InitializeDynamicAllocation : &:r965_5 # 965| r965_8(Overaligned *) = Convert : r965_5 @@ -5289,7 +5289,7 @@ ir.cpp: # 966| r966_4(unsigned long) = Convert : r966_3 # 966| r966_5(unsigned long) = Constant[1] : # 966| r966_6(unsigned long) = Mul : r966_4, r966_5 -# 966| r966_7(void *) = Call : func:r966_1, 0:r966_6 +# 966| r966_7(void *) = Call[operator new[]] : func:r966_1, 0:r966_6 # 966| mu966_8(unknown) = ^CallSideEffect : ~m? # 966| mu966_9(unknown) = ^InitializeDynamicAllocation : &:r966_7 # 966| r966_10(DefaultCtorWithDefaultParam *) = Convert : r966_7 @@ -5299,7 +5299,7 @@ ir.cpp: # 967| r967_4(unsigned long) = Convert : r967_3 # 967| r967_5(unsigned long) = Constant[4] : # 967| r967_6(unsigned long) = Mul : r967_4, r967_5 -# 967| r967_7(void *) = Call : func:r967_1, 0:r967_6 +# 967| r967_7(void *) = Call[operator new[]] : func:r967_1, 0:r967_6 # 967| mu967_8(unknown) = ^CallSideEffect : ~m? # 967| mu967_9(unknown) = ^InitializeDynamicAllocation : &:r967_7 # 967| r967_10(int *) = Convert : r967_7 @@ -5522,7 +5522,7 @@ ir.cpp: # 997| r997_7(glval<..(*)(..)>) = VariableAddress[fn] : # 997| r997_8(..(*)(..)) = Load : &:r997_7, ~m? # 997| r997_9(float) = Constant[1.0] : -# 997| r997_10(int) = Call : func:r997_8, 0:r997_9 +# 997| r997_10(int) = Call[?] : func:r997_8, 0:r997_9 # 997| mu997_11(unknown) = ^CallSideEffect : ~m? # 997| r997_12(int) = Add : r997_6, r997_10 # 997| mu997_13(int) = Store : &:r997_1, r997_12 @@ -5704,7 +5704,7 @@ ir.cpp: # 1044| r1044_2(glval) = Convert : r1044_1 # 1044| r1044_3(glval) = FunctionAddress[operator()] : # 1044| r1044_4(float) = Constant[1.0] : -# 1044| r1044_5(char) = Call : func:r1044_3, this:r1044_2, 0:r1044_4 +# 1044| r1044_5(char) = Call[operator()] : func:r1044_3, this:r1044_2, 0:r1044_4 # 1044| mu1044_6(unknown) = ^CallSideEffect : ~m? # 1044| v1044_7(void) = ^BufferReadSideEffect[-1] : &:r1044_2, ~m? # 1044| mu1044_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1044_2 @@ -5713,7 +5713,7 @@ ir.cpp: # 1045| mu1045_3(decltype([...](...){...})) = Uninitialized[#temp1045:20] : &:r1045_2 # 1045| r1045_4(glval) = FieldAddress[s] : r1045_2 # 1045| r1045_5(glval) = FunctionAddress[String] : -# 1045| v1045_6(void) = Call : func:r1045_5, this:r1045_4 +# 1045| v1045_6(void) = Call[String] : func:r1045_5, this:r1045_4 # 1045| mu1045_7(unknown) = ^CallSideEffect : ~m? # 1045| mu1045_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1045_4 # 1045| r1045_9(glval) = FieldAddress[x] : r1045_2 @@ -5726,7 +5726,7 @@ ir.cpp: # 1046| r1046_2(glval) = Convert : r1046_1 # 1046| r1046_3(glval) = FunctionAddress[operator()] : # 1046| r1046_4(float) = Constant[2.0] : -# 1046| r1046_5(char) = Call : func:r1046_3, this:r1046_2, 0:r1046_4 +# 1046| r1046_5(char) = Call[operator()] : func:r1046_3, this:r1046_2, 0:r1046_4 # 1046| mu1046_6(unknown) = ^CallSideEffect : ~m? # 1046| v1046_7(void) = ^BufferReadSideEffect[-1] : &:r1046_2, ~m? # 1046| mu1046_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1046_2 @@ -5745,7 +5745,7 @@ ir.cpp: # 1048| r1048_2(glval) = Convert : r1048_1 # 1048| r1048_3(glval) = FunctionAddress[operator()] : # 1048| r1048_4(float) = Constant[3.0] : -# 1048| r1048_5(char) = Call : func:r1048_3, this:r1048_2, 0:r1048_4 +# 1048| r1048_5(char) = Call[operator()] : func:r1048_3, this:r1048_2, 0:r1048_4 # 1048| mu1048_6(unknown) = ^CallSideEffect : ~m? # 1048| v1048_7(void) = ^BufferReadSideEffect[-1] : &:r1048_2, ~m? # 1048| mu1048_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1048_2 @@ -5754,7 +5754,7 @@ ir.cpp: # 1049| mu1049_3(decltype([...](...){...})) = Uninitialized[#temp1049:29] : &:r1049_2 # 1049| r1049_4(glval) = FieldAddress[s] : r1049_2 # 1049| r1049_5(glval) = FunctionAddress[String] : -# 1049| v1049_6(void) = Call : func:r1049_5, this:r1049_4 +# 1049| v1049_6(void) = Call[String] : func:r1049_5, this:r1049_4 # 1049| mu1049_7(unknown) = ^CallSideEffect : ~m? # 1049| mu1049_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1049_4 # 1049| r1049_9(decltype([...](...){...})) = Load : &:r1049_2, ~m? @@ -5763,7 +5763,7 @@ ir.cpp: # 1050| r1050_2(glval) = Convert : r1050_1 # 1050| r1050_3(glval) = FunctionAddress[operator()] : # 1050| r1050_4(float) = Constant[4.0] : -# 1050| r1050_5(char) = Call : func:r1050_3, this:r1050_2, 0:r1050_4 +# 1050| r1050_5(char) = Call[operator()] : func:r1050_3, this:r1050_2, 0:r1050_4 # 1050| mu1050_6(unknown) = ^CallSideEffect : ~m? # 1050| v1050_7(void) = ^BufferReadSideEffect[-1] : &:r1050_2, ~m? # 1050| mu1050_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1050_2 @@ -5786,7 +5786,7 @@ ir.cpp: # 1052| r1052_2(glval) = Convert : r1052_1 # 1052| r1052_3(glval) = FunctionAddress[operator()] : # 1052| r1052_4(float) = Constant[5.0] : -# 1052| r1052_5(char) = Call : func:r1052_3, this:r1052_2, 0:r1052_4 +# 1052| r1052_5(char) = Call[operator()] : func:r1052_3, this:r1052_2, 0:r1052_4 # 1052| mu1052_6(unknown) = ^CallSideEffect : ~m? # 1052| v1052_7(void) = ^BufferReadSideEffect[-1] : &:r1052_2, ~m? # 1052| mu1052_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1052_2 @@ -5825,7 +5825,7 @@ ir.cpp: # 1055| r1055_2(glval) = Convert : r1055_1 # 1055| r1055_3(glval) = FunctionAddress[operator()] : # 1055| r1055_4(float) = Constant[6.0] : -# 1055| r1055_5(char) = Call : func:r1055_3, this:r1055_2, 0:r1055_4 +# 1055| r1055_5(char) = Call[operator()] : func:r1055_3, this:r1055_2, 0:r1055_4 # 1055| mu1055_6(unknown) = ^CallSideEffect : ~m? # 1055| v1055_7(void) = ^BufferReadSideEffect[-1] : &:r1055_2, ~m? # 1055| mu1055_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1055_2 @@ -5891,7 +5891,7 @@ ir.cpp: # 1043| r1043_14(String &) = Load : &:r1043_13, ~m? # 1043| r1043_15(glval) = CopyValue : r1043_14 # 1043| r1043_16(glval) = FunctionAddress[c_str] : -# 1043| r1043_17(char *) = Call : func:r1043_16, this:r1043_15 +# 1043| r1043_17(char *) = Call[c_str] : func:r1043_16, this:r1043_15 # 1043| mu1043_18(unknown) = ^CallSideEffect : ~m? # 1043| v1043_19(void) = ^BufferReadSideEffect[-1] : &:r1043_15, ~m? # 1043| mu1043_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1043_15 @@ -5921,7 +5921,7 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 1045| r1045_8(glval) = FieldAddress[s] : mu1045_5 # 1045| r1045_9(glval) = FunctionAddress[~String] : -# 1045| v1045_10(void) = Call : func:r1045_9, this:r1045_8 +# 1045| v1045_10(void) = Call[~String] : func:r1045_9, this:r1045_8 # 1045| mu1045_11(unknown) = ^CallSideEffect : ~m? # 1045| v1045_12(void) = ReturnIndirection[#this] : &:r1045_6, ~m? # 1045| v1045_13(void) = ReturnVoid : @@ -5944,7 +5944,7 @@ ir.cpp: # 1045| r1045_12(lambda [] type at line 1045, col. 21 *) = Load : &:r1045_11, ~m? # 1045| r1045_13(glval) = FieldAddress[s] : r1045_12 # 1045| r1045_14(glval) = FunctionAddress[c_str] : -# 1045| r1045_15(char *) = Call : func:r1045_14, this:r1045_13 +# 1045| r1045_15(char *) = Call[c_str] : func:r1045_14, this:r1045_13 # 1045| mu1045_16(unknown) = ^CallSideEffect : ~m? # 1045| v1045_17(void) = ^BufferReadSideEffect[-1] : &:r1045_13, ~m? # 1045| mu1045_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1045_13 @@ -5979,7 +5979,7 @@ ir.cpp: # 1047| r1047_14(String &) = Load : &:r1047_13, ~m? # 1047| r1047_15(glval) = CopyValue : r1047_14 # 1047| r1047_16(glval) = FunctionAddress[c_str] : -# 1047| r1047_17(char *) = Call : func:r1047_16, this:r1047_15 +# 1047| r1047_17(char *) = Call[c_str] : func:r1047_16, this:r1047_15 # 1047| mu1047_18(unknown) = ^CallSideEffect : ~m? # 1047| v1047_19(void) = ^BufferReadSideEffect[-1] : &:r1047_15, ~m? # 1047| mu1047_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1047_15 @@ -6005,7 +6005,7 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 1049| r1049_8(glval) = FieldAddress[s] : mu1049_5 # 1049| r1049_9(glval) = FunctionAddress[~String] : -# 1049| v1049_10(void) = Call : func:r1049_9, this:r1049_8 +# 1049| v1049_10(void) = Call[~String] : func:r1049_9, this:r1049_8 # 1049| mu1049_11(unknown) = ^CallSideEffect : ~m? # 1049| v1049_12(void) = ReturnIndirection[#this] : &:r1049_6, ~m? # 1049| v1049_13(void) = ReturnVoid : @@ -6028,7 +6028,7 @@ ir.cpp: # 1049| r1049_12(lambda [] type at line 1049, col. 30 *) = Load : &:r1049_11, ~m? # 1049| r1049_13(glval) = FieldAddress[s] : r1049_12 # 1049| r1049_14(glval) = FunctionAddress[c_str] : -# 1049| r1049_15(char *) = Call : func:r1049_14, this:r1049_13 +# 1049| r1049_15(char *) = Call[c_str] : func:r1049_14, this:r1049_13 # 1049| mu1049_16(unknown) = ^CallSideEffect : ~m? # 1049| v1049_17(void) = ^BufferReadSideEffect[-1] : &:r1049_13, ~m? # 1049| mu1049_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1049_13 @@ -6060,7 +6060,7 @@ ir.cpp: # 1051| r1051_14(String &) = Load : &:r1051_13, ~m? # 1051| r1051_15(glval) = CopyValue : r1051_14 # 1051| r1051_16(glval) = FunctionAddress[c_str] : -# 1051| r1051_17(char *) = Call : func:r1051_16, this:r1051_15 +# 1051| r1051_17(char *) = Call[c_str] : func:r1051_16, this:r1051_15 # 1051| mu1051_18(unknown) = ^CallSideEffect : ~m? # 1051| v1051_19(void) = ^BufferReadSideEffect[-1] : &:r1051_15, ~m? # 1051| mu1051_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1051_15 @@ -6095,7 +6095,7 @@ ir.cpp: # 1054| r1054_14(String &) = Load : &:r1054_13, ~m? # 1054| r1054_15(glval) = CopyValue : r1054_14 # 1054| r1054_16(glval) = FunctionAddress[c_str] : -# 1054| r1054_17(char *) = Call : func:r1054_16, this:r1054_15 +# 1054| r1054_17(char *) = Call[c_str] : func:r1054_16, this:r1054_15 # 1054| mu1054_18(unknown) = ^CallSideEffect : ~m? # 1054| v1054_19(void) = ^BufferReadSideEffect[-1] : &:r1054_15, ~m? # 1054| mu1054_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1054_15 @@ -6143,7 +6143,7 @@ ir.cpp: # 1078| r1078_9(vector &) = Load : &:r1078_8, ~m? #-----| r0_1(glval>) = CopyValue : r1078_9 # 1078| r1078_10(glval) = FunctionAddress[begin] : -# 1078| r1078_11(iterator) = Call : func:r1078_10, this:r0_1 +# 1078| r1078_11(iterator) = Call[begin] : func:r1078_10, this:r0_1 # 1078| mu1078_12(unknown) = ^CallSideEffect : ~m? #-----| v0_2(void) = ^BufferReadSideEffect[-1] : &:r0_1, ~m? #-----| mu0_3(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_1 @@ -6153,7 +6153,7 @@ ir.cpp: # 1078| r1078_16(vector &) = Load : &:r1078_15, ~m? #-----| r0_4(glval>) = CopyValue : r1078_16 # 1078| r1078_17(glval) = FunctionAddress[end] : -# 1078| r1078_18(iterator) = Call : func:r1078_17, this:r0_4 +# 1078| r1078_18(iterator) = Call[end] : func:r1078_17, this:r0_4 # 1078| mu1078_19(unknown) = ^CallSideEffect : ~m? #-----| v0_5(void) = ^BufferReadSideEffect[-1] : &:r0_4, ~m? #-----| mu0_6(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_4 @@ -6166,7 +6166,7 @@ ir.cpp: # 1084| r1084_2(glval) = FunctionAddress[operator!=] : # 1084| r1084_3(glval) = VariableAddress[(__end)] : # 1084| r1084_4(iterator) = Load : &:r1084_3, ~m? -# 1084| r1084_5(bool) = Call : func:r1084_2, this:r0_7, 0:r1084_4 +# 1084| r1084_5(bool) = Call[operator!=] : func:r1084_2, this:r0_7, 0:r1084_4 # 1084| mu1084_6(unknown) = ^CallSideEffect : ~m? #-----| v0_8(void) = ^BufferReadSideEffect[-1] : &:r0_7, ~m? #-----| mu0_9(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_7 @@ -6177,7 +6177,7 @@ ir.cpp: # 1084| Block 2 # 1084| r1084_8(glval) = VariableAddress[(__begin)] : # 1084| r1084_9(glval) = FunctionAddress[operator++] : -# 1084| r1084_10(iterator &) = Call : func:r1084_9, this:r1084_8 +# 1084| r1084_10(iterator &) = Call[operator++] : func:r1084_9, this:r1084_8 # 1084| mu1084_11(unknown) = ^CallSideEffect : ~m? # 1084| v1084_12(void) = ^BufferReadSideEffect[-1] : &:r1084_8, ~m? # 1084| mu1084_13(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1084_8 @@ -6189,7 +6189,7 @@ ir.cpp: # 1084| r1084_16(glval) = VariableAddress[(__begin)] : #-----| r0_10(glval) = Convert : r1084_16 # 1084| r1084_17(glval) = FunctionAddress[operator*] : -# 1084| r1084_18(int &) = Call : func:r1084_17, this:r0_10 +# 1084| r1084_18(int &) = Call[operator*] : func:r1084_17, this:r0_10 # 1084| mu1084_19(unknown) = ^CallSideEffect : ~m? #-----| v0_11(void) = ^BufferReadSideEffect[-1] : &:r0_10, ~m? #-----| mu0_12(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_10 @@ -6224,7 +6224,7 @@ ir.cpp: # 1078| r1078_22(glval) = FunctionAddress[operator!=] : # 1078| r1078_23(glval) = VariableAddress[(__end)] : # 1078| r1078_24(iterator) = Load : &:r1078_23, ~m? -# 1078| r1078_25(bool) = Call : func:r1078_22, this:r0_13, 0:r1078_24 +# 1078| r1078_25(bool) = Call[operator!=] : func:r1078_22, this:r0_13, 0:r1078_24 # 1078| mu1078_26(unknown) = ^CallSideEffect : ~m? #-----| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_13, ~m? #-----| mu0_15(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_13 @@ -6237,7 +6237,7 @@ ir.cpp: # 1078| r1078_29(glval) = VariableAddress[(__begin)] : #-----| r0_16(glval) = Convert : r1078_29 # 1078| r1078_30(glval) = FunctionAddress[operator*] : -# 1078| r1078_31(int &) = Call : func:r1078_30, this:r0_16 +# 1078| r1078_31(int &) = Call[operator*] : func:r1078_30, this:r0_16 # 1078| mu1078_32(unknown) = ^CallSideEffect : ~m? #-----| v0_17(void) = ^BufferReadSideEffect[-1] : &:r0_16, ~m? #-----| mu0_18(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_16 @@ -6259,7 +6259,7 @@ ir.cpp: # 1078| v1078_35(void) = NoOp : # 1078| r1078_36(glval) = VariableAddress[(__begin)] : # 1078| r1078_37(glval) = FunctionAddress[operator++] : -# 1078| r1078_38(iterator &) = Call : func:r1078_37, this:r1078_36 +# 1078| r1078_38(iterator &) = Call[operator++] : func:r1078_37, this:r1078_36 # 1078| mu1078_39(unknown) = ^CallSideEffect : ~m? # 1078| v1078_40(void) = ^BufferReadSideEffect[-1] : &:r1078_36, ~m? # 1078| mu1078_41(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1078_36 @@ -6278,7 +6278,7 @@ ir.cpp: # 1084| r1084_32(vector &) = Load : &:r1084_31, ~m? #-----| r0_19(glval>) = CopyValue : r1084_32 # 1084| r1084_33(glval) = FunctionAddress[begin] : -# 1084| r1084_34(iterator) = Call : func:r1084_33, this:r0_19 +# 1084| r1084_34(iterator) = Call[begin] : func:r1084_33, this:r0_19 # 1084| mu1084_35(unknown) = ^CallSideEffect : ~m? #-----| v0_20(void) = ^BufferReadSideEffect[-1] : &:r0_19, ~m? #-----| mu0_21(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_19 @@ -6288,7 +6288,7 @@ ir.cpp: # 1084| r1084_39(vector &) = Load : &:r1084_38, ~m? #-----| r0_22(glval>) = CopyValue : r1084_39 # 1084| r1084_40(glval) = FunctionAddress[end] : -# 1084| r1084_41(iterator) = Call : func:r1084_40, this:r0_22 +# 1084| r1084_41(iterator) = Call[end] : func:r1084_40, this:r0_22 # 1084| mu1084_42(unknown) = ^CallSideEffect : ~m? #-----| v0_23(void) = ^BufferReadSideEffect[-1] : &:r0_22, ~m? #-----| mu0_24(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_22 @@ -6460,7 +6460,7 @@ ir.cpp: # 1149| r1149_13(glval) = FunctionAddress[String] : # 1149| r1149_14(glval) = StringConstant["String object"] : # 1149| r1149_15(char *) = Convert : r1149_14 -# 1149| v1149_16(void) = Call : func:r1149_13, this:r1149_11, 0:r1149_15 +# 1149| v1149_16(void) = Call[String] : func:r1149_13, this:r1149_11, 0:r1149_15 # 1149| mu1149_17(unknown) = ^CallSideEffect : ~m? # 1149| mu1149_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1149_11 # 1149| v1149_19(void) = ^BufferReadSideEffect[0] : &:r1149_15, ~m? @@ -6489,7 +6489,7 @@ ir.cpp: # 1154| r1154_3(glval) = FunctionAddress[String] : # 1154| r1154_4(glval) = VariableAddress[s] : # 1154| r1154_5(char *) = Load : &:r1154_4, ~m? -# 1154| v1154_6(void) = Call : func:r1154_3, this:r1154_1, 0:r1154_5 +# 1154| v1154_6(void) = Call[String] : func:r1154_3, this:r1154_1, 0:r1154_5 # 1154| mu1154_7(unknown) = ^CallSideEffect : ~m? # 1154| mu1154_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1154_1 # 1154| v1154_9(void) = ^BufferReadSideEffect[0] : &:r1154_5, ~m? @@ -6594,7 +6594,7 @@ ir.cpp: # 1174| r1174_6(int *) = CopyValue : r1174_5 # 1174| r1174_7(void *) = Convert : r1174_6 # 1174| r1174_8(int) = Constant[4] : -# 1174| r1174_9(void *) = Call : func:r1174_1, 0:r1174_4, 1:r1174_7, 2:r1174_8 +# 1174| r1174_9(void *) = Call[memcpy] : func:r1174_1, 0:r1174_4, 1:r1174_7, 2:r1174_8 # 1174| v1174_10(void) = ^SizedBufferReadSideEffect[1] : &:r1174_7, r1174_8, ~m? # 1174| mu1174_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r1174_4, r1174_8 # 1175| r1175_1(glval) = VariableAddress[#return] : @@ -6616,7 +6616,7 @@ ir.cpp: # 1179| r1179_3(glval) = FunctionAddress[String] : # 1179| r1179_4(glval) = StringConstant["foo"] : # 1179| r1179_5(char *) = Convert : r1179_4 -# 1179| r1179_6(String) = Call : func:r1179_3, this:r1179_1, 0:r1179_5 +# 1179| r1179_6(String) = Call[String] : func:r1179_3, this:r1179_1, 0:r1179_5 # 1179| mu1179_7(unknown) = ^CallSideEffect : ~m? # 1179| mu1179_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1179_1 # 1179| v1179_9(void) = ^BufferReadSideEffect[0] : &:r1179_5, ~m? @@ -6862,7 +6862,7 @@ ir.cpp: # 1242| r1242_5(glval) = FunctionAddress[String] : # 1242| r1242_6(glval) = StringConstant["static"] : # 1242| r1242_7(char *) = Convert : r1242_6 -# 1242| v1242_8(void) = Call : func:r1242_5, this:r1242_4, 0:r1242_7 +# 1242| v1242_8(void) = Call[String] : func:r1242_5, this:r1242_4, 0:r1242_7 # 1242| mu1242_9(unknown) = ^CallSideEffect : ~m? # 1242| mu1242_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1242_4 # 1242| v1242_11(void) = ^BufferReadSideEffect[0] : &:r1242_7, ~m? @@ -6883,7 +6883,7 @@ ir.cpp: # 1243| r1243_5(glval) = FunctionAddress[String] : # 1243| r1243_6(glval) = VariableAddress[dynamic] : # 1243| r1243_7(char *) = Load : &:r1243_6, ~m? -# 1243| v1243_8(void) = Call : func:r1243_5, this:r1243_4, 0:r1243_7 +# 1243| v1243_8(void) = Call[String] : func:r1243_5, this:r1243_4, 0:r1243_7 # 1243| mu1243_9(unknown) = ^CallSideEffect : ~m? # 1243| mu1243_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1243_4 # 1243| v1243_11(void) = ^BufferReadSideEffect[0] : &:r1243_7, ~m? @@ -6902,7 +6902,7 @@ ir.cpp: # 1241| Block 6 # 1241| r1241_4(glval) = VariableAddress[a] : #-----| r0_1(glval) = FunctionAddress[String] : -#-----| v0_2(void) = Call : func:r0_1, this:r1241_4 +#-----| v0_2(void) = Call[String] : func:r0_1, this:r1241_4 #-----| mu0_3(unknown) = ^CallSideEffect : ~m? #-----| mu0_4(String) = ^IndirectMayWriteSideEffect[-1] : &:r1241_4 # 1241| r1241_5(bool) = Constant[1] : @@ -6938,7 +6938,7 @@ ir.cpp: # 1254| r1254_4(glval) = VariableAddress[s1] : # 1254| r1254_5(char *) = Load : &:r1254_4, ~m? # 1254| r1254_6(char *) = Convert : r1254_5 -# 1254| r1254_7(char *) = Call : func:r1254_1, 0:r1254_3, 1:r1254_6 +# 1254| r1254_7(char *) = Call[strcpy] : func:r1254_1, 0:r1254_3, 1:r1254_6 # 1254| v1254_8(void) = ^BufferReadSideEffect[1] : &:r1254_6, ~m? # 1254| mu1254_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r1254_3 # 1255| r1255_1(glval) = FunctionAddress[strcat] : @@ -6947,7 +6947,7 @@ ir.cpp: # 1255| r1255_4(glval) = VariableAddress[s2] : # 1255| r1255_5(char *) = Load : &:r1255_4, ~m? # 1255| r1255_6(char *) = Convert : r1255_5 -# 1255| r1255_7(char *) = Call : func:r1255_1, 0:r1255_3, 1:r1255_6 +# 1255| r1255_7(char *) = Call[strcat] : func:r1255_1, 0:r1255_3, 1:r1255_6 # 1255| v1255_8(void) = ^BufferReadSideEffect[0] : &:r1255_3, ~m? # 1255| v1255_9(void) = ^BufferReadSideEffect[1] : &:r1255_6, ~m? # 1255| mu1255_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1255_3 @@ -6995,17 +6995,17 @@ ir.cpp: # 1271| r1271_1(glval) = VariableAddress[c] : # 1271| mu1271_2(C) = Uninitialized[c] : &:r1271_1 # 1271| r1271_3(glval) = FunctionAddress[C] : -# 1271| v1271_4(void) = Call : func:r1271_3, this:r1271_1 +# 1271| v1271_4(void) = Call[C] : func:r1271_3, this:r1271_1 # 1271| mu1271_5(unknown) = ^CallSideEffect : ~m? # 1271| mu1271_6(C) = ^IndirectMayWriteSideEffect[-1] : &:r1271_1 # 1272| r1272_1(glval) = VariableAddress[c] : # 1272| r1272_2(glval) = FunctionAddress[StaticMemberFunction] : # 1272| r1272_3(int) = Constant[10] : -# 1272| r1272_4(int) = Call : func:r1272_2, 0:r1272_3 +# 1272| r1272_4(int) = Call[StaticMemberFunction] : func:r1272_2, 0:r1272_3 # 1272| mu1272_5(unknown) = ^CallSideEffect : ~m? # 1273| r1273_1(glval) = FunctionAddress[StaticMemberFunction] : # 1273| r1273_2(int) = Constant[10] : -# 1273| r1273_3(int) = Call : func:r1273_1, 0:r1273_2 +# 1273| r1273_3(int) = Call[StaticMemberFunction] : func:r1273_1, 0:r1273_2 # 1273| mu1273_4(unknown) = ^CallSideEffect : ~m? # 1275| r1275_1(glval) = VariableAddress[a] : # 1275| mu1275_2(A) = Uninitialized[a] : &:r1275_1 @@ -7015,7 +7015,7 @@ ir.cpp: # 1276| r1276_4(A *) = CopyValue : r1276_3 # 1276| r1276_5(glval) = VariableAddress[int_arg] : # 1276| r1276_6(int) = Load : &:r1276_5, ~m? -# 1276| v1276_7(void) = Call : func:r1276_2, 0:r1276_4, 1:r1276_6 +# 1276| v1276_7(void) = Call[static_member] : func:r1276_2, 0:r1276_4, 1:r1276_6 # 1276| mu1276_8(unknown) = ^CallSideEffect : ~m? # 1276| v1276_9(void) = ^BufferReadSideEffect[0] : &:r1276_4, ~m? # 1276| mu1276_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1276_4 @@ -7024,7 +7024,7 @@ ir.cpp: # 1277| r1277_3(A *) = CopyValue : r1277_2 # 1277| r1277_4(glval) = VariableAddress[int_arg] : # 1277| r1277_5(int) = Load : &:r1277_4, ~m? -# 1277| v1277_6(void) = Call : func:r1277_1, 0:r1277_3, 1:r1277_5 +# 1277| v1277_6(void) = Call[static_member] : func:r1277_1, 0:r1277_3, 1:r1277_5 # 1277| mu1277_7(unknown) = ^CallSideEffect : ~m? # 1277| v1277_8(void) = ^BufferReadSideEffect[0] : &:r1277_3, ~m? # 1277| mu1277_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r1277_3 @@ -7037,7 +7037,7 @@ ir.cpp: # 1279| r1279_7(int) = Load : &:r1279_6, ~m? # 1279| r1279_8(int) = Constant[2] : # 1279| r1279_9(int) = Add : r1279_7, r1279_8 -# 1279| v1279_10(void) = Call : func:r1279_3, 0:r1279_5, 1:r1279_9 +# 1279| v1279_10(void) = Call[static_member] : func:r1279_3, 0:r1279_5, 1:r1279_9 # 1279| mu1279_11(unknown) = ^CallSideEffect : ~m? # 1279| v1279_12(void) = ^BufferReadSideEffect[0] : &:r1279_5, ~m? # 1279| mu1279_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r1279_5 @@ -7048,7 +7048,7 @@ ir.cpp: # 1280| r1280_5(glval) = VariableAddress[a] : # 1280| r1280_6(A *) = CopyValue : r1280_5 # 1280| r1280_7(int) = Constant[99] : -# 1280| v1280_8(void) = Call : func:r1280_4, 0:r1280_6, 1:r1280_7 +# 1280| v1280_8(void) = Call[static_member] : func:r1280_4, 0:r1280_6, 1:r1280_7 # 1280| mu1280_9(unknown) = ^CallSideEffect : ~m? # 1280| v1280_10(void) = ^BufferReadSideEffect[0] : &:r1280_6, ~m? # 1280| mu1280_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r1280_6 @@ -7058,22 +7058,22 @@ ir.cpp: # 1281| r1281_4(glval) = VariableAddress[a_arg] : # 1281| r1281_5(A *) = Load : &:r1281_4, ~m? # 1281| r1281_6(int) = Constant[-1] : -# 1281| v1281_7(void) = Call : func:r1281_3, 0:r1281_5, 1:r1281_6 +# 1281| v1281_7(void) = Call[static_member] : func:r1281_3, 0:r1281_5, 1:r1281_6 # 1281| mu1281_8(unknown) = ^CallSideEffect : ~m? # 1281| v1281_9(void) = ^BufferReadSideEffect[0] : &:r1281_5, ~m? # 1281| mu1281_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1281_5 # 1283| r1283_1(glval) = VariableAddress[a] : # 1283| r1283_2(glval) = FunctionAddress[static_member_without_def] : -# 1283| v1283_3(void) = Call : func:r1283_2 +# 1283| v1283_3(void) = Call[static_member_without_def] : func:r1283_2 # 1283| mu1283_4(unknown) = ^CallSideEffect : ~m? # 1284| r1284_1(glval) = FunctionAddress[static_member_without_def] : -# 1284| v1284_2(void) = Call : func:r1284_1 +# 1284| v1284_2(void) = Call[static_member_without_def] : func:r1284_1 # 1284| mu1284_3(unknown) = ^CallSideEffect : ~m? # 1286| r1286_1(glval) = FunctionAddress[getAnInstanceOfA] : -# 1286| r1286_2(A *) = Call : func:r1286_1 +# 1286| r1286_2(A *) = Call[getAnInstanceOfA] : func:r1286_1 # 1286| mu1286_3(unknown) = ^CallSideEffect : ~m? # 1286| r1286_4(glval) = FunctionAddress[static_member_without_def] : -# 1286| v1286_5(void) = Call : func:r1286_4 +# 1286| v1286_5(void) = Call[static_member_without_def] : func:r1286_4 # 1286| mu1286_6(unknown) = ^CallSideEffect : ~m? # 1287| v1287_1(void) = NoOp : # 1270| v1270_10(void) = ReturnIndirection[a_arg] : &:r1270_8, ~m? @@ -7123,7 +7123,7 @@ ir.cpp: # 1296| r1296_3(int) = Load : &:r1296_2, ~m? # 1296| r1296_4(glval) = VariableAddress[y] : # 1296| r1296_5(int) = Load : &:r1296_4, ~m? -# 1296| v1296_6(void) = Call : func:r1296_1, 0:r1296_3, 1:r1296_5 +# 1296| v1296_6(void) = Call[IntegerOps] : func:r1296_1, 0:r1296_3, 1:r1296_5 # 1296| mu1296_7(unknown) = ^CallSideEffect : ~m? # 1296| v1296_8(void) = NoOp : # 1295| v1295_8(void) = ReturnVoid : @@ -7372,7 +7372,7 @@ ir.cpp: # 1314| mu1314_7(int) = InitializeParameter[y] : &:r1314_6 # 1315| r1315_1(glval) = VariableAddress[#return] : # 1315| r1315_2(glval) = FunctionAddress[predicateA] : -# 1315| r1315_3(bool) = Call : func:r1315_2 +# 1315| r1315_3(bool) = Call[predicateA] : func:r1315_2 # 1315| mu1315_4(unknown) = ^CallSideEffect : ~m? # 1315| v1315_5(void) = ConditionalBranch : r1315_3 #-----| False -> Block 3 @@ -7380,7 +7380,7 @@ ir.cpp: # 1315| Block 1 # 1315| r1315_6(glval) = FunctionAddress[predicateB] : -# 1315| r1315_7(bool) = Call : func:r1315_6 +# 1315| r1315_7(bool) = Call[predicateB] : func:r1315_6 # 1315| mu1315_8(unknown) = ^CallSideEffect : ~m? # 1315| v1315_9(void) = ConditionalBranch : r1315_7 #-----| False -> Block 3 @@ -7423,7 +7423,7 @@ ir.cpp: # 1322| r1322_3(glval) = VariableAddress[p] : # 1322| r1322_4(int *) = Load : &:r1322_3, ~m? # 1322| r1322_5(void *) = Convert : r1322_4 -# 1322| r1322_6(void *) = Call : func:r1322_1, 0:r1322_2, 1:r1322_5 +# 1322| r1322_6(void *) = Call[operator new] : func:r1322_1, 0:r1322_2, 1:r1322_5 # 1322| mu1322_7(unknown) = ^CallSideEffect : ~m? # 1322| mu1322_8(unknown) = ^InitializeDynamicAllocation : &:r1322_6 # 1322| r1322_9(int *) = Convert : r1322_6 @@ -7462,12 +7462,12 @@ perf-regression.cpp: # 10| r10_1(glval) = VariableAddress[big] : # 10| r10_2(glval) = FunctionAddress[operator new] : # 10| r10_3(unsigned long) = Constant[1073741824] : -# 10| r10_4(void *) = Call : func:r10_2, 0:r10_3 +# 10| r10_4(void *) = Call[operator new] : func:r10_2, 0:r10_3 # 10| mu10_5(unknown) = ^CallSideEffect : ~m? # 10| mu10_6(unknown) = ^InitializeDynamicAllocation : &:r10_4 # 10| r10_7(Big *) = Convert : r10_4 # 10| r10_8(glval) = FunctionAddress[Big] : -# 10| v10_9(void) = Call : func:r10_8, this:r10_7 +# 10| v10_9(void) = Call[Big] : func:r10_8, this:r10_7 # 10| mu10_10(unknown) = ^CallSideEffect : ~m? # 10| mu10_11(Big) = ^IndirectMayWriteSideEffect[-1] : &:r10_7 # 10| mu10_12(Big *) = Store : &:r10_1, r10_7 @@ -7507,7 +7507,7 @@ struct_init.cpp: # 25| r25_1(glval) = FunctionAddress[let_info_escape] : # 25| r25_2(glval) = VariableAddress[static_infos] : # 25| r25_3(Info *) = Convert : r25_2 -# 25| v25_4(void) = Call : func:r25_1, 0:r25_3 +# 25| v25_4(void) = Call[let_info_escape] : func:r25_1, 0:r25_3 # 25| mu25_5(unknown) = ^CallSideEffect : ~m? # 25| v25_6(void) = ^BufferReadSideEffect[0] : &:r25_3, ~m? # 25| mu25_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r25_3 @@ -7545,7 +7545,7 @@ struct_init.cpp: # 33| r33_1(glval) = FunctionAddress[let_info_escape] : # 33| r33_2(glval) = VariableAddress[local_infos] : # 33| r33_3(Info *) = Convert : r33_2 -# 33| v33_4(void) = Call : func:r33_1, 0:r33_3 +# 33| v33_4(void) = Call[let_info_escape] : func:r33_1, 0:r33_3 # 33| mu33_5(unknown) = ^CallSideEffect : ~m? # 33| v33_6(void) = ^BufferReadSideEffect[0] : &:r33_3, ~m? # 33| mu33_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r33_3 @@ -7573,7 +7573,7 @@ struct_init.cpp: # 41| r41_1(glval) = FunctionAddress[let_info_escape] : # 41| r41_2(glval) = VariableAddress[static_infos] : # 41| r41_3(Info *) = Convert : r41_2 -# 41| v41_4(void) = Call : func:r41_1, 0:r41_3 +# 41| v41_4(void) = Call[let_info_escape] : func:r41_1, 0:r41_3 # 41| mu41_5(unknown) = ^CallSideEffect : ~m? # 41| v41_6(void) = ^BufferReadSideEffect[0] : &:r41_3, ~m? # 41| mu41_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r41_3 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 43b8116d85f..ae579bbbbdc 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -346,7 +346,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| m97_6(unknown) = ^CallSideEffect : ~m95_7 # 97| m97_7(unknown) = Chi : total:m95_7, partial:m97_6 # 97| v97_8(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m97_7 @@ -403,7 +403,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| m108_6(unknown) = ^CallSideEffect : ~m105_7 # 108| m108_7(unknown) = Chi : total:m105_7, partial:m108_6 # 108| v108_8(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m108_7 @@ -476,7 +476,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| m119_6(unknown) = ^CallSideEffect : ~m117_13 # 119| m119_7(unknown) = Chi : total:m117_13, partial:m119_6 # 119| v119_8(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m119_7 @@ -848,7 +848,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_10 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_8 # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_12 # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -856,7 +856,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_6 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_8 # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -865,7 +865,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_14 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -901,7 +901,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m207_6 # 209| m209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 209| m209_12(unknown) = Chi : total:m208_3, partial:m209_11 @@ -988,7 +988,7 @@ ssa.cpp: # 226| m226_3(unknown) = InitializeNonLocal : # 226| m226_4(unknown) = Chi : total:m226_2, partial:m226_3 # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| m227_3(unknown) = ^CallSideEffect : ~m226_4 # 227| m227_4(unknown) = Chi : total:m226_4, partial:m227_3 # 229| r229_1(glval) = VariableAddress[s] : @@ -1051,14 +1051,14 @@ ssa.cpp: # 240| m240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| m240_6(unknown) = ^CallSideEffect : ~m239_4 # 240| m240_7(unknown) = Chi : total:m239_4, partial:m240_6 # 240| m240_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 240| m240_9(Constructible) = Chi : total:m240_2, partial:m240_8 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 # 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 @@ -1066,7 +1066,7 @@ ssa.cpp: # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 # 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 @@ -1076,14 +1076,14 @@ ssa.cpp: # 243| m243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| m243_6(unknown) = ^CallSideEffect : ~m242_5 # 243| m243_7(unknown) = Chi : total:m242_5, partial:m243_6 # 243| m243_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 243| m243_9(Constructible) = Chi : total:m243_2, partial:m243_8 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 # 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 @@ -1114,7 +1114,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| m248_9(unknown) = ^CallSideEffect : ~m247_9 # 248| m248_10(unknown) = Chi : total:m247_9, partial:m248_9 # 248| m248_11(unknown) = ^InitializeDynamicAllocation : &:r248_8 @@ -1136,7 +1136,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_11 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6 # 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 250| m250_13(unknown) = Chi : total:m249_6, partial:m250_12 @@ -1166,14 +1166,14 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| m256_3(unknown) = ^CallSideEffect : ~m254_4 # 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3 #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| m259_3(unknown) = ^CallSideEffect : ~m254_4 # 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3 #-----| Goto -> Block 3 @@ -1213,7 +1213,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_11 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| m269_6(unknown) = ^CallSideEffect : ~m268_9 # 269| m269_7(unknown) = Chi : total:m268_9, partial:m269_6 # 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5 @@ -1226,7 +1226,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_6 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_11 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m269_7 # 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 270| m270_11(unknown) = Chi : total:m269_9, partial:m270_10 @@ -1362,7 +1362,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| m292_5(unknown) = ^CallSideEffect : ~m291_4 # 292| m292_6(unknown) = Chi : total:m291_4, partial:m292_5 # 292| m292_7(unknown) = ^InitializeDynamicAllocation : &:r292_4 @@ -1371,7 +1371,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| m293_5(unknown) = ^CallSideEffect : ~m292_6 # 293| m293_6(unknown) = Chi : total:m292_6, partial:m293_5 # 293| m293_7(unknown) = ^InitializeDynamicAllocation : &:r293_4 @@ -1380,7 +1380,7 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| m294_5(unknown) = ^CallSideEffect : ~m293_6 # 294| m294_6(unknown) = Chi : total:m293_6, partial:m294_5 # 294| m294_7(unknown) = ^InitializeDynamicAllocation : &:r294_4 @@ -1388,7 +1388,7 @@ ssa.cpp: # 294| r294_9(glval) = FunctionAddress[A] : # 294| r294_10(glval) = FunctionAddress[operator new] : # 294| r294_11(unsigned long) = Constant[4] : -# 294| r294_12(void *) = Call : func:r294_10, 0:r294_11 +# 294| r294_12(void *) = Call[operator new] : func:r294_10, 0:r294_11 # 294| m294_13(unknown) = ^CallSideEffect : ~m294_6 # 294| m294_14(unknown) = Chi : total:m294_6, partial:m294_13 # 294| m294_15(unknown) = ^InitializeDynamicAllocation : &:r294_12 @@ -1396,12 +1396,12 @@ ssa.cpp: # 294| r294_17(glval) = FunctionAddress[A] : # 294| r294_18(glval) = VariableAddress[x] : # 294| r294_19(int) = Load : &:r294_18, m291_6 -# 294| v294_20(void) = Call : func:r294_17, this:r294_16, 0:r294_19 +# 294| v294_20(void) = Call[A] : func:r294_17, this:r294_16, 0:r294_19 # 294| m294_21(unknown) = ^CallSideEffect : ~m294_14 # 294| m294_22(unknown) = Chi : total:m294_14, partial:m294_21 # 294| m294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_16 # 294| m294_24(unknown) = Chi : total:m294_15, partial:m294_23 -# 294| v294_25(void) = Call : func:r294_9, this:r294_8, 0:r294_16 +# 294| v294_25(void) = Call[A] : func:r294_9, this:r294_8, 0:r294_16 # 294| m294_26(unknown) = ^CallSideEffect : ~m294_22 # 294| m294_27(unknown) = Chi : total:m294_22, partial:m294_26 # 294| m294_28(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_8 @@ -1415,13 +1415,13 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| m295_5(unknown) = ^CallSideEffect : ~m294_27 # 295| m295_6(unknown) = Chi : total:m294_27, partial:m295_5 # 295| m295_7(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_8(A *) = Convert : r295_4 # 295| r295_9(glval) = FunctionAddress[A] : -# 295| v295_10(void) = Call : func:r295_9, this:r295_8 +# 295| v295_10(void) = Call[A] : func:r295_9, this:r295_8 # 295| m295_11(unknown) = ^CallSideEffect : ~m295_6 # 295| m295_12(unknown) = Chi : total:m295_6, partial:m295_11 # 295| m295_13(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_8 @@ -1454,7 +1454,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_6 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_8 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| m302_7(unknown) = ^CallSideEffect : ~m301_11 # 302| m302_8(unknown) = Chi : total:m301_11, partial:m302_7 # 302| v302_9(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m302_8 @@ -1465,7 +1465,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_6 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_8 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| m303_7(unknown) = ^CallSideEffect : ~m302_11 # 303| m303_8(unknown) = Chi : total:m302_11, partial:m303_7 # 303| v303_9(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m303_8 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index ef07fde174d..0e486a79289 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -345,7 +345,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| m97_6(unknown) = ^CallSideEffect : ~m95_4 # 97| m97_7(unknown) = Chi : total:m95_4, partial:m97_6 # 97| v97_8(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m95_6 @@ -401,7 +401,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| m108_6(unknown) = ^CallSideEffect : ~m105_4 # 108| m108_7(unknown) = Chi : total:m105_4, partial:m108_6 # 108| v108_8(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m105_6 @@ -473,7 +473,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| m119_6(unknown) = ^CallSideEffect : ~m116_4 # 119| m119_7(unknown) = Chi : total:m116_4, partial:m119_6 # 119| v119_8(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m117_12 @@ -843,7 +843,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_10 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_8 # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_12 # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -851,7 +851,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_6 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_8 # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -860,7 +860,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_14 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -894,7 +894,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m207_6 # 209| m209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 209| m209_12(int) = Chi : total:m208_2, partial:m209_11 @@ -981,7 +981,7 @@ ssa.cpp: # 226| m226_3(unknown) = InitializeNonLocal : # 226| m226_4(unknown) = Chi : total:m226_2, partial:m226_3 # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| m227_3(unknown) = ^CallSideEffect : ~m226_4 # 227| m227_4(unknown) = Chi : total:m226_4, partial:m227_3 # 229| r229_1(glval) = VariableAddress[s] : @@ -1044,14 +1044,14 @@ ssa.cpp: # 240| m240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| m240_6(unknown) = ^CallSideEffect : ~m239_4 # 240| m240_7(unknown) = Chi : total:m239_4, partial:m240_6 # 240| m240_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 240| m240_9(Constructible) = Chi : total:m240_2, partial:m240_8 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 # 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 @@ -1059,7 +1059,7 @@ ssa.cpp: # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 # 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 @@ -1069,14 +1069,14 @@ ssa.cpp: # 243| m243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| m243_6(unknown) = ^CallSideEffect : ~m242_5 # 243| m243_7(unknown) = Chi : total:m242_5, partial:m243_6 # 243| m243_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 243| m243_9(Constructible) = Chi : total:m243_2, partial:m243_8 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 # 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 @@ -1106,7 +1106,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| m248_9(unknown) = ^CallSideEffect : ~m247_4 # 248| m248_10(unknown) = Chi : total:m247_4, partial:m248_9 # 248| m248_11(unknown) = ^InitializeDynamicAllocation : &:r248_8 @@ -1127,7 +1127,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_10 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6 # 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 250| m250_13(unknown) = Chi : total:m248_11, partial:m250_12 @@ -1157,14 +1157,14 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| m256_3(unknown) = ^CallSideEffect : ~m254_4 # 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3 #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| m259_3(unknown) = ^CallSideEffect : ~m254_4 # 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3 #-----| Goto -> Block 3 @@ -1203,7 +1203,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_10 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| m269_6(unknown) = ^CallSideEffect : ~m268_4 # 269| m269_7(unknown) = Chi : total:m268_4, partial:m269_6 # 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5 @@ -1215,7 +1215,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_6 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_10 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m268_8 # 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 270| m270_11(unknown) = Chi : total:m269_8, partial:m270_10 @@ -1350,7 +1350,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| m292_5(unknown) = ^CallSideEffect : ~m291_4 # 292| m292_6(unknown) = Chi : total:m291_4, partial:m292_5 # 292| m292_7(unknown) = ^InitializeDynamicAllocation : &:r292_4 @@ -1359,7 +1359,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| m293_5(unknown) = ^CallSideEffect : ~m292_6 # 293| m293_6(unknown) = Chi : total:m292_6, partial:m293_5 # 293| m293_7(unknown) = ^InitializeDynamicAllocation : &:r293_4 @@ -1368,7 +1368,7 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| m294_5(unknown) = ^CallSideEffect : ~m293_6 # 294| m294_6(unknown) = Chi : total:m293_6, partial:m294_5 # 294| m294_7(unknown) = ^InitializeDynamicAllocation : &:r294_4 @@ -1376,7 +1376,7 @@ ssa.cpp: # 294| r294_9(glval) = FunctionAddress[A] : # 294| r294_10(glval) = FunctionAddress[operator new] : # 294| r294_11(unsigned long) = Constant[4] : -# 294| r294_12(void *) = Call : func:r294_10, 0:r294_11 +# 294| r294_12(void *) = Call[operator new] : func:r294_10, 0:r294_11 # 294| m294_13(unknown) = ^CallSideEffect : ~m294_6 # 294| m294_14(unknown) = Chi : total:m294_6, partial:m294_13 # 294| m294_15(unknown) = ^InitializeDynamicAllocation : &:r294_12 @@ -1384,12 +1384,12 @@ ssa.cpp: # 294| r294_17(glval) = FunctionAddress[A] : # 294| r294_18(glval) = VariableAddress[x] : # 294| r294_19(int) = Load : &:r294_18, m291_6 -# 294| v294_20(void) = Call : func:r294_17, this:r294_16, 0:r294_19 +# 294| v294_20(void) = Call[A] : func:r294_17, this:r294_16, 0:r294_19 # 294| m294_21(unknown) = ^CallSideEffect : ~m294_14 # 294| m294_22(unknown) = Chi : total:m294_14, partial:m294_21 # 294| m294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_16 # 294| m294_24(unknown) = Chi : total:m294_15, partial:m294_23 -# 294| v294_25(void) = Call : func:r294_9, this:r294_8, 0:r294_16 +# 294| v294_25(void) = Call[A] : func:r294_9, this:r294_8, 0:r294_16 # 294| m294_26(unknown) = ^CallSideEffect : ~m294_22 # 294| m294_27(unknown) = Chi : total:m294_22, partial:m294_26 # 294| m294_28(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_8 @@ -1403,13 +1403,13 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| m295_5(unknown) = ^CallSideEffect : ~m294_27 # 295| m295_6(unknown) = Chi : total:m294_27, partial:m295_5 # 295| m295_7(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_8(A *) = Convert : r295_4 # 295| r295_9(glval) = FunctionAddress[A] : -# 295| v295_10(void) = Call : func:r295_9, this:r295_8 +# 295| v295_10(void) = Call[A] : func:r295_9, this:r295_8 # 295| m295_11(unknown) = ^CallSideEffect : ~m295_6 # 295| m295_12(unknown) = Chi : total:m295_6, partial:m295_11 # 295| m295_13(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_8 @@ -1441,7 +1441,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_6 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_8 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| m302_7(unknown) = ^CallSideEffect : ~m301_4 # 302| m302_8(unknown) = Chi : total:m301_4, partial:m302_7 # 302| v302_9(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m301_10 @@ -1452,7 +1452,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_6 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_8 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| m303_7(unknown) = ^CallSideEffect : ~m302_8 # 303| m303_8(unknown) = Chi : total:m302_8, partial:m303_7 # 303| v303_9(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m302_11 diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 1d155eaf30d..9157b211d5b 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -334,7 +334,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| mu97_6(unknown) = ^CallSideEffect : ~m? # 97| v97_7(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m? # 97| mu97_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r97_4 @@ -386,7 +386,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| mu108_6(unknown) = ^CallSideEffect : ~m? # 108| v108_7(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m? # 108| mu108_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r108_4 @@ -450,7 +450,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| mu119_6(unknown) = ^CallSideEffect : ~m? # 119| v119_7(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m? # 119| mu119_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r119_4 @@ -789,7 +789,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_9 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m? # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m? # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -797,7 +797,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_5 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m? # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -806,7 +806,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_13 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -839,7 +839,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m? # 209| mu209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 210| r210_1(glval) = VariableAddress[#return] : @@ -917,7 +917,7 @@ ssa.cpp: # 226| mu226_2(unknown) = AliasedDefinition : # 226| mu226_3(unknown) = InitializeNonLocal : # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| mu227_3(unknown) = ^CallSideEffect : ~m? # 229| r229_1(glval) = VariableAddress[s] : # 229| r229_2(glval) = StringConstant["Literal"] : @@ -976,18 +976,18 @@ ssa.cpp: # 240| mu240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| mu240_6(unknown) = ^CallSideEffect : ~m? # 240| mu240_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? # 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? # 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 @@ -995,12 +995,12 @@ ssa.cpp: # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| mu243_6(unknown) = ^CallSideEffect : ~m? # 243| mu243_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? # 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 @@ -1027,7 +1027,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| mu248_9(unknown) = ^CallSideEffect : ~m? # 248| mu248_10(unknown) = ^InitializeDynamicAllocation : &:r248_8 # 248| r248_11(char *) = Convert : r248_8 @@ -1046,7 +1046,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_9 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m? # 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 251| r251_1(glval) = VariableAddress[#return] : @@ -1074,13 +1074,13 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| mu256_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| mu259_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 @@ -1116,7 +1116,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_9 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| mu269_6(unknown) = ^CallSideEffect : ~m? # 269| mu269_7(unknown) = ^InitializeDynamicAllocation : &:r269_5 # 269| m269_8(void *) = Store : &:r269_1, r269_5 @@ -1127,7 +1127,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_5 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_9 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m? # 270| mu270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 271| r271_1(glval) = VariableAddress[#return] : @@ -1250,7 +1250,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| mu292_5(unknown) = ^CallSideEffect : ~m? # 292| mu292_6(unknown) = ^InitializeDynamicAllocation : &:r292_4 # 292| r292_7(Point *) = Convert : r292_4 @@ -1258,7 +1258,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| mu293_5(unknown) = ^CallSideEffect : ~m? # 293| mu293_6(unknown) = ^InitializeDynamicAllocation : &:r293_4 # 293| r293_7(Point *) = Convert : r293_4 @@ -1266,24 +1266,24 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| mu294_5(unknown) = ^CallSideEffect : ~m? # 294| mu294_6(unknown) = ^InitializeDynamicAllocation : &:r294_4 # 294| r294_7(A *) = Convert : r294_4 # 294| r294_8(glval) = FunctionAddress[A] : # 294| r294_9(glval) = FunctionAddress[operator new] : # 294| r294_10(unsigned long) = Constant[4] : -# 294| r294_11(void *) = Call : func:r294_9, 0:r294_10 +# 294| r294_11(void *) = Call[operator new] : func:r294_9, 0:r294_10 # 294| mu294_12(unknown) = ^CallSideEffect : ~m? # 294| mu294_13(unknown) = ^InitializeDynamicAllocation : &:r294_11 # 294| r294_14(A *) = Convert : r294_11 # 294| r294_15(glval) = FunctionAddress[A] : # 294| r294_16(glval) = VariableAddress[x] : # 294| r294_17(int) = Load : &:r294_16, m291_5 -# 294| v294_18(void) = Call : func:r294_15, this:r294_14, 0:r294_17 +# 294| v294_18(void) = Call[A] : func:r294_15, this:r294_14, 0:r294_17 # 294| mu294_19(unknown) = ^CallSideEffect : ~m? # 294| mu294_20(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_14 -# 294| v294_21(void) = Call : func:r294_8, this:r294_7, 0:r294_14 +# 294| v294_21(void) = Call[A] : func:r294_8, this:r294_7, 0:r294_14 # 294| mu294_22(unknown) = ^CallSideEffect : ~m? # 294| mu294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_7 # 294| v294_24(void) = ^BufferReadSideEffect[0] : &:r294_14, ~m? @@ -1294,12 +1294,12 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| mu295_5(unknown) = ^CallSideEffect : ~m? # 295| mu295_6(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_7(A *) = Convert : r295_4 # 295| r295_8(glval) = FunctionAddress[A] : -# 295| v295_9(void) = Call : func:r295_8, this:r295_7 +# 295| v295_9(void) = Call[A] : func:r295_8, this:r295_7 # 295| mu295_10(unknown) = ^CallSideEffect : ~m? # 295| mu295_11(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_7 # 295| m295_12(A *) = Store : &:r295_1, r295_7 @@ -1328,7 +1328,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_5 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_7 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| mu302_7(unknown) = ^CallSideEffect : ~m? # 302| v302_8(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m? # 302| mu302_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r302_5 @@ -1337,7 +1337,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_5 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_7 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| mu303_7(unknown) = ^CallSideEffect : ~m? # 303| v303_8(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m? # 303| mu303_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r303_5 diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 1d155eaf30d..9157b211d5b 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -334,7 +334,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| mu97_6(unknown) = ^CallSideEffect : ~m? # 97| v97_7(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m? # 97| mu97_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r97_4 @@ -386,7 +386,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| mu108_6(unknown) = ^CallSideEffect : ~m? # 108| v108_7(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m? # 108| mu108_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r108_4 @@ -450,7 +450,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| mu119_6(unknown) = ^CallSideEffect : ~m? # 119| v119_7(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m? # 119| mu119_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r119_4 @@ -789,7 +789,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_9 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m? # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m? # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -797,7 +797,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_5 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m? # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -806,7 +806,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_13 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -839,7 +839,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m? # 209| mu209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 210| r210_1(glval) = VariableAddress[#return] : @@ -917,7 +917,7 @@ ssa.cpp: # 226| mu226_2(unknown) = AliasedDefinition : # 226| mu226_3(unknown) = InitializeNonLocal : # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| mu227_3(unknown) = ^CallSideEffect : ~m? # 229| r229_1(glval) = VariableAddress[s] : # 229| r229_2(glval) = StringConstant["Literal"] : @@ -976,18 +976,18 @@ ssa.cpp: # 240| mu240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| mu240_6(unknown) = ^CallSideEffect : ~m? # 240| mu240_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? # 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? # 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 @@ -995,12 +995,12 @@ ssa.cpp: # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| mu243_6(unknown) = ^CallSideEffect : ~m? # 243| mu243_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? # 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 @@ -1027,7 +1027,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| mu248_9(unknown) = ^CallSideEffect : ~m? # 248| mu248_10(unknown) = ^InitializeDynamicAllocation : &:r248_8 # 248| r248_11(char *) = Convert : r248_8 @@ -1046,7 +1046,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_9 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m? # 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 251| r251_1(glval) = VariableAddress[#return] : @@ -1074,13 +1074,13 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| mu256_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| mu259_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 @@ -1116,7 +1116,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_9 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| mu269_6(unknown) = ^CallSideEffect : ~m? # 269| mu269_7(unknown) = ^InitializeDynamicAllocation : &:r269_5 # 269| m269_8(void *) = Store : &:r269_1, r269_5 @@ -1127,7 +1127,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_5 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_9 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m? # 270| mu270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 271| r271_1(glval) = VariableAddress[#return] : @@ -1250,7 +1250,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| mu292_5(unknown) = ^CallSideEffect : ~m? # 292| mu292_6(unknown) = ^InitializeDynamicAllocation : &:r292_4 # 292| r292_7(Point *) = Convert : r292_4 @@ -1258,7 +1258,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| mu293_5(unknown) = ^CallSideEffect : ~m? # 293| mu293_6(unknown) = ^InitializeDynamicAllocation : &:r293_4 # 293| r293_7(Point *) = Convert : r293_4 @@ -1266,24 +1266,24 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| mu294_5(unknown) = ^CallSideEffect : ~m? # 294| mu294_6(unknown) = ^InitializeDynamicAllocation : &:r294_4 # 294| r294_7(A *) = Convert : r294_4 # 294| r294_8(glval) = FunctionAddress[A] : # 294| r294_9(glval) = FunctionAddress[operator new] : # 294| r294_10(unsigned long) = Constant[4] : -# 294| r294_11(void *) = Call : func:r294_9, 0:r294_10 +# 294| r294_11(void *) = Call[operator new] : func:r294_9, 0:r294_10 # 294| mu294_12(unknown) = ^CallSideEffect : ~m? # 294| mu294_13(unknown) = ^InitializeDynamicAllocation : &:r294_11 # 294| r294_14(A *) = Convert : r294_11 # 294| r294_15(glval) = FunctionAddress[A] : # 294| r294_16(glval) = VariableAddress[x] : # 294| r294_17(int) = Load : &:r294_16, m291_5 -# 294| v294_18(void) = Call : func:r294_15, this:r294_14, 0:r294_17 +# 294| v294_18(void) = Call[A] : func:r294_15, this:r294_14, 0:r294_17 # 294| mu294_19(unknown) = ^CallSideEffect : ~m? # 294| mu294_20(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_14 -# 294| v294_21(void) = Call : func:r294_8, this:r294_7, 0:r294_14 +# 294| v294_21(void) = Call[A] : func:r294_8, this:r294_7, 0:r294_14 # 294| mu294_22(unknown) = ^CallSideEffect : ~m? # 294| mu294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_7 # 294| v294_24(void) = ^BufferReadSideEffect[0] : &:r294_14, ~m? @@ -1294,12 +1294,12 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| mu295_5(unknown) = ^CallSideEffect : ~m? # 295| mu295_6(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_7(A *) = Convert : r295_4 # 295| r295_8(glval) = FunctionAddress[A] : -# 295| v295_9(void) = Call : func:r295_8, this:r295_7 +# 295| v295_9(void) = Call[A] : func:r295_8, this:r295_7 # 295| mu295_10(unknown) = ^CallSideEffect : ~m? # 295| mu295_11(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_7 # 295| m295_12(A *) = Store : &:r295_1, r295_7 @@ -1328,7 +1328,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_5 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_7 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| mu302_7(unknown) = ^CallSideEffect : ~m? # 302| v302_8(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m? # 302| mu302_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r302_5 @@ -1337,7 +1337,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_5 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_7 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| mu303_7(unknown) = ^CallSideEffect : ~m? # 303| v303_8(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m? # 303| mu303_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r303_5 diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index b5d59659460..0f8f7079b40 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -202,7 +202,7 @@ test.cpp: # 29| valnum = m29_10, r29_8 # 30| r30_1(glval) = FunctionAddress[change_global02] : # 30| valnum = unique -# 30| v30_2(void) = Call : func:r30_1 +# 30| v30_2(void) = Call[change_global02] : func:r30_1 # 30| m30_3(unknown) = ^CallSideEffect : ~m25_4 # 30| valnum = unique # 30| m30_4(unknown) = Chi : total:m25_4, partial:m30_3 @@ -538,7 +538,7 @@ test.cpp: # 77| valnum = r77_1, r79_1, r80_6 # 77| r77_2(glval) = FunctionAddress[getAValue] : # 77| valnum = unique -# 77| r77_3(int) = Call : func:r77_2 +# 77| r77_3(int) = Call[getAValue] : func:r77_2 # 77| valnum = unique # 77| m77_4(unknown) = ^CallSideEffect : ~m75_4 # 77| valnum = unique @@ -585,7 +585,7 @@ test.cpp: # 80| Block 1 # 80| r80_1(glval) = FunctionAddress[getAValue] : # 80| valnum = unique -# 80| r80_2(int) = Call : func:r80_1 +# 80| r80_2(int) = Call[getAValue] : func:r80_1 # 80| valnum = unique # 80| m80_3(unknown) = ^CallSideEffect : ~m77_5 # 80| valnum = unique diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index e16ba8ef678..7b9f2e78881 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -20,15 +20,15 @@ namespace Semmle.Autobuild.CSharp.Tests /// /// List of strings passed to FileDelete. /// - public readonly IList FileDeleteIn = new List(); + public IList FileDeleteIn { get; } = new List(); void IBuildActions.FileDelete(string file) { FileDeleteIn.Add(file); } - public readonly IList FileExistsIn = new List(); - public readonly IDictionary FileExists = new Dictionary(); + public IList FileExistsIn { get; } = new List(); + public IDictionary FileExists { get; } = new Dictionary(); bool IBuildActions.FileExists(string file) { @@ -42,12 +42,12 @@ namespace Semmle.Autobuild.CSharp.Tests throw new ArgumentException("Missing FileExists " + file); } - public readonly IList RunProcessIn = new List(); - public readonly IDictionary RunProcess = new Dictionary(); - public readonly IDictionary RunProcessOut = new Dictionary(); - public readonly IDictionary RunProcessWorkingDirectory = new Dictionary(); - public readonly HashSet CreateDirectories = new HashSet(); - public readonly HashSet<(string, string)> DownloadFiles = new HashSet<(string, string)>(); + public IList RunProcessIn { get; } = new List(); + public IDictionary RunProcess { get; } = new Dictionary(); + public IDictionary RunProcessOut { get; } = new Dictionary(); + public IDictionary RunProcessWorkingDirectory { get; } = new Dictionary(); + public HashSet CreateDirectories { get; } = new HashSet(); + public HashSet<(string, string)> DownloadFiles { get; } = new HashSet<(string, string)>(); int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env, out IList stdOut) { @@ -85,14 +85,14 @@ namespace Semmle.Autobuild.CSharp.Tests return ret; } - public readonly IList DirectoryDeleteIn = new List(); + public IList DirectoryDeleteIn { get; } = new List(); void IBuildActions.DirectoryDelete(string dir, bool recursive) { DirectoryDeleteIn.Add(dir); } - public readonly IDictionary DirectoryExists = new Dictionary(); + public IDictionary DirectoryExists { get; } = new Dictionary(); bool IBuildActions.DirectoryExists(string dir) { @@ -102,7 +102,7 @@ namespace Semmle.Autobuild.CSharp.Tests return ret; } - public readonly IDictionary GetEnvironmentVariable = new Dictionary(); + public IDictionary GetEnvironmentVariable { get; } = new Dictionary(); string? IBuildActions.GetEnvironmentVariable(string name) { @@ -112,14 +112,14 @@ namespace Semmle.Autobuild.CSharp.Tests return ret; } - public string GetCurrentDirectory = ""; + public string GetCurrentDirectory { get; set; } = ""; string IBuildActions.GetCurrentDirectory() { return GetCurrentDirectory; } - public readonly IDictionary EnumerateFiles = new Dictionary(); + public IDictionary EnumerateFiles { get; } = new Dictionary(); IEnumerable IBuildActions.EnumerateFiles(string dir) { @@ -129,7 +129,7 @@ namespace Semmle.Autobuild.CSharp.Tests return str.Split("\n").Select(p => PathCombine(dir, p)); } - public readonly IDictionary EnumerateDirectories = new Dictionary(); + public IDictionary EnumerateDirectories { get; } = new Dictionary(); IEnumerable IBuildActions.EnumerateDirectories(string dir) { @@ -141,7 +141,7 @@ namespace Semmle.Autobuild.CSharp.Tests : str.Split("\n").Select(p => PathCombine(dir, p)); } - public bool IsWindows; + public bool IsWindows { get; set; } bool IBuildActions.IsWindows() => IsWindows; @@ -164,7 +164,7 @@ namespace Semmle.Autobuild.CSharp.Tests { } - public readonly IDictionary LoadXml = new Dictionary(); + public IDictionary LoadXml { get; } = new Dictionary(); XmlDocument IBuildActions.LoadXml(string filename) { diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs index 0d62b62eb4d..b9a5c9c17c3 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs @@ -227,6 +227,6 @@ namespace Semmle.Autobuild.Shared public void DownloadFile(string address, string fileName) => DownloadFileAsync(address, fileName).Wait(); - public static readonly IBuildActions Instance = new SystemBuildActions(); + public static IBuildActions Instance { get; } = new SystemBuildActions(); } } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs index 0c07f0757a0..954befd2c05 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs @@ -232,13 +232,13 @@ namespace Semmle.Autobuild.Shared /// /// The empty build script that always returns exit code 0. /// - public static readonly BuildScript Success = Create(actions => successCode); + public static BuildScript Success { get; } = Create(actions => successCode); private const int failureCode = 1; /// /// The empty build script that always returns exit code 1. /// - public static readonly BuildScript Failure = Create(actions => failureCode); + public static BuildScript Failure { get; } = Create(actions => failureCode); private static bool Succeeded(int i) => i == successCode; diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs b/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs index 6173031ada2..9964ff5bf0c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs @@ -16,7 +16,7 @@ namespace Semmle.Extraction.CIL.Driver /// internal class AssemblyInfo { - public override string ToString() => filename; + public override string ToString() => Filename; private static AssemblyName CreateAssemblyName(MetadataReader mdReader, StringHandle name, System.Version version, StringHandle culture) { @@ -59,7 +59,7 @@ namespace Semmle.Extraction.CIL.Driver /// public AssemblyInfo(string path) { - filename = path; + Filename = path; // Attempt to open the file and see if it's a valid assembly. using var stream = File.OpenRead(path); @@ -75,9 +75,9 @@ namespace Semmle.Extraction.CIL.Driver throw new InvalidAssemblyException(); // Get our own assembly name - name = CreateAssemblyName(mdReader, mdReader.GetAssemblyDefinition()); + Name = CreateAssemblyName(mdReader, mdReader.GetAssemblyDefinition()); - references = mdReader.AssemblyReferences + References = mdReader.AssemblyReferences .Select(r => mdReader.GetAssemblyReference(r)) .Select(ar => CreateAssemblyName(mdReader, ar)) .ToArray(); @@ -91,10 +91,10 @@ namespace Semmle.Extraction.CIL.Driver } } - public readonly AssemblyName name; - public readonly string filename; - public bool extract; - public readonly AssemblyName[] references; + public AssemblyName Name { get; } + public string Filename { get; } + public bool Extract { get; set; } + public AssemblyName[] References { get; } } /// @@ -125,19 +125,19 @@ namespace Semmle.Extraction.CIL.Driver { var info = new AssemblyInfo(assemblyPath) { - extract = extractAll + Extract = extractAll }; - if (!assembliesRead.ContainsKey(info.name)) - assembliesRead.Add(info.name, info); + if (!assembliesRead.ContainsKey(info.Name)) + assembliesRead.Add(info.Name, info); } catch (InvalidAssemblyException) { } } } - public IEnumerable AssembliesToExtract => assembliesRead.Values.Where(info => info.extract); + public IEnumerable AssembliesToExtract => assembliesRead.Values.Where(info => info.Extract); - private IEnumerable AssembliesToReference => AssembliesToExtract.SelectMany(info => info.references); + private IEnumerable AssembliesToReference => AssembliesToExtract.SelectMany(info => info.References); public void ResolveReferences() { @@ -148,22 +148,22 @@ namespace Semmle.Extraction.CIL.Driver var item = assembliesToReference.Pop(); if (assembliesRead.TryGetValue(item, out var info)) { - if (!info.extract) + if (!info.Extract) { - info.extract = true; - foreach (var reference in info.references) + info.Extract = true; + foreach (var reference in info.References) assembliesToReference.Push(reference); } } else { - missingReferences.Add(item); + MissingReferences.Add(item); } } } private readonly HashSet filesAnalyzed = new HashSet(); - public readonly HashSet missingReferences = new HashSet(); + public HashSet MissingReferences {get;} = new HashSet(); } /// @@ -235,7 +235,7 @@ namespace Semmle.Extraction.CIL.Driver /// extracted. This is not an error, it just means that the database is not /// as complete as it could be. /// - public IEnumerable MissingReferences => assemblyList.missingReferences; + public IEnumerable MissingReferences => assemblyList.MissingReferences; private void ParseArgs(string[] args) { diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs b/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs index fc3c91eacec..53542c970e9 100644 --- a/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs @@ -42,7 +42,7 @@ namespace Semmle.Extraction.CIL.Driver using var logger = new ConsoleLogger(options.Verbosity); var actions = options.AssembliesToExtract - .Select(asm => asm.filename) + .Select(asm => asm.Filename) .Select(filename => () => ExtractAssembly(layout, filename, logger, options.NoCache, options.PDB, options.TrapCompression)) .ToArray(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs index 1dfd9f9daec..9718e01be80 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs @@ -38,7 +38,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// For each Payload, how many additional bytes in the bytestream need to be read. /// - internal static readonly int[] payloadSizes = { + private static readonly int[] payloadSizes = { 0, 4, 4, 1, 4, 4, 1, 1, 4, 1, 2, 4, 8, 4, 8, @@ -46,7 +46,7 @@ namespace Semmle.Extraction.CIL.Entities 4, 2, 1, 4, 2, 4 }; // Maps opcodes to payloads for each instruction. - public static readonly Dictionary opPayload = new Dictionary() + private static readonly Dictionary opPayload = new Dictionary() { { ILOpCode.Nop, Payload.None }, { ILOpCode.Break, Payload.None }, @@ -268,10 +268,10 @@ namespace Semmle.Extraction.CIL.Entities { ILOpCode.Readonly, Payload.None } }; - public readonly DefinitionMethod Method; - public readonly ILOpCode OpCode; - public readonly int Offset; - public readonly int Index; + public DefinitionMethod Method { get; } + public ILOpCode OpCode { get; } + public int Offset { get; } + public int Index { get; } private readonly int payloadValue; private readonly uint unsignedPayloadValue; diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs index b19908d4f8f..dc67a7679e8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs @@ -12,11 +12,11 @@ namespace Semmle.Extraction.CSharp.Standalone { public Extraction(string directory) { - this.directory = directory; + Directory = directory; } - public readonly string directory; - public readonly List Sources = new List(); + public string Directory { get; } + public List Sources { get; } = new List(); }; /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs index 8f992612698..c53a2c402e0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs @@ -83,7 +83,7 @@ namespace Semmle.Extraction.CSharp.Entities private class AccessorFactory : ICachedEntityFactory { - public static readonly AccessorFactory Instance = new AccessorFactory(); + public static AccessorFactory Instance { get; } = new AccessorFactory(); public Accessor Create(Context cx, IMethodSymbol init) => new Accessor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs index 67e4af5597a..ab6802cf457 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs @@ -39,7 +39,7 @@ namespace Semmle.Extraction.CSharp.Entities private class CommentBlockFactory : ICachedEntityFactory { - public static readonly CommentBlockFactory Instance = new CommentBlockFactory(); + public static CommentBlockFactory Instance { get; } = new CommentBlockFactory(); public CommentBlock Create(Context cx, ICommentBlock init) => new CommentBlock(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs index 17ad3b8a4df..ba1770f20e5 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs @@ -139,7 +139,7 @@ namespace Semmle.Extraction.CSharp.Entities private class CommentLineFactory : ICachedEntityFactory<(Microsoft.CodeAnalysis.Location, CommentLineType, string, string), CommentLine> { - public static readonly CommentLineFactory Instance = new CommentLineFactory(); + public static CommentLineFactory Instance { get; } = new CommentLineFactory(); public CommentLine Create(Context cx, (Microsoft.CodeAnalysis.Location, CommentLineType, string, string) init) => new CommentLine(cx, init.Item1, init.Item2, init.Item3, init.Item4); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 33b3fceec54..35b3f2eb808 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -147,7 +147,7 @@ namespace Semmle.Extraction.CSharp.Entities private class ConstructorFactory : ICachedEntityFactory { - public static readonly ConstructorFactory Instance = new ConstructorFactory(); + public static ConstructorFactory Instance { get; } = new ConstructorFactory(); public Constructor Create(Context cx, IMethodSymbol init) => new Constructor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs index 43a113c711d..aa125cae0a1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs @@ -28,7 +28,7 @@ namespace Semmle.Extraction.CSharp.Entities private class ConversionFactory : ICachedEntityFactory { - public static readonly ConversionFactory Instance = new ConversionFactory(); + public static ConversionFactory Instance { get; } = new ConversionFactory(); public Conversion Create(Context cx, IMethodSymbol init) => new Conversion(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs index 3b1a790c713..a5c4acc3c03 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs @@ -28,7 +28,7 @@ namespace Semmle.Extraction.CSharp.Entities private class DestructorFactory : ICachedEntityFactory { - public static readonly DestructorFactory Instance = new DestructorFactory(); + public static DestructorFactory Instance { get; } = new DestructorFactory(); public Destructor Create(Context cx, IMethodSymbol init) => new Destructor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index 044dbedbe7d..5c24cf97655 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -67,7 +67,7 @@ namespace Semmle.Extraction.CSharp.Entities private class EventFactory : ICachedEntityFactory { - public static readonly EventFactory Instance = new EventFactory(); + public static EventFactory Instance { get; } = new EventFactory(); public Event Create(Context cx, IEventSymbol init) => new Event(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs index ee2af2d5009..c75b6766dc7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs @@ -57,7 +57,7 @@ namespace Semmle.Extraction.CSharp.Entities private class EventAccessorFactory : ICachedEntityFactory { - public static readonly EventAccessorFactory Instance = new EventAccessorFactory(); + public static EventAccessorFactory Instance { get; } = new EventAccessorFactory(); public EventAccessor Create(Context cx, IMethodSymbol init) => new EventAccessor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index 6c096a11d9f..5a7a4962f3b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -20,9 +20,9 @@ namespace Semmle.Extraction.CSharp.Entities internal class Expression : FreshEntity, IExpressionParentEntity { private readonly IExpressionInfo info; - public readonly AnnotatedType Type; - public readonly Extraction.Entities.Location Location; - public readonly ExprKind Kind; + public AnnotatedType Type { get; } + public Extraction.Entities.Location Location { get; } + public ExprKind Kind { get; } internal Expression(IExpressionInfo info) : base(info.Context) @@ -294,7 +294,7 @@ namespace Semmle.Extraction.CSharp.Entities internal abstract class Expression : Expression where TExpressionSyntax : ExpressionSyntax { - public readonly TExpressionSyntax Syntax; + public TExpressionSyntax Syntax { get; } protected Expression(ExpressionNodeInfo info) : base(info) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 4426aadb8f8..2990332648d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -131,7 +131,7 @@ namespace Semmle.Extraction.CSharp.Entities private class FieldFactory : ICachedEntityFactory { - public static readonly FieldFactory Instance = new FieldFactory(); + public static FieldFactory Instance { get; } = new FieldFactory(); public Field Create(Context cx, IFieldSymbol init) => new Field(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index cff5cb0a0e5..5873d3e19f4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -100,7 +100,7 @@ namespace Semmle.Extraction.CSharp.Entities private class IndexerFactory : ICachedEntityFactory { - public static readonly IndexerFactory Instance = new IndexerFactory(); + public static IndexerFactory Instance { get; } = new IndexerFactory(); public Indexer Create(Context cx, IPropertySymbol init) => new Indexer(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs index e8820c146db..8b072a04bd9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs @@ -26,7 +26,7 @@ namespace Semmle.Extraction.CSharp.Entities private class LocalFunctionFactory : ICachedEntityFactory { - public static readonly LocalFunctionFactory Instance = new LocalFunctionFactory(); + public static LocalFunctionFactory Instance { get; } = new LocalFunctionFactory(); public LocalFunction Create(Context cx, IMethodSymbol init) => new LocalFunction(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs index 6f2f7442069..de44b16d69f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs @@ -55,7 +55,7 @@ namespace Semmle.Extraction.CSharp.Entities private class LocalVariableFactory : ICachedEntityFactory { - public static readonly LocalVariableFactory Instance = new LocalVariableFactory(); + public static LocalVariableFactory Instance { get; } = new LocalVariableFactory(); public LocalVariable Create(Context cx, ISymbol init) => new LocalVariable(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs index 6ebc9970952..4b2725b89f3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs @@ -142,7 +142,7 @@ namespace Semmle.Extraction.CSharp.Entities private class ModifierFactory : ICachedEntityFactory { - public static readonly ModifierFactory Instance = new ModifierFactory(); + public static ModifierFactory Instance { get; } = new ModifierFactory(); public Modifier Create(Context cx, string init) => new Modifier(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs index 876d457cddf..6ae31c82b64 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs @@ -38,7 +38,7 @@ namespace Semmle.Extraction.CSharp.Entities private class NamespaceFactory : ICachedEntityFactory { - public static readonly NamespaceFactory Instance = new NamespaceFactory(); + public static NamespaceFactory Instance { get; } = new NamespaceFactory(); public Namespace Create(Context cx, INamespaceSymbol init) => new Namespace(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs index 24862228c0d..dbeab838b25 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs @@ -57,7 +57,7 @@ namespace Semmle.Extraction.CSharp.Entities private class OrdinaryMethodFactory : ICachedEntityFactory { - public static readonly OrdinaryMethodFactory Instance = new OrdinaryMethodFactory(); + public static OrdinaryMethodFactory Instance { get; } = new OrdinaryMethodFactory(); public OrdinaryMethod Create(Context cx, IMethodSymbol init) => new OrdinaryMethod(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index a0fe58c2c7d..c42a3edd236 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -171,7 +171,7 @@ namespace Semmle.Extraction.CSharp.Entities private class ParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), Parameter> { - public static readonly ParameterFactory Instance = new ParameterFactory(); + public static ParameterFactory Instance { get; } = new ParameterFactory(); public Parameter Create(Context cx, (IParameterSymbol, IEntity, Parameter) init) => new Parameter(cx, init.Item1, init.Item2, init.Item3); } @@ -212,7 +212,7 @@ namespace Semmle.Extraction.CSharp.Entities private class VarargsTypeFactory : ICachedEntityFactory { - public static readonly VarargsTypeFactory Instance = new VarargsTypeFactory(); + public static VarargsTypeFactory Instance { get; } = new VarargsTypeFactory(); public VarargsType Create(Context cx, string init) => new VarargsType(cx); } @@ -247,7 +247,7 @@ namespace Semmle.Extraction.CSharp.Entities private class VarargsParamFactory : ICachedEntityFactory { - public static readonly VarargsParamFactory Instance = new VarargsParamFactory(); + public static VarargsParamFactory Instance { get; } = new VarargsParamFactory(); public VarargsParam Create(Context cx, Method init) => new VarargsParam(cx, init); } @@ -275,7 +275,7 @@ namespace Semmle.Extraction.CSharp.Entities private class ExtensionParamFactory : ICachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter> { - public static readonly ExtensionParamFactory Instance = new ExtensionParamFactory(); + public static ExtensionParamFactory Instance { get; } = new ExtensionParamFactory(); public ConstructedExtensionParameter Create(Context cx, (Method, Parameter) init) => new ConstructedExtensionParameter(cx, init.Item1, init.Item2); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index 26d354b2bb5..739708138ad 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -126,7 +126,7 @@ namespace Semmle.Extraction.CSharp.Entities private class PropertyFactory : ICachedEntityFactory { - public static readonly PropertyFactory Instance = new PropertyFactory(); + public static PropertyFactory Instance { get; } = new PropertyFactory(); public Property Create(Context cx, IPropertySymbol init) => new Property(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs index 0a81e10f173..f64067be9c4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs @@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements internal class Switch : Statement { private static readonly object nullLabel = new object(); - public static readonly object DefaultLabel = new object(); + public static object DefaultLabel { get; } = new object(); // Sometimes, the literal "null" is used as a label. // This is inconveniently represented by the "null" object. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs index 7ecadc77b89..2c617158219 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs @@ -40,7 +40,7 @@ namespace Semmle.Extraction.CSharp.Entities private class ArrayTypeFactory : ICachedEntityFactory { - public static readonly ArrayTypeFactory Instance = new ArrayTypeFactory(); + public static ArrayTypeFactory Instance { get; } = new ArrayTypeFactory(); public ArrayType Create(Context cx, IArrayTypeSymbol init) => new ArrayType(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs index c036af7f4f6..2516afe037b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs @@ -29,7 +29,7 @@ namespace Semmle.Extraction.CSharp.Entities private class DynamicTypeFactory : ICachedEntityFactory { - public static readonly DynamicTypeFactory Instance = new DynamicTypeFactory(); + public static DynamicTypeFactory Instance { get; } = new DynamicTypeFactory(); public DynamicType Create(Context cx, IDynamicTypeSymbol init) => new DynamicType(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index b394705ab41..5812c4a9303 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -176,14 +176,14 @@ namespace Semmle.Extraction.CSharp.Entities private class NamedTypeFactory : ICachedEntityFactory { - public static readonly NamedTypeFactory Instance = new NamedTypeFactory(); + public static NamedTypeFactory Instance { get; } = new NamedTypeFactory(); public NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, false); } private class UnderlyingTupleTypeFactory : ICachedEntityFactory { - public static readonly UnderlyingTupleTypeFactory Instance = new UnderlyingTupleTypeFactory(); + public static UnderlyingTupleTypeFactory Instance { get; } = new UnderlyingTupleTypeFactory(); public NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, true); } @@ -213,7 +213,7 @@ namespace Semmle.Extraction.CSharp.Entities private class NamedTypeRefFactory : ICachedEntityFactory { - public static readonly NamedTypeRefFactory Instance = new NamedTypeRefFactory(); + public static NamedTypeRefFactory Instance { get; } = new NamedTypeRefFactory(); public NamedTypeRef Create(Context cx, INamedTypeSymbol init) => new NamedTypeRef(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs index 5847587b3f2..2822e346384 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs @@ -31,7 +31,7 @@ namespace Semmle.Extraction.CSharp.Entities private class NullTypeFactory : ICachedEntityFactory { - public static readonly NullTypeFactory Instance = new NullTypeFactory(); + public static NullTypeFactory Instance { get; } = new NullTypeFactory(); public NullType Create(Context cx, ITypeSymbol init) => new NullType(cx); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs index cae2567256d..106efdd0cde 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs @@ -129,7 +129,7 @@ namespace Semmle.Extraction.CSharp.Entities private class NullabilityFactory : ICachedEntityFactory { - public static readonly NullabilityFactory Instance = new NullabilityFactory(); + public static NullabilityFactory Instance { get; } = new NullabilityFactory(); public NullabilityEntity Create(Context cx, Nullability init) => new NullabilityEntity(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs index 69ea81cc323..e2fbb8677cc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CSharp.Entities private class PointerTypeFactory : ICachedEntityFactory { - public static readonly PointerTypeFactory Instance = new PointerTypeFactory(); + public static PointerTypeFactory Instance { get; } = new PointerTypeFactory(); public PointerType Create(Context cx, IPointerTypeSymbol init) => new PointerType(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index 9d27b348f78..af9c4bf4fa6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities private class TupleTypeFactory : ICachedEntityFactory { - public static readonly TupleTypeFactory Instance = new TupleTypeFactory(); + public static TupleTypeFactory Instance { get; } = new TupleTypeFactory(); public TupleType Create(Context cx, INamedTypeSymbol init) => new TupleType(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index 8a0a2a6fc24..1b1d205b3d7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -305,7 +305,7 @@ namespace Semmle.Extraction.CSharp.Entities private class DelegateTypeParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), DelegateTypeParameter> { - public static readonly DelegateTypeParameterFactory Instance = new DelegateTypeParameterFactory(); + public static DelegateTypeParameterFactory Instance { get; } = new DelegateTypeParameterFactory(); public DelegateTypeParameter Create(Context cx, (IParameterSymbol, IEntity, Parameter) init) => new DelegateTypeParameter(cx, init.Item1, init.Item2, init.Item3); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index 0a5cea87884..b4d35f7b506 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -129,7 +129,7 @@ namespace Semmle.Extraction.CSharp.Entities private class TypeParameterFactory : ICachedEntityFactory { - public static readonly TypeParameterFactory Instance = new TypeParameterFactory(); + public static TypeParameterFactory Instance { get; } = new TypeParameterFactory(); public TypeParameter Create(Context cx, ITypeParameterSymbol init) => new TypeParameter(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs index 81bdcce1b09..d4c6fa457c1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs @@ -187,7 +187,7 @@ namespace Semmle.Extraction.CSharp.Entities private class UserOperatorFactory : ICachedEntityFactory { - public static readonly UserOperatorFactory Instance = new UserOperatorFactory(); + public static UserOperatorFactory Instance { get; } = new UserOperatorFactory(); public UserOperator Create(Context cx, IMethodSymbol init) => new UserOperator(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction/Entities/Assembly.cs index c479f9b3c30..35055541371 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Assembly.cs @@ -53,7 +53,7 @@ namespace Semmle.Extraction.Entities private class AssemblyConstructorFactory : ICachedEntityFactory { - public static readonly AssemblyConstructorFactory Instance = new AssemblyConstructorFactory(); + public static AssemblyConstructorFactory Instance { get; } = new AssemblyConstructorFactory(); public Assembly Create(Context cx, Microsoft.CodeAnalysis.Location? init) => new Assembly(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs index ef50e84f1c3..80bc3ad0533 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs @@ -78,7 +78,7 @@ namespace Semmle.Extraction.Entities private class GeneratedFileFactory : ICachedEntityFactory { - public static readonly GeneratedFileFactory Instance = new GeneratedFileFactory(); + public static GeneratedFileFactory Instance { get; } = new GeneratedFileFactory(); public GeneratedFile Create(Context cx, string? init) => new GeneratedFile(cx); } @@ -88,7 +88,7 @@ namespace Semmle.Extraction.Entities private class FileFactory : ICachedEntityFactory { - public static readonly FileFactory Instance = new FileFactory(); + public static FileFactory Instance { get; } = new FileFactory(); public File Create(Context cx, string init) => new File(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs index 2562a23828b..917d15419e8 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs @@ -28,7 +28,7 @@ namespace Semmle.Extraction.Entities private class FolderFactory : ICachedEntityFactory { - public static readonly FolderFactory Instance = new FolderFactory(); + public static FolderFactory Instance { get; } = new FolderFactory(); public Folder Create(Context cx, PathTransformer.ITransformedPath init) => new Folder(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs b/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs index 750b2cab7bf..6d05263aecd 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.Entities private class GeneratedLocationFactory : ICachedEntityFactory { - public static readonly GeneratedLocationFactory Instance = new GeneratedLocationFactory(); + public static GeneratedLocationFactory Instance { get; } = new GeneratedLocationFactory(); public GeneratedLocation Create(Context cx, string? init) => new GeneratedLocation(cx); } diff --git a/csharp/extractor/Semmle.Extraction/Entities/SourceLocation.cs b/csharp/extractor/Semmle.Extraction/Entities/SourceLocation.cs index 0ae644ab8c5..2697049dce2 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/SourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/SourceLocation.cs @@ -58,7 +58,7 @@ namespace Semmle.Extraction.Entities private class SourceLocationFactory : ICachedEntityFactory { - public static readonly SourceLocationFactory Instance = new SourceLocationFactory(); + public static SourceLocationFactory Instance { get; } = new SourceLocationFactory(); public SourceLocation Create(Context cx, Microsoft.CodeAnalysis.Location init) => new NonGeneratedSourceLocation(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction/Extractor.cs b/csharp/extractor/Semmle.Extraction/Extractor.cs index 3640d78c864..1931aeeeb33 100644 --- a/csharp/extractor/Semmle.Extraction/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction/Extractor.cs @@ -101,11 +101,6 @@ namespace Semmle.Extraction /// public class Extractor : IExtractor { - /// - /// The default number of threads to use for extraction. - /// - public static readonly int DefaultNumberOfThreads = Environment.ProcessorCount; - public bool Standalone { get; private set; diff --git a/csharp/extractor/Semmle.Extraction/Id.cs b/csharp/extractor/Semmle.Extraction/Id.cs index a5409dafaa9..3843bfb4531 100644 --- a/csharp/extractor/Semmle.Extraction/Id.cs +++ b/csharp/extractor/Semmle.Extraction/Id.cs @@ -116,7 +116,7 @@ namespace Semmle.Extraction public int Value { get; private set; } - public static readonly Label InvalidLabel = new Label(0); + public static Label InvalidLabel { get; } = new Label(0); public bool Valid => Value > 0; diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs index 99eea766c6c..4871f4c83ae 100644 --- a/csharp/extractor/Semmle.Extraction/Layout.cs +++ b/csharp/extractor/Semmle.Extraction/Layout.cs @@ -171,7 +171,7 @@ namespace Semmle.Extraction { private readonly List filePatterns = new List(); - public readonly Layout.SubProject Directories; + public Layout.SubProject Directories { get; } private static string? ReadVariable(string name, string line) { diff --git a/csharp/extractor/Semmle.Extraction/Options.cs b/csharp/extractor/Semmle.Extraction/Options.cs index 4030b2c2a95..fffe3c88b4b 100644 --- a/csharp/extractor/Semmle.Extraction/Options.cs +++ b/csharp/extractor/Semmle.Extraction/Options.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction /// /// The specified number of threads, or the default if unspecified. /// - public int Threads { get; private set; } = Extractor.DefaultNumberOfThreads; + public int Threads { get; private set; } = System.Environment.ProcessorCount; /// /// The verbosity used in output and logging. diff --git a/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll index ac284440648..21dfedd95cd 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll @@ -40,7 +40,17 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - string getLabel() { result = "" } + final string getLabel() { if alwaysPrintLabel() then result = getId() + ":" else result = "" } + + /** + * Gets an identifier that uniquely identifies this operand within its instruction. + */ + abstract string getId(); + + /** + * Holds if the operand should always be prefixed with its label in the dump of its instruction. + */ + predicate alwaysPrintLabel() { none() } } /** @@ -69,7 +79,9 @@ class AddressOperandTag extends RegisterOperandTag, TAddressOperand { final override int getSortOrder() { result = 0 } - final override string getLabel() { result = "&:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "&" } } AddressOperandTag addressOperand() { result = TAddressOperand() } @@ -82,6 +94,8 @@ class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand { final override string toString() { result = "BufferSize" } final override int getSortOrder() { result = 1 } + + final override string getId() { result = "size" } } BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() } @@ -93,6 +107,8 @@ class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand { final override string toString() { result = "SideEffect" } final override int getSortOrder() { result = 2 } + + final override string getId() { result = "side_effect" } } SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() } @@ -105,6 +121,8 @@ class LoadOperandTag extends TypedOperandTag, TLoadOperand { final override string toString() { result = "Load" } final override int getSortOrder() { result = 3 } + + final override string getId() { result = "load" } } LoadOperandTag loadOperand() { result = TLoadOperand() } @@ -116,6 +134,8 @@ class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand { final override string toString() { result = "StoreValue" } final override int getSortOrder() { result = 4 } + + final override string getId() { result = "store" } } StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() } @@ -127,6 +147,8 @@ class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand { final override string toString() { result = "Unary" } final override int getSortOrder() { result = 5 } + + final override string getId() { result = "unary" } } UnaryOperandTag unaryOperand() { result = TUnaryOperand() } @@ -138,6 +160,8 @@ class LeftOperandTag extends RegisterOperandTag, TLeftOperand { final override string toString() { result = "Left" } final override int getSortOrder() { result = 6 } + + final override string getId() { result = "left" } } LeftOperandTag leftOperand() { result = TLeftOperand() } @@ -149,6 +173,8 @@ class RightOperandTag extends RegisterOperandTag, TRightOperand { final override string toString() { result = "Right" } final override int getSortOrder() { result = 7 } + + final override string getId() { result = "right" } } RightOperandTag rightOperand() { result = TRightOperand() } @@ -160,6 +186,8 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand { final override string toString() { result = "Condition" } final override int getSortOrder() { result = 8 } + + final override string getId() { result = "cond" } } ConditionOperandTag conditionOperand() { result = TConditionOperand() } @@ -172,7 +200,9 @@ class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand { final override int getSortOrder() { result = 10 } - final override string getLabel() { result = "func:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "func" } } CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() } @@ -195,7 +225,9 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand { final override int getSortOrder() { result = 11 } - final override string getLabel() { result = "this:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "this" } } ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() } @@ -212,9 +244,11 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgume final override int getSortOrder() { result = 12 + argIndex } - final override string getLabel() { result = argIndex.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } final int getArgIndex() { result = argIndex } + + final override string getId() { result = argIndex.toString() } } PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) { @@ -228,7 +262,9 @@ class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand { final override int getSortOrder() { result = 13 } - final override string getLabel() { result = "total:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "total" } } ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() } @@ -238,7 +274,9 @@ class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand { final override int getSortOrder() { result = 14 } - final override string getLabel() { result = "partial:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "partial" } } ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() } @@ -252,7 +290,9 @@ class AsmOperandTag extends RegisterOperandTag, TAsmOperand { final override int getSortOrder() { result = 15 + index } - final override string getLabel() { result = index.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = index.toString() } } AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index e476aec60af..a12e35d471b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll index b3e3a5b1195..59dadee7154 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -50,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +221,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +241,22 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index e476aec60af..a12e35d471b 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll index b3e3a5b1195..59dadee7154 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -50,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +221,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +241,22 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll b/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll index c36bcc0dc8a..b0a6d9d98ed 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll @@ -11,10 +11,7 @@ cached private newtype TCallContext = TEmptyCallContext() or TArgNonDelegateCallContext(Expr arg) { exists(DispatchCall dc | arg = dc.getArgument(_)) } or - TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } or - TDelegateToLibraryCallableArgCallContext(DelegateArgumentToLibraryCallable arg, int i) { - exists(arg.getDelegateType().getParameter(i)) - } + TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } /** * A call context. @@ -79,31 +76,3 @@ class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateC override Location getLocation() { result = dc.getArgument(arg).getLocation() } } - -/** - * An argument of a call to a delegate supplied to a library callable, - * identified by the delegate argument itself. - * - * For example, in `x.Select(y => y)` the call to the supplied delegate - * that happens inside the library callable `Select` is not available - * in the database, so the delegate argument `y => y` is used to - * represent the call. - */ -class DelegateArgumentToLibraryCallableArgumentContext extends ArgumentCallContext, - TDelegateToLibraryCallableArgCallContext { - Expr delegate; - int arg; - - DelegateArgumentToLibraryCallableArgumentContext() { - this = TDelegateToLibraryCallableArgCallContext(delegate, arg) - } - - override predicate isArgument(Expr call, int i) { - call = delegate and - i = arg - } - - override string toString() { result = "argument " + arg + " of " + delegate.toString() } - - override Location getLocation() { result = delegate.getLocation() } -} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll index ebd9324f8f8..c1c10b9bf49 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll @@ -199,27 +199,13 @@ class CallableFlowSink extends TCallableFlowSink { /** Gets the sink of flow for call `c`, if any. */ Expr getSink(Call c) { none() } - - /** - * Gets the type of the sink for call `c`. Unlike `getSink()`, this is defined - * for all flow sink specifications. - */ - Type getSinkType(Call c) { result = this.getSink(c).getType() } } /** A flow sink specification: (method call) qualifier. */ class CallableFlowSinkQualifier extends CallableFlowSink, TCallableFlowSinkQualifier { override string toString() { result = "qualifier" } - override Expr getSink(Call c) { - result = c.getChild(-1) - or - // E.g. `new Dictionary{ {0, "a"}, {1, "b"} }` - result.(CollectionInitializer).getAnElementInitializer() = c - or - // E.g. `new Dictionary() { [0] = "a", [1] = "b" }` - result.(ObjectInitializer).getAMemberInitializer().getLValue() = c - } + override Expr getSink(Call c) { result = c.getChild(-1) } } /** A flow sink specification: return value. */ @@ -253,8 +239,6 @@ class CallableFlowSinkArg extends CallableFlowSink, TCallableFlowSinkArg { // The uses of the `i`th argument are the actual sinks none() } - - override Type getSinkType(Call c) { result = this.getArgument(c).getType() } } private predicate isCollectionType(ValueOrRefType t) { @@ -312,16 +296,6 @@ class CallableFlowSinkDelegateArg extends CallableFlowSink, TCallableFlowSinkDel // The uses of the `j`th parameter are the actual sinks none() } - - override Type getSinkType(Call c) { - result = - c - .getArgument(delegateIndex) - .(DelegateArgumentToLibraryCallable) - .getDelegateType() - .getParameter(parameterIndex) - .getType() - } } /** A specification of data flow for a library (non-source code) type. */ diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll index 75b03ac52df..928682a761e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll @@ -1018,7 +1018,7 @@ module Ssa { private predicate intraInstanceCallEdge(Callable c1, InstanceCallable c2) { exists(Call c | c.getEnclosingCallable() = c1 and - c2 = getARuntimeTarget(c) and + c2 = getARuntimeTarget(c, _) and c.(QualifiableExpr).targetIsLocalInstance() ) } @@ -1034,15 +1034,28 @@ module Ssa { * the SSA library and `Call.getARuntimeTarget()` mutually recursive), and * * (3) indirect calls to delegates via calls to library callables are included. + * + * The Boolean `libraryDelegateCall` indicates whether `c` is a call to a library + * method and the result is a delegate passed to `c`. For example, in + * + * ```csharp + * Lazy M1() + * { + * return new Lazy(M2); + * } + * ``` + * + * the constructor call `new Lazy(M2)` includes `M2` as a target. */ - Callable getARuntimeTarget(Call c) { + Callable getARuntimeTarget(Call c, boolean libraryDelegateCall) { // Non-delegate call: use dispatch library exists(DispatchCall dc | dc.getCall() = c | - result = dc.getADynamicTarget().getSourceDeclaration() + result = dc.getADynamicTarget().getSourceDeclaration() and + libraryDelegateCall = false ) or // Delegate call: use simple analysis - result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c) + result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c, libraryDelegateCall) } private module SimpleDelegateAnalysis { @@ -1055,12 +1068,14 @@ module Ssa { * Either `c` is a delegate call and `e` is the qualifier, or `c` is a call to * a library callable and `e` is a delegate argument. */ - private predicate delegateCall(Call c, Expr e) { - c = any(DelegateCall dc | e = dc.getDelegateExpr()) + private predicate delegateCall(Call c, Expr e, boolean libraryDelegateCall) { + c = any(DelegateCall dc | e = dc.getDelegateExpr()) and + libraryDelegateCall = false or c.getTarget().fromLibrary() and e = c.getAnArgument() and - e.getType() instanceof SystemLinqExpressions::DelegateExtType + e.getType() instanceof SystemLinqExpressions::DelegateExtType and + libraryDelegateCall = true } /** Holds if expression `e` is a delegate creation for callable `c` of type `t`. */ @@ -1090,7 +1105,7 @@ module Ssa { callable = call.getTarget() or callable = call.getTarget().(Method).getAnOverrider+() or callable = call.getTarget().(Method).getAnUltimateImplementor() or - callable = getARuntimeDelegateTarget(call) + callable = getARuntimeDelegateTarget(call, false) ) or pred = succ.(DelegateCreation).getArgument() @@ -1110,7 +1125,7 @@ module Ssa { } private predicate reachesDelegateCall(Expr e) { - delegateCall(_, e) + delegateCall(_, e, _) or exists(Expr mid | reachesDelegateCall(mid) | delegateFlowStep(e, mid)) } @@ -1128,12 +1143,14 @@ module Ssa { } /** Gets a run-time target for the delegate call `c`. */ - Callable getARuntimeDelegateTarget(Call c) { delegateCall(c, delegateCallSource(result)) } + Callable getARuntimeDelegateTarget(Call c, boolean libraryDelegateCall) { + delegateCall(c, delegateCallSource(result), libraryDelegateCall) + } } /** Holds if `(c1,c2)` is an edge in the call graph. */ predicate callEdge(Callable c1, Callable c2) { - exists(Call c | c.getEnclosingCallable() = c1 | getARuntimeTarget(c) = c2) + exists(Call c | c.getEnclosingCallable() = c1 and c2 = getARuntimeTarget(c, _)) } /** @@ -1179,7 +1196,7 @@ module Ssa { pragma[noinline] predicate callAt(BasicBlock bb, int i, Call call) { bb.getNode(i) = call.getAControlFlowNode() and - getARuntimeTarget(call).hasBody() + getARuntimeTarget(call, _).hasBody() } /** @@ -1201,7 +1218,7 @@ module Ssa { private predicate pruneFromLeft(Callable c) { exists(Call call, TrackedFieldOrProp f | updateCandidate(_, _, f, call) and - c = getARuntimeTarget(call) and + c = getARuntimeTarget(call, _) and generalSetter(_, f.getAssignable(), _) ) or @@ -1238,7 +1255,7 @@ module Ssa { updateCandidate(_, _, tfp, call) and fp = tfp.getAssignable() and generalSetter(_, fp, _) and - c1 = getARuntimeTarget(call) + c1 = getARuntimeTarget(call, _) } pragma[noinline] @@ -1279,7 +1296,7 @@ module Ssa { Call call, TrackedFieldOrProp tfp, Callable setter ) { updateCandidate(_, _, tfp, call) and - setsOwnFieldOrPropTransitive(getARuntimeTarget(call), tfp.getAssignable(), setter) + setsOwnFieldOrPropTransitive(getARuntimeTarget(call, _), tfp.getAssignable(), setter) } private predicate updatesNamedFieldOrPropPossiblyLive( @@ -1447,7 +1464,7 @@ module Ssa { private predicate pruneFromLeft(Callable c) { exists(Call call, CapturedWrittenLocalScopeSourceVariable v | updateCandidate(_, _, v, call) and - c = getARuntimeTarget(call) and + c = getARuntimeTarget(call, _) and relevantDefinition(_, v.getAssignable(), _) ) or @@ -1485,12 +1502,12 @@ module Ssa { pragma[noinline] private predicate updatesCapturedVariablePrefix( Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable c, - CapturedWrittenLocalScopeVariable captured + CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall ) { updateCandidate(_, _, v, call) and captured = v.getAssignable() and relevantDefinitionProj(_, captured) and - c = getARuntimeTarget(call) + c = getARuntimeTarget(call, libraryDelegateCall) } /** @@ -1505,11 +1522,13 @@ module Ssa { Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable writer, boolean additionalCalls ) { - exists(PrunedCallable c, CapturedWrittenLocalScopeVariable captured | - updatesCapturedVariablePrefix(call, v, c, captured) and + exists( + PrunedCallable c, CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall + | + updatesCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and relevantDefinitionProj(writer, captured) and ( - c = writer and additionalCalls = false + c = writer and additionalCalls = libraryDelegateCall or callEdgePrunedPlus(c, writer) and additionalCalls = true ) @@ -1667,7 +1686,7 @@ module Ssa { private predicate pruneFromLeft(Callable c) { exists(Call call, CapturedReadLocalScopeSourceVariable v | implicitReadCandidate(_, _, call.getAControlFlowNode(), v) and - c = getARuntimeTarget(call) + c = getARuntimeTarget(call, _) ) or exists(Callable mid | pruneFromLeft(mid) | callEdge(mid, c)) @@ -1702,12 +1721,12 @@ module Ssa { pragma[noinline] private predicate readsCapturedVariablePrefix( ControlFlow::Node call, CapturedReadLocalScopeSourceVariable v, PrunedCallable c, - CapturedReadLocalScopeVariable captured + CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall ) { implicitReadCandidate(_, _, call, v) and captured = v.getAssignable() and capturerReads(_, captured) and - c = getARuntimeTarget(call.getElement()) + c = getARuntimeTarget(call.getElement(), libraryDelegateCall) } /** @@ -1722,11 +1741,13 @@ module Ssa { ControlFlow::Nodes::ElementNode call, CapturedReadLocalScopeSourceVariable v, Callable reader, boolean additionalCalls ) { - exists(PrunedCallable c, CapturedReadLocalScopeVariable captured | - readsCapturedVariablePrefix(call, v, c, captured) and + exists( + PrunedCallable c, CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall + | + readsCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and capturerReads(reader, captured) and ( - c = reader and additionalCalls = false + c = reader and additionalCalls = libraryDelegateCall or callEdgePrunedPlus(c, reader) and additionalCalls = true ) @@ -1765,7 +1786,6 @@ module Ssa { def.getSourceVariable().getAssignable() = lsv | lsv = v.getAssignable() and - adef = def.getAPossibleDefinition() and bb.getNode(i) = adef.getAControlFlowNode() and updatesCapturedVariable(def.getCall(), _, adef, additionalCalls) ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index 25ca28ea789..08d099961d0 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -3,18 +3,24 @@ private import cil private import dotnet private import DataFlowPrivate private import DelegateDataFlow +private import semmle.code.csharp.dataflow.LibraryTypeDataFlow private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.frameworks.system.Collections private import semmle.code.csharp.frameworks.system.collections.Generic /** - * Gets a source declaration of callable `c` that has a body. + * Gets a source declaration of callable `c` that has a body or has + * a flow summary. + * * If the callable has both CIL and source code, return only the source * code version. */ DotNet::Callable getCallableForDataFlow(DotNet::Callable c) { - result.hasBody() and exists(DotNet::Callable sourceDecl | sourceDecl = c.getSourceDeclaration() | + result = sourceDecl and + Summaries::summary(result, _, _, _, _, _) + or + result.hasBody() and if sourceDecl.getFile().fromSource() then // C# callable with C# implementation in the database @@ -83,7 +89,8 @@ private module Cached { exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | v = def.getSourceVariable().getAssignable() ) - } + } or + TQualifierReturnKind() cached newtype TDataFlowCall = @@ -93,15 +100,23 @@ private module Cached { TExplicitDelegateCall(ControlFlow::Nodes::ElementNode cfn, DelegateCall dc) { cfn.getElement() = dc } or - TImplicitDelegateCall(ControlFlow::Nodes::ElementNode cfn, DelegateArgumentToLibraryCallable arg) { - cfn.getElement() = arg - } or TTransitiveCapturedCall(ControlFlow::Nodes::ElementNode cfn, Callable target) { transitiveCapturedCallTarget(cfn, target) } or TCilCall(CIL::Call call) { // No need to include calls that are compiled from source not call.getImplementation().getMethod().compiledFromSource() + } or + TSummaryDelegateCall(SourceDeclarationCallable c, int pos) { + exists(CallableFlowSourceDelegateArg source | + Summaries::summary(c, source, _, _, _, _) and + pos = source.getArgumentIndex() + ) + or + exists(CallableFlowSinkDelegateArg sink | + Summaries::summary(c, _, _, sink, _, _) and + pos = sink.getDelegateIndex() + ) } /** Gets a viable run-time target for the call `call`. */ @@ -118,7 +133,7 @@ private module DispatchImpl { * Gets a viable run-time target for the delegate call `call`, requiring * call context `cc`. */ - private DotNet::Callable viableDelegateCallable(DataFlowCall call, CallContext cc) { + private DataFlowCallable viableDelegateCallable(DataFlowCall call, CallContext cc) { result = call.(DelegateDataFlowCall).getARuntimeTarget(cc) } @@ -143,7 +158,7 @@ private module DispatchImpl { * Gets a viable dispatch target of `call` in the context `ctx`. This is * restricted to those `call`s for which a context might make a difference. */ - DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) | cc.isArgument(ctx.getExpr(), _) ) @@ -153,6 +168,7 @@ private module DispatchImpl { .(NonDelegateDataFlowCall) .getDispatchCall() .getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall()) + .getSourceDeclaration() } } @@ -218,6 +234,11 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind override string toString() { result = "captured " + v } } +/** A value returned through the qualifier of a call. */ +class QualifierReturnKind extends ReturnKind, TQualifierReturnKind { + override string toString() { result = "qualifier" } +} + class DataFlowCallable = DotNet::Callable; /** A call relevant for data flow. */ @@ -261,7 +282,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { /** Gets the underlying call. */ DispatchCall getDispatchCall() { result = dc } - override DotNet::Callable getARuntimeTarget() { + override DataFlowCallable getARuntimeTarget() { result = getCallableForDataFlow(dc.getADynamicTarget()) } @@ -279,9 +300,9 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { /** A delegate call relevant for data flow. */ abstract class DelegateDataFlowCall extends DataFlowCall { /** Gets a viable run-time target of this call requiring call context `cc`. */ - abstract DotNet::Callable getARuntimeTarget(CallContext::CallContext cc); + abstract DataFlowCallable getARuntimeTarget(CallContext::CallContext cc); - override DotNet::Callable getARuntimeTarget() { result = this.getARuntimeTarget(_) } + override DataFlowCallable getARuntimeTarget() { result = this.getARuntimeTarget(_) } } /** An explicit delegate call relevant for data flow. */ @@ -291,8 +312,8 @@ class ExplicitDelegateDataFlowCall extends DelegateDataFlowCall, TExplicitDelega ExplicitDelegateDataFlowCall() { this = TExplicitDelegateCall(cfn, dc) } - override DotNet::Callable getARuntimeTarget(CallContext::CallContext cc) { - result = dc.getARuntimeTarget(cc) + override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) { + result = getCallableForDataFlow(dc.getARuntimeTarget(cc)) } override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn } @@ -306,50 +327,6 @@ class ExplicitDelegateDataFlowCall extends DelegateDataFlowCall, TExplicitDelega override Location getLocation() { result = cfn.getLocation() } } -/** - * An implicit delegate call in a call to a library method. For example, we add - * an implicit call to `M` in `new Lazy(M)` (although, technically, the delegate - * would first be called when accessing the `Value` property). - */ -class ImplicitDelegateDataFlowCall extends DelegateDataFlowCall, TImplicitDelegateCall { - private ControlFlow::Nodes::ElementNode cfn; - private DelegateArgumentToLibraryCallable arg; - - ImplicitDelegateDataFlowCall() { this = TImplicitDelegateCall(cfn, arg) } - - /** - * Holds if the underlying delegate argument is the `i`th argument of the - * call `c` targeting a library callable. For example, `M` is the `0`th - * argument of `new Lazy(M)`. - */ - predicate isArgumentOf(NonDelegateDataFlowCall c, int i) { - exists(ImplicitDelegateOutNode out | out.getControlFlowNode() = cfn | out.isArgumentOf(c, i)) - } - - /** Gets the number of parameters of the supplied delegate. */ - int getNumberOfDelegateParameters() { result = arg.getDelegateType().getNumberOfParameters() } - - /** Gets the type of the `i`th parameter of the supplied delegate. */ - Type getDelegateParameterType(int i) { result = arg.getDelegateType().getParameter(i).getType() } - - /** Gets the return type of the supplied delegate. */ - Type getDelegateReturnType() { result = arg.getDelegateType().getReturnType() } - - override DotNet::Callable getARuntimeTarget(CallContext::CallContext cc) { - result = arg.getARuntimeTarget(cc) - } - - override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn } - - override ImplicitDelegateOutNode getNode() { result.getControlFlowNode() = cfn } - - override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() } - - override string toString() { result = "[implicit call] " + cfn.toString() } - - override Location getLocation() { result = cfn.getLocation() } -} - /** * A call that can reach a callable, using one or more additional calls, which * reads or updates a captured variable. We model such a chain of calls as just @@ -380,7 +357,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall { CilDataFlowCall() { this = TCilCall(call) } - override DotNet::Callable getARuntimeTarget() { + override DataFlowCallable getARuntimeTarget() { // There is no dispatch library for CIL, so do not consider overrides for now result = getCallableForDataFlow(call.getTarget()) } @@ -395,3 +372,34 @@ class CilDataFlowCall extends DataFlowCall, TCilCall { override Location getLocation() { result = call.getLocation() } } + +/** + * A delegate call inside a callable with a flow summary. + * + * For example, in `ints.Select(i => i + 1)` there is a call to the delegate at + * parameter position `1` (counting the qualifier as the `0`th argument) inside + * the method `Select`. + */ +class SummaryDelegateCall extends DelegateDataFlowCall, TSummaryDelegateCall { + private SourceDeclarationCallable c; + private int pos; + + SummaryDelegateCall() { this = TSummaryDelegateCall(c, pos) } + + override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) { + exists(SummaryDelegateParameterSink p | + p = TSummaryParameterNode(c, pos) and + result = p.getARuntimeTarget(cc) + ) + } + + override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() } + + override DataFlow::Node getNode() { none() } + + override Callable getEnclosingCallable() { result = c } + + override string toString() { result = "[summary] delegate call, parameter " + pos + " of " + c } + + override Location getLocation() { result = c.getLocation() } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 8c210edbe5f..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,21 +1524,60 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** - * 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 - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, 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 `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1588,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1607,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1631,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1655,181 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1856,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1877,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1888,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1918,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1936,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration 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)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2111,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2128,34 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,6 +2190,113 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2096,13 +2304,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2322,175 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. 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 + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2323,12 +2694,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2369,22 +2740,23 @@ private predicate pathStoreStep( } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2767,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2779,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2790,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2444,9 +2816,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2477,7 +2851,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2861,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2869,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2507,9 +2884,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index cb333136a48..bae73a6609d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -17,6 +17,7 @@ private import semmle.code.csharp.frameworks.EntityFramework private import semmle.code.csharp.frameworks.NHibernate private import semmle.code.csharp.frameworks.system.Collections private import semmle.code.csharp.frameworks.system.threading.Tasks +private import semmle.code.csharp.frameworks.system.linq.Expressions abstract class NodeImpl extends Node { /** Do not call: use `getEnclosingCallable()` instead. */ @@ -321,10 +322,8 @@ module LocalFlow { * inter-procedurality or field-sensitivity. */ predicate excludeFromExposedRelations(Node n) { - n instanceof LibraryCodeNode or - n instanceof ImplicitCapturedArgumentNode or - n instanceof ImplicitDelegateOutNode or - n instanceof ImplicitDelegateArgumentNode + n instanceof Summaries::SummaryNodeImpl or + n instanceof ImplicitCapturedArgumentNode } } @@ -396,8 +395,8 @@ private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, bool f.isFieldLike() and f instanceof InstanceFieldOrProperty or - exists(LibraryFlow::AdjustedAccessPath ap | - LibraryFlow::libraryFlowSummary(_, _, ap, _, _, _) and + exists(AccessPath ap | + Summaries::summary(_, _, ap, _, _, _) and ap.contains(f.getContent()) ) ) @@ -441,8 +440,8 @@ private predicate fieldOrPropertyRead(Expr e1, Content c, FieldOrPropertyRead e2 ret.isFieldLike() and ret = e2.getTarget() or - exists(LibraryFlow::AdjustedAccessPath ap, Property target | - LibraryFlow::libraryFlowSummary(_, _, _, _, ap, _) and + exists(AccessPath ap, Property target | + Summaries::summary(_, _, _, _, ap, _) and ap.contains(ret.getContent()) and target.getGetter() = e2.(PropertyCall).getARuntimeTarget() and overridesOrImplementsSourceDecl(target, ret) @@ -520,7 +519,7 @@ private Gvn::GvnType getANonTypeParameterSubType(DataFlowTypeOrUnifiable t) { /** A collection of cached types and predicates to be evaluated in the same stage. */ cached private module Cached { - private import LibraryFlow + private import Summaries cached newtype TNode = @@ -539,20 +538,6 @@ private module Cached { v = def.getSourceVariable().getAssignable() ) } or - TImplicitDelegateOutNode( - ControlFlow::Nodes::ElementNode cfn, ControlFlow::Nodes::ElementNode call - ) { - cfn.getElement() instanceof DelegateArgumentToLibraryCallable and - any(DelegateArgumentConfiguration x).hasExprPath(_, cfn, _, call) - } or - TImplicitDelegateArgumentNode(ControlFlow::Nodes::ElementNode cfn, int i, int j) { - exists(Call call, CallableFlowSinkDelegateArg sink | - libraryFlowSummary(call, _, _, sink, _, _) and - i = sink.getDelegateIndex() and - j = sink.getDelegateParameterIndex() and - call.getArgument(i).getAControlFlowNode() = cfn - ) - } or TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation } or TObjectInitializerNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement().(ObjectCreation).hasInitializer() @@ -578,18 +563,48 @@ private module Cached { cfn.getElement() = fla.getQualifier() ) } or - TLibraryCodeNode( - ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp, - CallableFlowSink sink, AdjustedAccessPath sinkAp, boolean preservesValue, - LibraryCodeNodeState state - ) { - libraryFlowSummary(callCfn.getElement(), source, sourceAp, sink, sinkAp, preservesValue) and - ( - state = TLibraryCodeNodeAfterReadState(sourceAp.drop(_)) and - (sourceAp.length() > 1 or sinkAp.length() > 0 or preservesValue = false) + TSummaryParameterNode(SourceDeclarationCallable c, int i) { + exists(CallableFlowSource source | Summaries::summary(c, source, _, _, _, _) | + source instanceof CallableFlowSourceQualifier and i = -1 or - state = TLibraryCodeNodeBeforeStoreState(sinkAp.drop(_)) and - (sinkAp.length() > 1 or sourceAp.length() > 0 or preservesValue = false) + i = source.(CallableFlowSourceArg).getArgumentIndex() + or + i = source.(CallableFlowSourceDelegateArg).getArgumentIndex() + ) + or + exists(CallableFlowSink sink | Summaries::summary(c, _, _, sink, _, _) | + i = sink.(CallableFlowSinkDelegateArg).getDelegateIndex() + ) + } or + TSummaryInternalNode( + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue, + SummaryInternalNodeState state + ) { + Summaries::summary(c, source, sourceAp, sink, sinkAp, preservesValue) and + ( + state = TSummaryInternalNodeAfterReadState(sourceAp.drop(_)) + or + state = TSummaryInternalNodeBeforeStoreState(sinkAp.drop(_)) + ) + } or + TSummaryReturnNode(SourceDeclarationCallable c, ReturnKind rk) { + exists(CallableFlowSink sink | + Summaries::summary(c, _, _, sink, _, _) and + rk = Summaries::toReturnKind(sink) + ) + } or + TSummaryDelegateOutNode(SourceDeclarationCallable c, int pos) { + exists(CallableFlowSourceDelegateArg source | + Summaries::summary(c, source, _, _, _, _) and + pos = source.getArgumentIndex() + ) + } or + TSummaryDelegateArgumentNode(SourceDeclarationCallable c, int delegateIndex, int parameterIndex) { + exists(CallableFlowSinkDelegateArg sink | + Summaries::summary(c, _, _, sink, _, _) and + delegateIndex = sink.getDelegateIndex() and + parameterIndex = sink.getDelegateParameterIndex() ) } or TParamsArgumentNode(ControlFlow::Node callCfn) { @@ -608,7 +623,7 @@ private module Cached { or LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) or - LibraryFlow::localStepLibrary(nodeFrom, nodeTo, true) + Summaries::summaryLocalStep(nodeFrom, nodeTo, true) or nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) } @@ -628,9 +643,7 @@ private module Cached { or // Simple flow through library code is included in the exposed local // step relation, even though flow is technically inter-procedural - LibraryFlow::localStepLibrary(nodeFrom, nodeTo, true) and - not LocalFlow::excludeFromExposedRelations(nodeFrom) and - not LocalFlow::excludeFromExposedRelations(nodeTo) + Summaries::summaryThroughStep(nodeFrom, nodeTo, true) } /** @@ -677,7 +690,7 @@ private module Cached { c instanceof ElementContent ) or - storeStepLibrary(node1, c, node2) + summaryStoreStep(node1, c, node2) } pragma[nomagic] @@ -711,7 +724,7 @@ private module Cached { c = getResultContent() ) or - readStepLibrary(node1, c, node2) + summaryReadStep(node1, c, node2) } /** @@ -725,10 +738,10 @@ private module Cached { or fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) or - storeStepLibrary(n, c, _) and + summaryStoreStep(n, c, _) and not c instanceof ElementContent or - clearsContentLibrary(n, c) + summaryClearsContent(n, c) } /** @@ -784,6 +797,14 @@ private module Cached { ) } + cached + predicate qualifierOutNode(DataFlowCall call, Node n) { + n.(ExprPostUpdateNode).getPreUpdateNode().(ExplicitArgumentNode).argumentOf(call, -1) + or + any(ObjectOrCollectionInitializerConfiguration x) + .hasExprPath(_, n.(ExprNode).getControlFlowNode(), _, call.getControlFlowNode()) + } + cached predicate castNode(Node n) { n.asExpr() instanceof Cast @@ -806,13 +827,9 @@ private module Cached { or n instanceof ImplicitCapturedArgumentNode or - n instanceof ImplicitDelegateOutNode - or - n instanceof ImplicitDelegateArgumentNode - or n instanceof MallocNode or - n instanceof LibraryCodeNode + n instanceof Summaries::SummaryNodeImpl or n instanceof ParamsArgumentNode } @@ -861,7 +878,8 @@ private module ParameterNodes { private DotNet::Parameter parameter; ExplicitParameterNode() { - explicitParameterNode(this, parameter) or + explicitParameterNode(this, parameter) + or this = TCilParameterNode(parameter) } @@ -869,7 +887,7 @@ private module ParameterNodes { override predicate isParameterOf(DataFlowCallable c, int i) { c.getParameter(i) = parameter } - override DotNet::Callable getEnclosingCallableImpl() { result = parameter.getCallable() } + override DataFlowCallable getEnclosingCallableImpl() { result = parameter.getCallable() } override DotNet::Type getTypeImpl() { result = parameter.getType() } @@ -968,11 +986,52 @@ private module ParameterNodes { c = this.getEnclosingCallable() } } + + /** A parameter node for a callable with a flow summary. */ + class SummaryParameterNode extends ParameterNodeImpl, Summaries::SummaryNodeImpl, + TSummaryParameterNode { + private SourceDeclarationCallable sdc; + private int i; + + SummaryParameterNode() { this = TSummaryParameterNode(sdc, i) } + + override Parameter getParameter() { result = sdc.getParameter(i) } + + override predicate isParameterOf(DataFlowCallable c, int pos) { + c = sdc and + pos = i + } + + override Callable getEnclosingCallableImpl() { result = sdc } + + override Type getTypeImpl() { + result = sdc.getParameter(i).getType() + or + i = -1 and + result = sdc.getDeclaringType() + } + + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { + result = sdc.getParameter(i).getLocation() + or + i = -1 and + result = sdc.getLocation() + } + + override string toStringImpl() { + result = "[summary] " + sdc.getParameter(i) + or + i = -1 and + result = "[summary] this" + } + } } import ParameterNodes -/** A data flow node that represents a call argument. */ +/** A data-flow node that represents a call argument. */ abstract class ArgumentNode extends Node { /** Holds if this argument occurs at the given position in the given call. */ cached @@ -983,21 +1042,6 @@ abstract class ArgumentNode extends Node { } private module ArgumentNodes { - class DelegateArgumentConfiguration extends ControlFlowReachabilityConfiguration { - DelegateArgumentConfiguration() { this = "DelegateArgumentConfiguration" } - - override predicate candidate( - Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor - ) { - exists(DelegateArgumentToLibraryCallable arg | e1 = arg.getCall().getAnArgument() | - e2 = arg.getCall() and - scope = e2 and - exactScope = false and - isSuccessor = true - ) - } - } - private class ArgumentConfiguration extends ControlFlowReachabilityConfiguration { ArgumentConfiguration() { this = "ArgumentConfiguration" } @@ -1011,8 +1055,8 @@ private module ArgumentNodes { } } - /** A data flow node that represents an explicit call argument. */ - private class ExplicitArgumentNode extends ArgumentNode { + /** A data-flow node that represents an explicit call argument. */ + class ExplicitArgumentNode extends ArgumentNode { ExplicitArgumentNode() { this.asExpr() instanceof Argument or @@ -1071,22 +1115,11 @@ private module ArgumentNodes { override predicate argumentOf(DataFlowCall call, int pos) { exists(ImplicitCapturedParameterNode p, boolean additionalCalls | this.flowsInto(p, additionalCalls) and - p.isParameterOf(call.getARuntimeTarget(), pos) - | + p.isParameterOf(call.getARuntimeTarget(), pos) and + call.getControlFlowNode() = cfn and if call instanceof TransitiveCapturedDataFlowCall - then additionalCalls = true and call.getControlFlowNode() = cfn - else ( - additionalCalls = false and - ( - call.getControlFlowNode() = cfn - or - exists(DataFlowCall parent | - call.(ImplicitDelegateDataFlowCall).isArgumentOf(parent, _) - | - parent.getControlFlowNode() = cfn - ) - ) - ) + then additionalCalls = true + else additionalCalls = false ) } @@ -1127,46 +1160,7 @@ private module ArgumentNodes { } /** - * A data flow node that represents an implicit argument of an implicit delegate - * call in library code. For example, in - * - * ```csharp - * x.Select(Foo); - * ``` - * - * `x` is an implicit argument of the implicit call to `Foo`. - */ - class ImplicitDelegateArgumentNode extends ArgumentNode, NodeImpl, TImplicitDelegateArgumentNode { - private ControlFlow::Node cfn; - private int delegateIndex; - private int parameterIndex; - - ImplicitDelegateArgumentNode() { - this = TImplicitDelegateArgumentNode(cfn, delegateIndex, parameterIndex) - } - - private ImplicitDelegateDataFlowCall getDelegateCall() { result.getControlFlowNode() = cfn } - - override predicate argumentOf(DataFlowCall call, int pos) { - call = this.getDelegateCall() and - pos = parameterIndex - } - - override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } - - override Type getTypeImpl() { - result = this.getDelegateCall().getDelegateParameterType(parameterIndex) - } - - override ControlFlow::Node getControlFlowNodeImpl() { none() } - - override Location getLocationImpl() { result = cfn.getLocation() } - - override string toStringImpl() { result = "[implicit argument " + parameterIndex + "] " + cfn } - } - - /** - * A data flow node that represents the implicit array creation in a call to a + * A data-flow node that represents the implicit array creation in a call to a * callable with a `params` parameter. For example, there is an implicit array * creation `new [] { "a", "b", "c" }` in * @@ -1203,11 +1197,55 @@ private module ArgumentNodes { override string toStringImpl() { result = "[implicit array creation] " + callCfn } } + + /** + * An argument node inside a callable with a flow summary, where the argument is + * passed to a supplied delegate. For example, in `ints.Select(Foo)` there is a + * node that represents the argument of the call to `Foo` inside `Select`. + */ + class SummaryDelegateArgumentNode extends ArgumentNode, Summaries::SummaryNodeImpl, + TSummaryDelegateArgumentNode { + private SourceDeclarationCallable c; + private int delegateIndex; + private int parameterIndex; + + SummaryDelegateArgumentNode() { + this = TSummaryDelegateArgumentNode(c, delegateIndex, parameterIndex) + } + + override DataFlowCallable getEnclosingCallableImpl() { result = c } + + override DotNet::Type getTypeImpl() { + result = + c + .getParameter(delegateIndex) + .getType() + .(SystemLinqExpressions::DelegateExtType) + .getDelegateType() + .getParameter(parameterIndex) + .getType() + } + + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { result = c.getLocation() } + + override string toStringImpl() { + result = + "[summary] argument " + parameterIndex + " of delegate call, parameter " + parameterIndex + + " of " + c + } + + override predicate argumentOf(DataFlowCall call, int pos) { + call = TSummaryDelegateCall(c, delegateIndex) and + pos = parameterIndex + } + } } import ArgumentNodes -/** A data flow node that represents a value returned by a callable. */ +/** A data-flow node that represents a value returned by a callable. */ abstract class ReturnNode extends Node { /** Gets the kind of this return node. */ abstract ReturnKind getKind(); @@ -1215,7 +1253,7 @@ abstract class ReturnNode extends Node { private module ReturnNodes { /** - * A data flow node that represents an expression returned by a callable, + * A data-flow node that represents an expression returned by a callable, * either using a (`yield`) `return` statement or an expression body (`=>`). */ class ExprReturnNode extends ReturnNode, ExprNode { @@ -1233,7 +1271,7 @@ private module ReturnNodes { } /** - * A data flow node that represents an assignment to an `out` or a `ref` + * A data-flow node that represents an assignment to an `out` or a `ref` * parameter. */ class OutRefReturnNode extends ReturnNode, SsaDefinitionNode { @@ -1308,11 +1346,39 @@ private module ReturnNodes { result.getVariable() = edef.getSourceVariable().getAssignable() } } + + /** A return node for a callable with a flow summary. */ + class SummaryReturnNode extends ReturnNode, Summaries::SummaryNodeImpl, TSummaryReturnNode { + private SourceDeclarationCallable sdc; + private ReturnKind rk; + + SummaryReturnNode() { this = TSummaryReturnNode(sdc, rk) } + + override Callable getEnclosingCallableImpl() { result = sdc } + + override DotNet::Type getTypeImpl() { + rk instanceof NormalReturnKind and + result in [sdc.getReturnType(), sdc.(Constructor).getDeclaringType()] + or + rk instanceof QualifierReturnKind and + result = sdc.getDeclaringType() + or + result = sdc.getParameter(rk.(OutRefReturnKind).getPosition()).getType() + } + + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { result = sdc.getLocation() } + + override string toStringImpl() { result = "[summary] return of kind " + rk + " inside " + sdc } + + override ReturnKind getKind() { result = rk } + } } import ReturnNodes -/** A data flow node that represents the output of a call. */ +/** A data-flow node that represents the output of a call. */ abstract class OutNode extends Node { /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ cached @@ -1344,7 +1410,7 @@ private module OutNodes { } /** - * A data flow node that reads a value returned directly by a callable, + * A data-flow node that reads a value returned directly by a callable, * either via a C# call or a CIL call. */ class ExprOutNode extends OutNode, ExprNode { @@ -1370,8 +1436,44 @@ private module OutNodes { } } + class ObjectOrCollectionInitializerConfiguration extends ControlFlowReachabilityConfiguration { + ObjectOrCollectionInitializerConfiguration() { + this = "ObjectOrCollectionInitializerConfiguration" + } + + override predicate candidate( + Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor + ) { + exactScope = false and + scope = e1 and + isSuccessor = false and + ( + // E.g. `new Dictionary{ {0, "a"}, {1, "b"} }` + e1.(CollectionInitializer).getAnElementInitializer() = e2 + or + // E.g. `new Dictionary() { [0] = "a", [1] = "b" }` + e1.(ObjectInitializer).getAMemberInitializer().getLValue() = e2 + ) + } + } + /** - * A data flow node that reads a value returned implicitly by a callable + * A data-flow node that contains a value returned by a callable, by writing + * to the qualifier of the call. + */ + private class QualifierOutNode extends OutNode, Node { + private DataFlowCall call; + + QualifierOutNode() { qualifierOutNode(call, this) } + + override DataFlowCall getCall(ReturnKind kind) { + result = call and + kind instanceof QualifierReturnKind + } + } + + /** + * A data-flow node that reads a value returned implicitly by a callable * using a captured variable. */ class CapturedOutNode extends OutNode, SsaDefinitionNode { @@ -1384,10 +1486,8 @@ private module OutNodes { | additionalCalls = false and call = csharpCall(_, cfn) or - additionalCalls = false and - call.(ImplicitDelegateDataFlowCall).isArgumentOf(csharpCall(_, cfn), _) - or - additionalCalls = true and call = TTransitiveCapturedCall(cfn, n.getEnclosingCallable()) + additionalCalls = true and + call = TTransitiveCapturedCall(cfn, n.getEnclosingCallable()) ) } @@ -1399,7 +1499,7 @@ private module OutNodes { } /** - * A data flow node that reads a value returned by a callable using an + * A data-flow node that reads a value returned by a callable using an * `out` or `ref` parameter. */ class ParamOutNode extends OutNode, AssignableDefinitionNode { @@ -1418,566 +1518,219 @@ private module OutNodes { } /** - * A data flow node that represents the output of an implicit delegate call, - * in a call to a library method. For example, the output from the implicit - * call to `M` in `new Lazy(M)`. + * An output node inside a callable with a flow summary, where the output is the + * result of calling a supplied delegate. For example, in `ints.Select(Foo)` there + * is a node that represents the output of calling `Foo` inside `Select`. */ - class ImplicitDelegateOutNode extends OutNode, NodeImpl, TImplicitDelegateOutNode { - private ControlFlow::Nodes::ElementNode cfn; - private ControlFlow::Nodes::ElementNode call; + private class SummaryDelegateOutNode extends OutNode, Summaries::SummaryNodeImpl, + TSummaryDelegateOutNode { + private SourceDeclarationCallable c; + private int pos; - ImplicitDelegateOutNode() { this = TImplicitDelegateOutNode(cfn, call) } + SummaryDelegateOutNode() { this = TSummaryDelegateOutNode(c, pos) } - /** - * Holds if the underlying delegate argument is the `i`th argument of the - * call `c` targeting a library callable. For example, `M` is the `0`th - * argument of `new Lazy(M)`. - */ - predicate isArgumentOf(DataFlowCall c, int i) { - c.getControlFlowNode() = call and - call.getElement().(Call).getArgument(i) = cfn.getElement() + override Callable getEnclosingCallableImpl() { result = c } + + override DotNet::Type getTypeImpl() { + result = + c + .getParameter(pos) + .getType() + .(SystemLinqExpressions::DelegateExtType) + .getDelegateType() + .getReturnType() } - override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { result = cfn } + override ControlFlow::Node getControlFlowNodeImpl() { none() } - override ImplicitDelegateDataFlowCall getCall(ReturnKind kind) { - result.getNode() = this and - ( - kind instanceof NormalReturnKind and - not result.getDelegateReturnType() instanceof VoidType - or - kind instanceof YieldReturnKind and - result.getDelegateReturnType() instanceof YieldReturnType - ) + override Location getLocationImpl() { result = c.getLocation() } + + override string toStringImpl() { + result = "[summary] output from delegate call, parameter " + pos + " of " + c + "]" } - override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } - - override Type getTypeImpl() { - exists(ImplicitDelegateDataFlowCall c | c.getNode() = this | - result = c.getDelegateReturnType() - ) + override SummaryDelegateCall getCall(ReturnKind kind) { + result = TSummaryDelegateCall(c, pos) and + kind instanceof NormalReturnKind } - - override Location getLocationImpl() { result = cfn.getLocation() } - - override string toStringImpl() { result = "[output] " + cfn } } } import OutNodes /** - * Provides predicates related to flow through library code, based on - * the flow summaries in `LibraryTypeDataFlow.qll`. + * Provides predicates for interpreting flow summaries defined in + * `LibraryTypeDataFlow.qll`. */ -module LibraryFlow { - pragma[nomagic] - private ValueOrRefType getPreciseSourceProperty0( - Call call, CallableFlowSource source, AccessPath sourceAp, Property p, AccessPath sourceApTail - ) { - exists(LibraryTypeDataFlow ltdf, Property p0 | - ltdf.callableFlow(source, sourceAp, _, _, call.getTarget().getSourceDeclaration(), _) and - sourceAp.getHead().(PropertyContent).getProperty() = p0 and - sourceAp.getTail() = sourceApTail and - overridesOrImplementsSourceDecl(p, p0) and - result = source.getSourceType(call) - ) - } - - /** - * Gets a precise source property for source `source` and access path `sourceAp`, - * in the context of `call`. For example, in - * - * ```csharp - * var list = new List(); - * var count = list.Count(); - * ``` - * - * the step from `list` to `list.Count()`, which may be modeled as a read of - * the `Count` property from `ICollection`, can be strengthened to be a - * read of the `Count` property from `List`. - */ - pragma[nomagic] - private Property getPreciseSourceProperty( - Call call, CallableFlowSource source, AccessPath sourceAp, AccessPath sourceApTail - ) { - getPreciseSourceProperty0(call, source, sourceAp, result, sourceApTail).hasMember(result) - } - - pragma[nomagic] - private ValueOrRefType getPreciseSinkProperty0( - Call call, CallableFlowSink sink, AccessPath sinkAp, Property p, AccessPath sinkApTail - ) { - exists(LibraryTypeDataFlow ltdf, Property p0 | - ltdf.callableFlow(_, _, sink, sinkAp, call.getTarget().getSourceDeclaration(), _) and - sinkAp.getHead().(PropertyContent).getProperty() = p0 and - sinkAp.getTail() = sinkApTail and - overridesOrImplementsSourceDecl(p, p0) and - result = sink.getSinkType(call) - ) - } - - /** - * Gets a precise sink property for sink `sink` and access path `sinkAp`, - * in the context of `call`. For example, in - * - * ```csharp - * var list = new List(); - * list.Add("taint"); - * var enumerator = list.getEnumerator(); - * ``` - * - * the step from `list` to `list.getEnumerator()`, which may be modeled as a - * read of a collection element followed by a store into the `Current` - * property, can be strengthened to be a store into the `Current` property - * from `List.Enumerator`, rather than the generic `Current` property - * from `IEnumerator`. - */ - pragma[nomagic] - private Property getPreciseSinkProperty( - Call call, CallableFlowSink sink, AccessPath sinkAp, AccessPath sinkApTail - ) { - getPreciseSinkProperty0(call, sink, sinkAp, result, sinkApTail).hasMember(result) - } - - predicate adjustSourceHead( - Call call, CallableFlowSource source, AccessPath sourceAp0, AccessPath sourceApTail, - PropertyContent p - ) { - overridesOrImplementsSourceDecl(p.getProperty(), - getPreciseSourceProperty(call, source, sourceAp0, sourceApTail).getSourceDeclaration()) - } - - predicate adjustSinkHead( - Call call, CallableFlowSink sink, AccessPath sinkAp0, AccessPath sinkApTail, PropertyContent p - ) { - p.getProperty() = getPreciseSinkProperty(call, sink, sinkAp0, sinkApTail).getSourceDeclaration() - } - - private newtype TAdjustedAccessPath = - TOriginalAccessPath(AccessPath ap) or - THeadAdjustedAccessPath(PropertyContent head, AccessPath tail) { - adjustSourceHead(_, _, _, tail, head) - or - adjustSinkHead(_, _, _, tail, head) - } - - /** - * An access path used in a library-code flow-summary, where the head of the path - * may have been adjusted. For example, in - * - * ```csharp - * var list = new List(); - * list.Add("taint"); - * var enumerator = list.getEnumerator(); - * ``` - * - * the step from `list` to `list.getEnumerator()`, which may be modeled as a - * read of a collection element followed by a store into the `Current` - * property, can be strengthened to be a store into the `Current` property - * from `List.Enumerator`, rather than the generic `Current` property - * from `IEnumerator`. - */ - abstract class AdjustedAccessPath extends TAdjustedAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Gets the tail of this access path, if any. */ - abstract AdjustedAccessPath getTail(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets the access path obtained by dropping the first `i` elements, if any. */ - abstract AdjustedAccessPath drop(int i); - - /** Holds if this access path contains content `c`. */ - predicate contains(Content c) { c = this.drop(_).getHead() } - - /** Gets a textual representation of this access path. */ - string toString() { - exists(Content head, AdjustedAccessPath tail | - head = this.getHead() and - tail = this.getTail() and - if tail.length() = 0 then result = head.toString() else result = head + ", " + tail - ) - or - this.length() = 0 and - result = "" - } - } - - private class OriginalAccessPath extends AdjustedAccessPath, TOriginalAccessPath { - private AccessPath ap; - - OriginalAccessPath() { this = TOriginalAccessPath(ap) } - - override Content getHead() { result = ap.getHead() } - - override AdjustedAccessPath getTail() { result = TOriginalAccessPath(ap.getTail()) } - - override int length() { result = ap.length() } - - override AdjustedAccessPath drop(int i) { result = TOriginalAccessPath(ap.drop(i)) } - } - - private class HeadAdjustedAccessPath extends AdjustedAccessPath, THeadAdjustedAccessPath { - private PropertyContent head; - private AccessPath tail; - - HeadAdjustedAccessPath() { this = THeadAdjustedAccessPath(head, tail) } - - override Content getHead() { result = head } - - override AdjustedAccessPath getTail() { result = TOriginalAccessPath(tail) } - - override int length() { result = 1 + tail.length() } - - override AdjustedAccessPath drop(int i) { - i = 0 and result = this - or - result = TOriginalAccessPath(tail.drop(i - 1)) - } - } - - module AdjustedAccessPath { - AdjustedAccessPath empty() { result.length() = 0 } - - AdjustedAccessPath singleton(Content c) { result.getHead() = c and result.length() = 1 } - } - - pragma[nomagic] - private predicate callableFlow( - CallableFlowSource source, AccessPath sourceAp, boolean adjustSourceAp, CallableFlowSink sink, - AccessPath sinkAp, boolean adjustSinkAp, SourceDeclarationCallable c, boolean preservesValue - ) { - any(LibraryTypeDataFlow ltdf).callableFlow(source, sourceAp, sink, sinkAp, c, preservesValue) and - ( - if sourceAp.getHead() instanceof PropertyContent - then adjustSourceAp = true - else adjustSourceAp = false - ) and - if sinkAp.getHead() instanceof PropertyContent - then adjustSinkAp = true - else adjustSinkAp = false - } +module Summaries { + /** A data-flow node used to interpret a flow summary. */ + abstract class SummaryNodeImpl extends NodeImpl { } /** * Holds if data can flow from a node of kind `source` to a node of kind `sink`, - * using a call to a library callable. + * using a call to a callable with a flow summary. * * `sourceAp` describes the contents of the source node that flows to the sink * (if any), and `sinkAp` describes the contents of the sink that it flows to * (if any). */ pragma[nomagic] - predicate libraryFlowSummary( - Call call, CallableFlowSource source, AdjustedAccessPath sourceAp, CallableFlowSink sink, - AdjustedAccessPath sinkAp, boolean preservesValue + predicate summary( + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue ) { - exists(SourceDeclarationCallable c | c = call.getTarget().getSourceDeclaration() | - any(LibraryTypeDataFlow ltdf).callableFlow(source, sink, c, preservesValue) and - sourceAp = TOriginalAccessPath(AccessPath::empty()) and - sinkAp = TOriginalAccessPath(AccessPath::empty()) - or - exists( - AccessPath sourceAp0, boolean adjustSourceAp, AccessPath sinkAp0, boolean adjustSinkAp - | - callableFlow(source, sourceAp0, adjustSourceAp, sink, sinkAp0, adjustSinkAp, c, - preservesValue) and - ( - adjustSourceAp = false and - sourceAp = TOriginalAccessPath(sourceAp0) - or - adjustSourceAp = true and - exists(PropertyContent p, AccessPath sourceApTail | - adjustSourceHead(call, source, sourceAp0, sourceApTail, p) and - sourceAp = THeadAdjustedAccessPath(p, sourceApTail) - ) - ) and - ( - adjustSinkAp = false and - sinkAp = TOriginalAccessPath(sinkAp0) - or - adjustSinkAp = true and - exists(PropertyContent p, AccessPath sinkApTail | - adjustSinkHead(call, sink, sinkAp0, sinkApTail, p) and - sinkAp = THeadAdjustedAccessPath(p, sinkApTail) - ) - ) - ) - ) + any(LibraryTypeDataFlow ltdf).callableFlow(source, sink, c, preservesValue) and + sourceAp = AccessPath::empty() and + sinkAp = AccessPath::empty() + or + any(LibraryTypeDataFlow ltdf).callableFlow(source, sourceAp, sink, sinkAp, c, preservesValue) } - private class LibrarySourceConfiguration extends ControlFlowReachabilityConfiguration { - LibrarySourceConfiguration() { this = "LibrarySourceConfiguration" } - - override predicate candidate( - Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor - ) { - exists(CallableFlowSource source | - libraryFlowSummary(e2, source, _, _, _, _) and - e1 = source.getSource(e2) and - scope = e2 and - exactScope = false and - isSuccessor = true - ) - } + /** Gets the return kind that matches `sink`, if any. */ + ReturnKind toReturnKind(CallableFlowSink sink) { + sink instanceof CallableFlowSinkQualifier and result instanceof QualifierReturnKind + or + sink instanceof CallableFlowSinkReturn and result instanceof NormalReturnKind + or + sink.(CallableFlowSinkArg).getArgumentIndex() = result.(OutRefReturnKind).getPosition() } - private class LibrarySinkConfiguration extends ControlFlowReachabilityConfiguration { - LibrarySinkConfiguration() { this = "LibrarySinkConfiguration" } - - override predicate candidate( - Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor - ) { - exists(CallableFlowSink sink | - libraryFlowSummary(e1, _, _, sink, _, _) and - e2 = sink.getSink(e1) and - exactScope = false and - if e2 instanceof ObjectOrCollectionInitializer - then scope = e2 and isSuccessor = true - else ( - scope = e1 and isSuccessor = false - ) - ) - } - - override predicate candidateDef( - Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope, - boolean isSuccessor - ) { - exists(CallableFlowSinkArg sink | - libraryFlowSummary(e, _, _, sink, _, _) and - scope = e and - exactScope = false and - isSuccessor = true and - def.getTargetAccess() = sink.getArgument(e) and - def instanceof AssignableDefinitions::OutRefDefinition - ) - } - } - - newtype TLibraryCodeNodeState = - TLibraryCodeNodeAfterReadState(AdjustedAccessPath ap) { ap.length() > 0 } or - TLibraryCodeNodeBeforeStoreState(AdjustedAccessPath ap) { ap.length() > 0 } + newtype TSummaryInternalNodeState = + TSummaryInternalNodeAfterReadState(AccessPath ap) { ap.length() > 0 } or + TSummaryInternalNodeBeforeStoreState(AccessPath ap) { ap.length() > 0 } /** * A state used to break up (complex) flow summaries for library code into atomic * flow steps. For a flow summary with source access path `sourceAp` and sink * access path `sinkAp`, the following states are used: * - * - `TLibraryCodeNodeAfterReadState(AccessPath ap)`: this state represents + * - `TSummaryInternalNodeAfterReadState(AccessPath ap)`: this state represents * that the head of `ap` has been read from, where `ap` is a suffix of * `sourceAp`. - * - `TLibraryCodeNodeBeforeStoreState(AccessPath ap)`: this state represents + * - `TSummaryInternalNodeBeforeStoreState(AccessPath ap)`: this state represents * that the head of `ap` is to be stored into next, where `ap` is a suffix of * `sinkAp`. * * The state machine for flow summaries has no branching, hence from the entry * state there is a unique path to the exit state. */ - class LibraryCodeNodeState extends TLibraryCodeNodeState { + class SummaryInternalNodeState extends TSummaryInternalNodeState { string toString() { - exists(AdjustedAccessPath ap | - this = TLibraryCodeNodeAfterReadState(ap) and + exists(AccessPath ap | + this = TSummaryInternalNodeAfterReadState(ap) and result = "after read: " + ap ) or - exists(AdjustedAccessPath ap | - this = TLibraryCodeNodeBeforeStoreState(ap) and + exists(AccessPath ap | + this = TSummaryInternalNodeBeforeStoreState(ap) and result = "before store: " + ap ) } /** Holds if this state represents the state after the last read. */ predicate isLastReadState() { - this = TLibraryCodeNodeAfterReadState(AdjustedAccessPath::singleton(_)) + this = TSummaryInternalNodeAfterReadState(AccessPath::singleton(_)) } /** Holds if this state represents the state before the first store. */ predicate isFirstStoreState() { - this = TLibraryCodeNodeBeforeStoreState(AdjustedAccessPath::singleton(_)) + this = TSummaryInternalNodeBeforeStoreState(AccessPath::singleton(_)) } } - /** - * Holds if `entry` is an entry node of kind `source` for the call `callCfn`, which - * targets a library callable with a flow summary. - */ - private predicate entry(Node entry, ControlFlow::Node callCfn, CallableFlowSource source) { - // The source is either an argument or a qualifier, for example - // `s` in `int.Parse(s)` - exists(LibrarySourceConfiguration x, Call call | - callCfn = call.getAControlFlowNode() and - x.hasExprPath(source.getSource(call), entry.(ExprNode).getControlFlowNode(), _, callCfn) + private NodeImpl getSourceNode(SourceDeclarationCallable c, CallableFlowSource source) { + exists(int i | result = TSummaryParameterNode(c, i) | + source instanceof CallableFlowSourceQualifier and i = -1 + or + i = source.(CallableFlowSourceArg).getArgumentIndex() ) or - // The source is the output of a supplied delegate argument, for - // example the output of `Foo` in `new Lazy(Foo)` - exists(DataFlowCall call, int pos | - pos = source.(CallableFlowSourceDelegateArg).getArgumentIndex() and - entry.(ImplicitDelegateOutNode).isArgumentOf(call, pos) and - callCfn = call.getControlFlowNode() - ) + result = TSummaryDelegateOutNode(c, source.(CallableFlowSourceDelegateArg).getArgumentIndex()) } - /** - * Holds if `exit` is an exit node of kind `sink` for the call `callCfn`, which - * targets a library callable with a flow summary. - */ - private predicate exit(Node exit, ControlFlow::Node callCfn, CallableFlowSink sink) { - exists(LibrarySinkConfiguration x, Call call, ExprNode e | - callCfn = call.getAControlFlowNode() and - x.hasExprPath(_, callCfn, sink.getSink(call), e.getControlFlowNode()) - | - // The sink is an ordinary return value, for example `int.Parse(s)` - sink instanceof CallableFlowSinkReturn and - exit = e - or - // The sink is a qualifier, for example `list` in `list.Add(x)` - sink instanceof CallableFlowSinkQualifier and - if e.getExpr() instanceof ObjectOrCollectionInitializer - then exit = e - else exit.(ExprPostUpdateNode).getPreUpdateNode() = e - ) + private NodeImpl getSinkNode(SourceDeclarationCallable c, CallableFlowSink sink) { + result = TSummaryReturnNode(c, toReturnKind(sink)) or - // The sink is an `out`/`ref` argument, for example `out i` in - // `int.TryParse(s, out i)` - exists(LibrarySinkConfiguration x, OutRefReturnKind k | - exit = - any(ParamOutNode out | - out.getCall(k).getControlFlowNode() = callCfn and - sink.(CallableFlowSinkArg).getArgumentIndex() = k.getPosition() and - x.hasDefPath(_, callCfn, out.getDefinition(), _) - ) - ) - or - // The sink is a parameter of a supplied delegate argument, for example - // the parameter of `Foo` in `list.Select(Foo)`. - // - // This is implemented using a node that represents the implicit argument - // (`ImplicitDelegateArgumentNode`) of the implicit call - // (`ImplicitDelegateDataFlowCall`) to `Foo`. - exists( - DataFlowCall call, ImplicitDelegateDataFlowCall dcall, int delegateIndex, int parameterIndex - | - sink = - any(CallableFlowSinkDelegateArg s | - delegateIndex = s.getDelegateIndex() and - parameterIndex = s.getDelegateParameterIndex() - ) and - exit = TImplicitDelegateArgumentNode(dcall.getControlFlowNode(), _, parameterIndex) and - dcall.isArgumentOf(call, delegateIndex) and - callCfn = call.getControlFlowNode() - ) + sink = + any(CallableFlowSinkDelegateArg s | + result = + TSummaryDelegateArgumentNode(c, s.getDelegateIndex(), s.getDelegateParameterIndex()) + ) } /** * Holds if there is a local step from `pred` to `succ`, which is synthesized - * from a library-code flow summary. + * from a flow summary. */ - predicate localStepLibrary(Node pred, Node succ, boolean preservesValue) { + predicate summaryLocalStep(Node pred, Node succ, boolean preservesValue) { exists( - ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp, - CallableFlowSink sink, AdjustedAccessPath sinkAp + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp | - libraryFlowSummary(callCfn.getElement(), source, sourceAp, sink, sinkAp, preservesValue) + pred = getSourceNode(c, source) | // Simple flow summary without reads or stores - sourceAp = AdjustedAccessPath::empty() and - sinkAp = AdjustedAccessPath::empty() and - entry(pred, callCfn, source) and - exit(succ, callCfn, sink) + sourceAp = AccessPath::empty() and + sinkAp = AccessPath::empty() and + summary(c, source, sourceAp, sink, sinkAp, preservesValue) and + succ = getSinkNode(c, sink) or - // Entry step for a complex summary with no reads and (1) multiple stores, or - // (2) at least one store and non-value-preservation - exists(LibraryCodeNodeState succState | - sourceAp = AdjustedAccessPath::empty() and - entry(pred, callCfn, source) and + // Flow summary with stores but no reads + exists(SummaryInternalNodeState succState | + sourceAp = AccessPath::empty() and succState.isFirstStoreState() and - succ = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, succState) - ) - or - // Exit step for a complex summary with no stores and (1) multiple reads, or - // (2) at least one read and non-value-preservation - exists(LibraryCodeNodeState predState | - sinkAp = AdjustedAccessPath::empty() and - predState.isLastReadState() and - pred = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, predState) and - exit(succ, callCfn, sink) + succ = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, succState) ) ) or + // Exit step after last read (no stores) + exists( + SourceDeclarationCallable c, SummaryInternalNodeState predState, CallableFlowSink sink, + AccessPath sinkAp + | + sinkAp = AccessPath::empty() and + predState.isLastReadState() and + pred = TSummaryInternalNode(c, _, _, sink, sinkAp, preservesValue, predState) and + succ = getSinkNode(c, sink) + ) + or // Internal step for complex flow summaries with both reads and writes exists( - ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp, - CallableFlowSink sink, AdjustedAccessPath sinkAp, LibraryCodeNodeState predState, - LibraryCodeNodeState succState + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, SummaryInternalNodeState predState, + SummaryInternalNodeState succState | predState.isLastReadState() and - pred = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, predState) and + pred = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, predState) and succState.isFirstStoreState() and - succ = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, succState) - ) - } - - /** - * Holds if there is a store of `pred` into content `c` of `succ`, which happens - * via library code. - */ - predicate setterLibrary(Node pred, Content c, Node succ, boolean preservesValue) { - exists(ControlFlow::Node callCfn, CallableFlowSource source, CallableFlowSink sink | - libraryFlowSummary(callCfn.getElement(), source, AdjustedAccessPath::empty(), sink, - AdjustedAccessPath::singleton(c), preservesValue) - | - entry(pred, callCfn, source) and - exit(succ, callCfn, sink) + succ = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, succState) ) } /** * Holds if data can flow from `pred` to `succ` via an assignment to - * content `c`, using library code. + * content `c`, using a flow summary. */ - predicate storeStepLibrary(Node pred, Content c, Node succ) { - // Complex flow summary + predicate summaryStoreStep(Node pred, Content c, Node succ) { exists( - ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp, - CallableFlowSink sink, AdjustedAccessPath sinkAp, boolean preservesValue, - LibraryCodeNodeState predState, AdjustedAccessPath ap + SourceDeclarationCallable sdc, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue, + SummaryInternalNodeState predState, AccessPath predAp | - predState = TLibraryCodeNodeBeforeStoreState(ap) and - pred = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, predState) and - c = ap.getHead() + predState = TSummaryInternalNodeBeforeStoreState(predAp) and + pred = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, predState) and + c = predAp.getHead() | // More stores needed - exists(LibraryCodeNodeState succState | + exists(SummaryInternalNodeState succState | succState = - TLibraryCodeNodeBeforeStoreState(any(AdjustedAccessPath succAp | succAp.getTail() = ap)) and - succ = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, succState) + TSummaryInternalNodeBeforeStoreState(any(AccessPath succAp | succAp.getTail() = predAp)) and + succ = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, succState) ) or // Last store - ap = sinkAp and - exit(succ, callCfn, sink) - ) - or - // Value-preserving setter - setterLibrary(pred, c, succ, true) - } - - /** - * Holds if there is a read of `c` from `pred` to `succ`, which happens via - * library code. - */ - predicate getterLibrary(Node pred, Content c, Node succ, boolean preservesValue) { - exists(ControlFlow::Node callCfn, CallableFlowSource source, CallableFlowSink sink | - libraryFlowSummary(callCfn.getElement(), source, AdjustedAccessPath::singleton(c), sink, - AdjustedAccessPath::empty(), preservesValue) and - entry(pred, callCfn, source) and - exit(succ, callCfn, sink) + predAp = sinkAp and + succ = getSinkNode(sdc, sink) ) } @@ -1985,95 +1738,133 @@ module LibraryFlow { * Holds if data can flow from `pred` to `succ` via a read of content `c`, * using library code. */ - predicate readStepLibrary(Node pred, Content c, Node succ) { - // Complex flow summary + predicate summaryReadStep(Node pred, Content c, Node succ) { exists( - ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp, - CallableFlowSink sink, AdjustedAccessPath sinkAp, boolean preservesValue, - LibraryCodeNodeState succState, AdjustedAccessPath ap + SourceDeclarationCallable sdc, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue, + SummaryInternalNodeState succState, AccessPath succAp | - succState = TLibraryCodeNodeAfterReadState(ap) and - succ = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, succState) and - c = ap.getHead() + succState = TSummaryInternalNodeAfterReadState(succAp) and + succ = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, succState) and + c = succAp.getHead() | // First read - ap = sourceAp and - entry(pred, callCfn, source) + succAp = sourceAp and + pred = getSourceNode(sdc, source) or // Subsequent reads - exists(LibraryCodeNodeState predState, AdjustedAccessPath predAp | - predState = TLibraryCodeNodeAfterReadState(predAp) and - predAp.getTail() = ap and - pred = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, predState) + exists(SummaryInternalNodeState predState, AccessPath predAp | + predState = TSummaryInternalNodeAfterReadState(predAp) and + predAp.getTail() = succAp and + pred = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, predState) ) ) - or - // Value-preserving getter - getterLibrary(pred, c, succ, true) + } + + pragma[nomagic] + private SummaryParameterNode summaryArgParam(ArgumentNode arg, ReturnKind rk, OutNode out) { + exists(DataFlowCall call, int pos, SourceDeclarationCallable sdc | + arg.argumentOf(call, pos) and + call.getARuntimeTarget() = sdc and + result = TSummaryParameterNode(sdc, pos) and + call = out.getCall(rk) + ) + } + + /** + * Holds if `arg` flows to `out` using a simple flow summary, that is, a flow + * summary without delegates, reads, and stores. + */ + predicate summaryThroughStep(ArgumentNode arg, OutNode out, boolean preservesValue) { + exists(ReturnKind rk | + summaryLocalStep(summaryArgParam(arg, rk, out), TSummaryReturnNode(_, rk), preservesValue) + ) + } + + /** + * Holds if there is a (taint+)store of `arg` into content `c` of `out` using a + * flow summary. + */ + predicate summarySetterStep(ArgumentNode arg, Content c, OutNode out) { + exists(ReturnKind rk, Node mid | + summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and + summaryStoreStep(mid, c, TSummaryReturnNode(_, rk)) + ) + } + + /** + * Holds if there is a read(+taint) of `c` from `arg` to `out` using a + * flow summary. + */ + predicate summaryGetterStep(ArgumentNode arg, Content c, OutNode out) { + exists(ReturnKind rk, Node mid | + summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and + summaryLocalStep(mid, TSummaryReturnNode(_, rk), _) + ) } /** * Holds if values stored inside content `c` are cleared at node `n`, as a result * of calling a library method. */ - predicate clearsContentLibrary(Node n, Content c) { + predicate summaryClearsContent(Node n, Content c) { exists(LibraryTypeDataFlow ltdf, CallableFlowSource source, Call call | ltdf.clearsContent(source, c, call.getTarget().getSourceDeclaration()) and n.asExpr() = source.getSource(call) ) } -} -/** Gets the type of content `c`. */ -pragma[noinline] -private Gvn::GvnType getContentType(Content c) { - exists(Type t | result = Gvn::getGlobalValueNumber(t) | - t = c.(FieldContent).getField().getType() - or - t = c.(PropertyContent).getProperty().getType() - or - c instanceof ElementContent and - t instanceof ObjectType // we don't know what the actual element type is - ) -} - -/** A data-flow node used to model flow through library code. */ -class LibraryCodeNode extends NodeImpl, TLibraryCodeNode { - private ControlFlow::Node callCfn; - private CallableFlowSource source; - private LibraryFlow::AdjustedAccessPath sourceAp; - private CallableFlowSink sink; - private LibraryFlow::AdjustedAccessPath sinkAp; - private boolean preservesValue; - private LibraryFlow::LibraryCodeNodeState state; - - LibraryCodeNode() { - this = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, state) - } - - override Callable getEnclosingCallableImpl() { result = callCfn.getEnclosingCallable() } - - override Gvn::GvnType getDataFlowType() { - exists(LibraryFlow::AdjustedAccessPath ap | - state = LibraryFlow::TLibraryCodeNodeAfterReadState(ap) and - if sinkAp.length() = 0 and state.isLastReadState() and preservesValue = true - then result = Gvn::getGlobalValueNumber(sink.getSinkType(callCfn.getElement())) - else result = getContentType(ap.getHead()) + /** Gets the type of content `c`. */ + pragma[noinline] + private Gvn::GvnType getContentType(Content c) { + exists(Type t | result = Gvn::getGlobalValueNumber(t) | + t = c.(FieldContent).getField().getType() or - state = LibraryFlow::TLibraryCodeNodeBeforeStoreState(ap) and - if sourceAp.length() = 0 and state.isFirstStoreState() and preservesValue = true - then result = Gvn::getGlobalValueNumber(source.getSourceType(callCfn.getElement())) - else result = getContentType(ap.getHead()) + t = c.(PropertyContent).getProperty().getType() + or + c instanceof ElementContent and + t instanceof ObjectType // we don't know what the actual element type is ) } - override DotNet::Type getTypeImpl() { none() } + /** A data-flow node used to model flow summaries. */ + private class SummaryInternalNode extends SummaryNodeImpl, TSummaryInternalNode { + private SourceDeclarationCallable c; + private CallableFlowSource source; + private AccessPath sourceAp; + private CallableFlowSink sink; + private AccessPath sinkAp; + private boolean preservesValue; + private SummaryInternalNodeState state; - override ControlFlow::Node getControlFlowNodeImpl() { result = callCfn } + SummaryInternalNode() { + this = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, state) + } - override Location getLocationImpl() { result = callCfn.getLocation() } + override DataFlowCallable getEnclosingCallableImpl() { result = c } - override string toStringImpl() { result = "[library code: " + state + "] " + callCfn } + override Gvn::GvnType getDataFlowType() { + exists(AccessPath ap | + state = TSummaryInternalNodeAfterReadState(ap) and + if sinkAp.length() = 0 and state.isLastReadState() and preservesValue = true + then result = getSinkNode(c, sink).getDataFlowType() + else result = getContentType(ap.getHead()) + or + state = TSummaryInternalNodeBeforeStoreState(ap) and + if sourceAp.length() = 0 and state.isFirstStoreState() and preservesValue = true + then result = getSourceNode(c, source).getDataFlowType() + else result = getContentType(ap.getHead()) + ) + } + + override DotNet::Type getTypeImpl() { none() } + + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { result = c.getLocation() } + + override string toStringImpl() { result = "[summary] " + state + " in " + c } + } } /** A field or a property. */ @@ -2325,7 +2116,7 @@ private module PostUpdateNodes { override MallocNode getPreUpdateNode() { result.getControlFlowNode() = cfn } - override DataFlowCallable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } + override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } override DotNet::Type getTypeImpl() { result = oc.getType() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index 759921cf5be..bf012e53283 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -123,7 +123,8 @@ class ParameterNode extends Node { this.(SsaDefinitionNode).getDefinition() instanceof ImplicitCapturedParameterNodeImpl::SsaCapturedEntryDefinition or this = TInstanceParameterNode(_) or - this = TCilParameterNode(_) + this = TCilParameterNode(_) or + this = TSummaryParameterNode(_, _) } /** Gets the parameter corresponding to this node, if any. */ diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll index d031b345308..243c6b83ef5 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll @@ -30,7 +30,7 @@ private class DelegateFlowSource extends DataFlow::ExprNode { } /** A sink of flow for a delegate expression. */ -abstract private class DelegateFlowSink extends DataFlow::ExprNode { +abstract private class DelegateFlowSink extends DataFlow::Node { /** * Gets an actual run-time target of this delegate call in the given call * context, if any. The call context records the *last* call required to @@ -92,7 +92,7 @@ abstract private class DelegateFlowSink extends DataFlow::ExprNode { } /** A delegate call expression. */ -library class DelegateCallExpr extends DelegateFlowSink { +class DelegateCallExpr extends DelegateFlowSink, DataFlow::ExprNode { DelegateCall dc; DelegateCallExpr() { this.getExpr() = dc.getDelegateExpr() } @@ -101,50 +101,15 @@ library class DelegateCallExpr extends DelegateFlowSink { DelegateCall getDelegateCall() { result = dc } } -/** A delegate expression that is passed as the argument to a library callable. */ -library class DelegateArgumentToLibraryCallable extends Expr { - DelegateType dt; - Call call; - - DelegateArgumentToLibraryCallable() { - exists(Callable callable, Parameter p | - this = call.getArgumentForParameter(p) and - callable = call.getTarget() and - callable.fromLibrary() and - dt = p.getType().(SystemLinqExpressions::DelegateExtType).getDelegateType() - ) - } - - /** Gets the call that this argument belongs to. */ - Call getCall() { result = call } - - /** Gets the index of this delegate argument in the call. */ - int getArgumentIndex() { this = this.getCall().getArgument(result) } - - /** Gets the delegate type of this argument. */ - DelegateType getDelegateType() { result = dt } - - /** - * Gets an actual run-time target of this delegate call in the given call - * context, if any. The call context records the *last* call required to - * resolve the target, if any. Example: - */ - Callable getARuntimeTarget(CallContext context) { - exists(DelegateArgumentToLibraryCallableSink sink | sink.getExpr() = this | - result = sink.getARuntimeTarget(context) - ) - } -} - -/** A delegate expression that is passed as the argument to a library callable. */ -private class DelegateArgumentToLibraryCallableSink extends DelegateFlowSink { - DelegateArgumentToLibraryCallableSink() { - this.getExpr() instanceof DelegateArgumentToLibraryCallable +/** A parameter of delegate type belonging to a callable with a flow summary. */ +class SummaryDelegateParameterSink extends DelegateFlowSink, SummaryParameterNode { + SummaryDelegateParameterSink() { + this.getType() instanceof SystemLinqExpressions::DelegateExtType } } /** A delegate expression that is added to an event. */ -library class AddEventSource extends DelegateFlowSink { +class AddEventSource extends DelegateFlowSink, DataFlow::ExprNode { AddEventExpr ae; AddEventSource() { this.getExpr() = ae.getRValue() } @@ -192,7 +157,13 @@ private predicate flowsFrom( or // Local flow exists(DataFlow::Node mid | flowsFrom(sink, mid, isReturned, lastCall) | - DataFlow::localFlowStep(node, mid) or + LocalFlow::localFlowStepCommon(node, mid) + or + exists(Ssa::Definition def | + LocalFlow::localSsaFlowStep(def, node, mid) and + LocalFlow::usesInstanceField(def) + ) + or node.asExpr() = mid.asExpr().(DelegateCreation).getArgument() ) or @@ -205,7 +176,7 @@ private predicate flowsFrom( ) or // Flow into a callable (non-delegate call) - exists(ExplicitParameterNode mid, CallContext prevLastCall, NonDelegateCall call, Parameter p | + exists(ParameterNode mid, CallContext prevLastCall, NonDelegateCall call, Parameter p | flowsFrom(sink, mid, isReturned, prevLastCall) and isReturned = false and p = mid.getParameter() and @@ -215,8 +186,7 @@ private predicate flowsFrom( or // Flow into a callable (delegate call) exists( - ExplicitParameterNode mid, CallContext prevLastCall, DelegateCall call, Callable c, Parameter p, - int i + ParameterNode mid, CallContext prevLastCall, DelegateCall call, Callable c, Parameter p, int i | flowsFrom(sink, mid, isReturned, prevLastCall) and isReturned = false and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll index 04af24087af..68284e965fa 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll @@ -112,7 +112,7 @@ private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node n cached private module Cached { - private import LibraryFlow + private import Summaries /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local @@ -130,19 +130,19 @@ private module Cached { ( // Simple flow through library code is included in the exposed local // step relation, even though flow is technically inter-procedural - LibraryFlow::localStepLibrary(nodeFrom, nodeTo, false) + summaryThroughStep(nodeFrom, nodeTo, false) or // Taint collection by adding a tainted element exists(DataFlow::ElementContent c | storeStep(nodeFrom, c, nodeTo) or - setterLibrary(nodeFrom, c, nodeTo, false) + summarySetterStep(nodeFrom, c, nodeTo) ) or exists(DataFlow::Content c | readStep(nodeFrom, c, nodeTo) or - getterLibrary(nodeFrom, c, nodeTo, false) + summaryGetterStep(nodeFrom, c, nodeTo) | // Taint members c = any(TaintedMember m).(FieldOrProperty).getContent() @@ -169,7 +169,7 @@ private module Cached { // tracking configurations where the source is a collection readStep(nodeFrom, TElementContent(), nodeTo) or - LibraryFlow::localStepLibrary(nodeFrom, nodeTo, false) + summaryLocalStep(nodeFrom, nodeTo, false) or nodeTo = nodeFrom.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false) } diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index e426955f9da..e0586a0adcd 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -222,7 +222,7 @@ casts.cs: # 13| r13_1(glval) = VariableAddress[Aobj] : # 13| r13_2(Casts_A) = NewObj : # 13| r13_3() = FunctionAddress[Casts_A] : -# 13| v13_4(Void) = Call : func:r13_3, this:r13_2 +# 13| v13_4(Void) = Call[Casts_A] : func:r13_3, this:r13_2 # 13| mu13_5() = ^CallSideEffect : ~m? # 13| mu13_6(Casts_A) = Store : &:r13_1, r13_2 # 14| r14_1(glval) = VariableAddress[bobjCE] : @@ -247,13 +247,13 @@ collections.cs: # 13| r13_1(glval>) = VariableAddress[dict] : # 13| r13_2(Dictionary) = NewObj : # 13| r13_3() = FunctionAddress[Dictionary] : -# 13| v13_4(Void) = Call : func:r13_3, this:r13_2 +# 13| v13_4(Void) = Call[Dictionary] : func:r13_3, this:r13_2 # 13| mu13_5() = ^CallSideEffect : ~m? # 15| r15_1() = FunctionAddress[Add] : # 15| r15_2(Int32) = Constant[0] : # 15| r15_3(MyClass) = NewObj : # 15| r15_4() = FunctionAddress[MyClass] : -# 15| v15_5(Void) = Call : func:r15_4, this:r15_3 +# 15| v15_5(Void) = Call[MyClass] : func:r15_4, this:r15_3 # 15| mu15_6() = ^CallSideEffect : ~m? # 15| r15_7(String) = StringConstant["Hello"] : # 15| r15_8(glval) = FieldAddress[a] : r15_3 @@ -261,13 +261,13 @@ collections.cs: # 15| r15_10(String) = StringConstant["World"] : # 15| r15_11(glval) = FieldAddress[b] : r15_3 # 15| mu15_12(String) = Store : &:r15_11, r15_10 -# 15| v15_13(Void) = Call : func:r15_1, this:r13_2, 0:r15_2, 1:r15_3 +# 15| v15_13(Void) = Call[Add] : func:r15_1, this:r13_2, 0:r15_2, 1:r15_3 # 15| mu15_14() = ^CallSideEffect : ~m? # 16| r16_1() = FunctionAddress[Add] : # 16| r16_2(Int32) = Constant[1] : # 16| r16_3(MyClass) = NewObj : # 16| r16_4() = FunctionAddress[MyClass] : -# 16| v16_5(Void) = Call : func:r16_4, this:r16_3 +# 16| v16_5(Void) = Call[MyClass] : func:r16_4, this:r16_3 # 16| mu16_6() = ^CallSideEffect : ~m? # 16| r16_7(String) = StringConstant["Foo"] : # 16| r16_8(glval) = FieldAddress[a] : r16_3 @@ -275,7 +275,7 @@ collections.cs: # 16| r16_10(String) = StringConstant["Bar"] : # 16| r16_11(glval) = FieldAddress[b] : r16_3 # 16| mu16_12(String) = Store : &:r16_11, r16_10 -# 16| v16_13(Void) = Call : func:r16_1, this:r13_2, 0:r16_2, 1:r16_3 +# 16| v16_13(Void) = Call[Add] : func:r16_1, this:r13_2, 0:r16_2, 1:r16_3 # 16| mu16_14() = ^CallSideEffect : ~m? # 13| mu13_6(Dictionary) = Store : &:r13_1, r13_2 # 11| v11_3(Void) = ReturnVoid : @@ -316,7 +316,7 @@ constructor_init.cs: # 17| r17_3(glval) = InitializeThis : # 17| r17_4(glval) = Convert[DerivedClass : BaseClass] : r17_3 # 17| r17_5() = FunctionAddress[BaseClass] : -# 17| v17_6(Void) = Call : func:r17_5, this:r17_4 +# 17| v17_6(Void) = Call[BaseClass] : func:r17_5, this:r17_4 # 17| mu17_7() = ^CallSideEffect : ~m? # 18| v18_1(Void) = NoOp : # 17| v17_8(Void) = ReturnVoid : @@ -334,7 +334,7 @@ constructor_init.cs: # 21| r21_7() = FunctionAddress[BaseClass] : # 21| r21_8(glval) = VariableAddress[i] : # 21| r21_9(Int32) = Load : &:r21_8, ~m? -# 21| v21_10(Void) = Call : func:r21_7, this:r21_6, 0:r21_9 +# 21| v21_10(Void) = Call[BaseClass] : func:r21_7, this:r21_6, 0:r21_9 # 21| mu21_11() = ^CallSideEffect : ~m? # 22| v22_1(Void) = NoOp : # 21| v21_12(Void) = ReturnVoid : @@ -353,7 +353,7 @@ constructor_init.cs: # 25| r25_8() = FunctionAddress[DerivedClass] : # 25| r25_9(glval) = VariableAddress[i] : # 25| r25_10(Int32) = Load : &:r25_9, ~m? -# 25| v25_11(Void) = Call : func:r25_8, this:r25_3, 0:r25_10 +# 25| v25_11(Void) = Call[DerivedClass] : func:r25_8, this:r25_3, 0:r25_10 # 25| mu25_12() = ^CallSideEffect : ~m? # 26| v26_1(Void) = NoOp : # 25| v25_13(Void) = ReturnVoid : @@ -367,14 +367,14 @@ constructor_init.cs: # 31| r31_1(glval) = VariableAddress[obj1] : # 31| r31_2(DerivedClass) = NewObj : # 31| r31_3() = FunctionAddress[DerivedClass] : -# 31| v31_4(Void) = Call : func:r31_3, this:r31_2 +# 31| v31_4(Void) = Call[DerivedClass] : func:r31_3, this:r31_2 # 31| mu31_5() = ^CallSideEffect : ~m? # 31| mu31_6(DerivedClass) = Store : &:r31_1, r31_2 # 32| r32_1(glval) = VariableAddress[obj2] : # 32| r32_2(DerivedClass) = NewObj : # 32| r32_3() = FunctionAddress[DerivedClass] : # 32| r32_4(Int32) = Constant[1] : -# 32| v32_5(Void) = Call : func:r32_3, this:r32_2, 0:r32_4 +# 32| v32_5(Void) = Call[DerivedClass] : func:r32_3, this:r32_2, 0:r32_4 # 32| mu32_6() = ^CallSideEffect : ~m? # 32| mu32_7(DerivedClass) = Store : &:r32_1, r32_2 # 33| r33_1(glval) = VariableAddress[obj3] : @@ -382,7 +382,7 @@ constructor_init.cs: # 33| r33_3() = FunctionAddress[DerivedClass] : # 33| r33_4(Int32) = Constant[1] : # 33| r33_5(Int32) = Constant[2] : -# 33| v33_6(Void) = Call : func:r33_3, this:r33_2, 0:r33_4, 1:r33_5 +# 33| v33_6(Void) = Call[DerivedClass] : func:r33_3, this:r33_2, 0:r33_4, 1:r33_5 # 33| mu33_7() = ^CallSideEffect : ~m? # 33| mu33_8(DerivedClass) = Store : &:r33_1, r33_2 # 29| v29_3(Void) = ReturnVoid : @@ -453,14 +453,14 @@ delegates.cs: # 12| r12_2(Del) = NewObj : # 12| r12_3() = FunctionAddress[Del] : # 12| r12_4(glval) = FunctionAddress[returns] : -# 12| v12_5(Void) = Call : func:r12_3, this:r12_2, 0:r12_4 +# 12| v12_5(Void) = Call[Del] : func:r12_3, this:r12_2, 0:r12_4 # 12| mu12_6() = ^CallSideEffect : ~m? # 12| mu12_7(Del) = Store : &:r12_1, r12_2 # 13| r13_1(glval) = VariableAddress[del1] : # 13| r13_2(Del) = Load : &:r13_1, ~m? # 13| r13_3() = FunctionAddress[Invoke] : # 13| r13_4(Int32) = Constant[5] : -# 13| v13_5(Void) = Call : func:r13_3, this:r13_2, 0:r13_4 +# 13| v13_5(Void) = Call[Invoke] : func:r13_3, this:r13_2, 0:r13_4 # 13| mu13_6() = ^CallSideEffect : ~m? # 11| v11_3(Void) = ReturnVoid : # 11| v11_4(Void) = AliasedUse : ~m? @@ -475,7 +475,7 @@ events.cs: # 10| r10_1(MyDel) = NewObj : # 10| r10_2() = FunctionAddress[MyDel] : # 10| r10_3(glval) = FunctionAddress[Fun] : -# 10| v10_4(Void) = Call : func:r10_2, this:r10_1, 0:r10_3 +# 10| v10_4(Void) = Call[MyDel] : func:r10_2, this:r10_1, 0:r10_3 # 10| mu10_5() = ^CallSideEffect : ~m? # 10| r10_6(Events) = CopyValue : r8_3 # 10| r10_7(glval) = FieldAddress[Inst] : r10_6 @@ -494,7 +494,7 @@ events.cs: # 15| r15_3(Events) = CopyValue : r13_3 # 15| r15_4(glval) = FieldAddress[Inst] : r15_3 # 15| r15_5(MyDel) = Load : &:r15_4, ~m? -# 15| v15_6(Void) = Call : func:r15_2, this:r15_1, 0:r15_5 +# 15| v15_6(Void) = Call[add_MyEvent] : func:r15_2, this:r15_1, 0:r15_5 # 15| mu15_7() = ^CallSideEffect : ~m? # 13| v13_4(Void) = ReturnVoid : # 13| v13_5(Void) = AliasedUse : ~m? @@ -510,7 +510,7 @@ events.cs: # 20| r20_3(Events) = CopyValue : r18_3 # 20| r20_4(glval) = FieldAddress[Inst] : r20_3 # 20| r20_5(MyDel) = Load : &:r20_4, ~m? -# 20| v20_6(Void) = Call : func:r20_2, this:r20_1, 0:r20_5 +# 20| v20_6(Void) = Call[remove_MyEvent] : func:r20_2, this:r20_1, 0:r20_5 # 20| mu20_7() = ^CallSideEffect : ~m? # 18| v18_4(Void) = ReturnVoid : # 18| v18_5(Void) = AliasedUse : ~m? @@ -541,26 +541,26 @@ events.cs: # 30| r30_1(glval) = VariableAddress[obj] : # 30| r30_2(Events) = NewObj : # 30| r30_3() = FunctionAddress[Events] : -# 30| v30_4(Void) = Call : func:r30_3, this:r30_2 +# 30| v30_4(Void) = Call[Events] : func:r30_3, this:r30_2 # 30| mu30_5() = ^CallSideEffect : ~m? # 30| mu30_6(Events) = Store : &:r30_1, r30_2 # 31| r31_1(glval) = VariableAddress[obj] : # 31| r31_2(Events) = Load : &:r31_1, ~m? # 31| r31_3() = FunctionAddress[AddEvent] : -# 31| v31_4(Void) = Call : func:r31_3, this:r31_2 +# 31| v31_4(Void) = Call[AddEvent] : func:r31_3, this:r31_2 # 31| mu31_5() = ^CallSideEffect : ~m? # 32| r32_1(glval) = VariableAddress[result] : # 32| r32_2(glval) = VariableAddress[obj] : # 32| r32_3(Events) = Load : &:r32_2, ~m? # 32| r32_4() = FunctionAddress[Invoke] : # 32| r32_5(String) = StringConstant["string"] : -# 32| v32_6(Void) = Call : func:r32_4, this:r32_3, 0:r32_5 +# 32| v32_6(Void) = Call[Invoke] : func:r32_4, this:r32_3, 0:r32_5 # 32| mu32_7() = ^CallSideEffect : ~m? # 32| mu32_8(String) = Store : &:r32_1, v32_6 # 33| r33_1(glval) = VariableAddress[obj] : # 33| r33_2(Events) = Load : &:r33_1, ~m? # 33| r33_3() = FunctionAddress[RemoveEvent] : -# 33| v33_4(Void) = Call : func:r33_3, this:r33_2 +# 33| v33_4(Void) = Call[RemoveEvent] : func:r33_3, this:r33_2 # 33| mu33_5() = ^CallSideEffect : ~m? # 28| v28_5(Void) = ReturnVoid : # 28| v28_6(Void) = AliasedUse : ~m? @@ -605,7 +605,7 @@ foreach.cs: # 7| r7_2(glval) = VariableAddress[a_array] : # 7| r7_3(Int32[]) = Load : &:r7_2, ~m? # 7| r7_4() = FunctionAddress[GetEnumerator] : -# 7| r7_5(IEnumerator) = Call : func:r7_4, this:r7_3 +# 7| r7_5(IEnumerator) = Call[GetEnumerator] : func:r7_4, this:r7_3 # 7| mu7_6() = ^CallSideEffect : ~m? # 7| mu7_7(IEnumerator) = Store : &:r7_1, r7_5 #-----| Goto -> Block 1 @@ -614,7 +614,7 @@ foreach.cs: # 7| r7_8(glval) = VariableAddress[#temp7:9] : # 7| r7_9(Boolean) = Load : &:r7_8, ~m? # 7| r7_10() = FunctionAddress[MoveNext] : -# 7| r7_11(Boolean) = Call : func:r7_10, this:r7_9 +# 7| r7_11(Boolean) = Call[MoveNext] : func:r7_10, this:r7_9 # 7| mu7_12() = ^CallSideEffect : ~m? # 7| v7_13(Void) = ConditionalBranch : r7_11 #-----| False -> Block 3 @@ -625,7 +625,7 @@ foreach.cs: # 7| r7_15(glval) = VariableAddress[#temp7:9] : # 7| r7_16(Boolean) = Load : &:r7_15, ~m? # 7| r7_17() = FunctionAddress[get_Current] : -# 7| r7_18(Int32) = Call : func:r7_17, this:r7_16 +# 7| r7_18(Int32) = Call[get_Current] : func:r7_17, this:r7_16 # 7| mu7_19() = ^CallSideEffect : ~m? # 7| mu7_20(Int32) = Store : &:r7_14, r7_18 # 9| r9_1(glval) = VariableAddress[x] : @@ -638,7 +638,7 @@ foreach.cs: # 7| r7_21(glval) = VariableAddress[#temp7:9] : # 7| r7_22(Boolean) = Load : &:r7_21, ~m? # 7| r7_23() = FunctionAddress[Dispose] : -# 7| v7_24(Void) = Call : func:r7_23, this:r7_22 +# 7| v7_24(Void) = Call[Dispose] : func:r7_23, this:r7_22 # 7| mu7_25() = ^CallSideEffect : ~m? # 4| v4_3(Void) = ReturnVoid : # 4| v4_4(Void) = AliasedUse : ~m? @@ -673,7 +673,7 @@ func_with_param_call.cs: # 12| r12_2() = FunctionAddress[f] : # 12| r12_3(Int32) = Constant[2] : # 12| r12_4(Int32) = Constant[3] : -# 12| r12_5(Int32) = Call : func:r12_2, 0:r12_3, 1:r12_4 +# 12| r12_5(Int32) = Call[f] : func:r12_2, 0:r12_3, 1:r12_4 # 12| mu12_6() = ^CallSideEffect : ~m? # 12| mu12_7(Int32) = Store : &:r12_1, r12_5 # 10| r10_3(glval) = VariableAddress[#return] : @@ -732,7 +732,7 @@ indexers.cs: # 21| r21_1(glval) = VariableAddress[inst] : # 21| r21_2(MyClass) = NewObj : # 21| r21_3() = FunctionAddress[MyClass] : -# 21| v21_4(Void) = Call : func:r21_3, this:r21_2 +# 21| v21_4(Void) = Call[MyClass] : func:r21_3, this:r21_2 # 21| mu21_5() = ^CallSideEffect : ~m? # 21| mu21_6(MyClass) = Store : &:r21_1, r21_2 # 22| r22_1(glval) = VariableAddress[inst] : @@ -740,14 +740,14 @@ indexers.cs: # 22| r22_3() = FunctionAddress[set_Item] : # 22| r22_4(Int32) = Constant[0] : # 22| r22_5(String) = StringConstant["str1"] : -# 22| v22_6(Void) = Call : func:r22_3, this:r22_2, 0:r22_4, 1:r22_5 +# 22| v22_6(Void) = Call[set_Item] : func:r22_3, this:r22_2, 0:r22_4, 1:r22_5 # 22| mu22_7() = ^CallSideEffect : ~m? # 23| r23_1(glval) = VariableAddress[inst] : # 23| r23_2(MyClass) = Load : &:r23_1, ~m? # 23| r23_3() = FunctionAddress[set_Item] : # 23| r23_4(Int32) = Constant[1] : # 23| r23_5(String) = StringConstant["str1"] : -# 23| v23_6(Void) = Call : func:r23_3, this:r23_2, 0:r23_4, 1:r23_5 +# 23| v23_6(Void) = Call[set_Item] : func:r23_3, this:r23_2, 0:r23_4, 1:r23_5 # 23| mu23_7() = ^CallSideEffect : ~m? # 24| r24_1(glval) = VariableAddress[inst] : # 24| r24_2(MyClass) = Load : &:r24_1, ~m? @@ -757,9 +757,9 @@ indexers.cs: # 24| r24_6(MyClass) = Load : &:r24_5, ~m? # 24| r24_7() = FunctionAddress[get_Item] : # 24| r24_8(Int32) = Constant[0] : -# 24| r24_9(String) = Call : func:r24_7, this:r24_6, 0:r24_8 +# 24| r24_9(String) = Call[get_Item] : func:r24_7, this:r24_6, 0:r24_8 # 24| mu24_10() = ^CallSideEffect : ~m? -# 24| v24_11(Void) = Call : func:r24_3, this:r24_2, 0:r24_4, 1:r24_9 +# 24| v24_11(Void) = Call[set_Item] : func:r24_3, this:r24_2, 0:r24_4, 1:r24_9 # 24| mu24_12() = ^CallSideEffect : ~m? # 19| v19_3(Void) = ReturnVoid : # 19| v19_4(Void) = AliasedUse : ~m? @@ -799,13 +799,13 @@ inheritance_polymorphism.cs: # 25| r25_1(glval) = VariableAddress[objB] : # 25| r25_2(B) = NewObj : # 25| r25_3() = FunctionAddress[B] : -# 25| v25_4(Void) = Call : func:r25_3, this:r25_2 +# 25| v25_4(Void) = Call[B] : func:r25_3, this:r25_2 # 25| mu25_5() = ^CallSideEffect : ~m? # 25| mu25_6(B) = Store : &:r25_1, r25_2 # 26| r26_1(glval) = VariableAddress[objB] : # 26| r26_2(B) = Load : &:r26_1, ~m? # 26| r26_3() = FunctionAddress[function] : -# 26| r26_4(Int32) = Call : func:r26_3, this:r26_2 +# 26| r26_4(Int32) = Call[function] : func:r26_3, this:r26_2 # 26| mu26_5() = ^CallSideEffect : ~m? # 29| r29_1(glval) = VariableAddress[objA] : # 29| mu29_2(A) = Uninitialized[objA] : &:r29_1 @@ -817,19 +817,19 @@ inheritance_polymorphism.cs: # 31| r31_1(glval) = VariableAddress[objA] : # 31| r31_2(A) = Load : &:r31_1, ~m? # 31| r31_3() = FunctionAddress[function] : -# 31| r31_4(Int32) = Call : func:r31_3, this:r31_2 +# 31| r31_4(Int32) = Call[function] : func:r31_3, this:r31_2 # 31| mu31_5() = ^CallSideEffect : ~m? # 33| r33_1(glval) = VariableAddress[objC] : # 33| r33_2(C) = NewObj : # 33| r33_3() = FunctionAddress[C] : -# 33| v33_4(Void) = Call : func:r33_3, this:r33_2 +# 33| v33_4(Void) = Call[C] : func:r33_3, this:r33_2 # 33| mu33_5() = ^CallSideEffect : ~m? # 33| r33_6(A) = Convert : r33_2 # 33| mu33_7(A) = Store : &:r33_1, r33_2 # 34| r34_1(glval) = VariableAddress[objC] : # 34| r34_2(A) = Load : &:r34_1, ~m? # 34| r34_3() = FunctionAddress[function] : -# 34| r34_4(Int32) = Call : func:r34_3, this:r34_2 +# 34| r34_4(Int32) = Call[function] : func:r34_3, this:r34_2 # 34| mu34_5() = ^CallSideEffect : ~m? # 23| v23_3(Void) = ReturnVoid : # 23| v23_4(Void) = AliasedUse : ~m? @@ -905,7 +905,7 @@ inoutref.cs: # 26| r26_4(glval) = VariableAddress[c1] : # 26| r26_5(MyClass) = Load : &:r26_4, ~m? # 26| r26_6(MyClass) = Load : &:r26_5, ~m? -# 26| v26_7(Void) = Call : func:r26_1, 0:r26_3, 1:r26_6 +# 26| v26_7(Void) = Call[set] : func:r26_1, 0:r26_3, 1:r26_6 # 26| mu26_8() = ^CallSideEffect : ~m? # 16| v16_13(Void) = ReturnVoid : # 16| v16_14(Void) = AliasedUse : ~m? @@ -921,14 +921,14 @@ inoutref.cs: # 32| r32_1(glval) = VariableAddress[b] : # 32| r32_2(MyStruct) = NewObj : # 32| r32_3() = FunctionAddress[MyStruct] : -# 32| v32_4(Void) = Call : func:r32_3, this:r32_2 +# 32| v32_4(Void) = Call[MyStruct] : func:r32_3, this:r32_2 # 32| mu32_5() = ^CallSideEffect : ~m? # 32| r32_6(MyStruct) = Load : &:r32_2, ~m? # 32| mu32_7(MyStruct) = Store : &:r32_1, r32_6 # 33| r33_1(glval) = VariableAddress[c] : # 33| r33_2(MyClass) = NewObj : # 33| r33_3() = FunctionAddress[MyClass] : -# 33| v33_4(Void) = Call : func:r33_3, this:r33_2 +# 33| v33_4(Void) = Call[MyClass] : func:r33_3, this:r33_2 # 33| mu33_5() = ^CallSideEffect : ~m? # 33| mu33_6(MyClass) = Store : &:r33_1, r33_2 # 34| r34_1() = FunctionAddress[F] : @@ -937,7 +937,7 @@ inoutref.cs: # 34| r34_4(glval) = VariableAddress[b] : # 34| r34_5(glval) = VariableAddress[c] : # 34| r34_6(glval) = VariableAddress[c] : -# 34| v34_7(Void) = Call : func:r34_1, 0:r34_2, 1:r34_3, 2:r34_4, 3:r34_5, 4:r34_6 +# 34| v34_7(Void) = Call[F] : func:r34_1, 0:r34_2, 1:r34_3, 2:r34_4, 3:r34_5, 4:r34_6 # 34| mu34_8() = ^CallSideEffect : ~m? # 36| r36_1(glval) = VariableAddress[x] : # 36| r36_2(glval) = VariableAddress[b] : @@ -1058,7 +1058,7 @@ jumps.cs: # 13| Block 6 # 13| r13_1() = FunctionAddress[WriteLine] : # 13| r13_2(String) = StringConstant["BreakAndContinue"] : -# 13| v13_3(Void) = Call : func:r13_1, 0:r13_2 +# 13| v13_3(Void) = Call[WriteLine] : func:r13_1, 0:r13_2 # 13| mu13_4() = ^CallSideEffect : ~m? #-----| Goto -> Block 19 @@ -1177,7 +1177,7 @@ jumps.cs: # 37| v37_1(Void) = NoOp : # 38| r38_1() = FunctionAddress[WriteLine] : # 38| r38_2(String) = StringConstant["Done"] : -# 38| v38_3(Void) = Call : func:r38_1, 0:r38_2 +# 38| v38_3(Void) = Call[WriteLine] : func:r38_1, 0:r38_2 # 38| mu38_4() = ^CallSideEffect : ~m? # 5| v5_3(Void) = ReturnVoid : # 5| v5_4(Void) = AliasedUse : ~m? @@ -1191,7 +1191,7 @@ lock.cs: # 7| r7_1(glval) = VariableAddress[object] : # 7| r7_2(Object) = NewObj : # 7| r7_3() = FunctionAddress[Object] : -# 7| v7_4(Void) = Call : func:r7_3, this:r7_2 +# 7| v7_4(Void) = Call[Object] : func:r7_3, this:r7_2 # 7| mu7_5() = ^CallSideEffect : ~m? # 7| mu7_6(Object) = Store : &:r7_1, r7_2 # 8| r8_1(glval) = VariableAddress[#temp8:9] : @@ -1205,15 +1205,15 @@ lock.cs: # 8| r8_9(glval) = VariableAddress[#temp8:9] : # 8| r8_10(Object) = Load : &:r8_9, ~m? # 8| r8_11(glval) = VariableAddress[#temp8:9] : -# 8| v8_12(Void) = Call : func:r8_8, 0:r8_10, 1:r8_11 +# 8| v8_12(Void) = Call[Enter] : func:r8_8, 0:r8_10, 1:r8_11 # 8| mu8_13() = ^CallSideEffect : ~m? # 10| r10_1() = FunctionAddress[WriteLine] : # 10| r10_2(glval) = VariableAddress[object] : # 10| r10_3(Object) = Load : &:r10_2, ~m? # 10| r10_4() = FunctionAddress[ToString] : -# 10| r10_5(String) = Call : func:r10_4, this:r10_3 +# 10| r10_5(String) = Call[ToString] : func:r10_4, this:r10_3 # 10| mu10_6() = ^CallSideEffect : ~m? -# 10| v10_7(Void) = Call : func:r10_1, 0:r10_5 +# 10| v10_7(Void) = Call[WriteLine] : func:r10_1, 0:r10_5 # 10| mu10_8() = ^CallSideEffect : ~m? # 8| r8_14(glval) = VariableAddress[#temp8:9] : # 8| r8_15(Boolean) = Load : &:r8_14, ~m? @@ -1230,7 +1230,7 @@ lock.cs: # 8| r8_17() = FunctionAddress[Exit] : # 8| r8_18(glval) = VariableAddress[#temp8:9] : # 8| r8_19(Object) = Load : &:r8_18, ~m? -# 8| v8_20(Void) = Call : func:r8_17, 0:r8_19 +# 8| v8_20(Void) = Call[Exit] : func:r8_17, 0:r8_19 # 8| mu8_21() = ^CallSideEffect : ~m? #-----| Goto -> Block 1 @@ -1280,13 +1280,13 @@ obj_creation.cs: # 23| r23_2(MyClass) = NewObj : # 23| r23_3() = FunctionAddress[MyClass] : # 23| r23_4(Int32) = Constant[100] : -# 23| v23_5(Void) = Call : func:r23_3, this:r23_2, 0:r23_4 +# 23| v23_5(Void) = Call[MyClass] : func:r23_3, this:r23_2, 0:r23_4 # 23| mu23_6() = ^CallSideEffect : ~m? # 23| mu23_7(MyClass) = Store : &:r23_1, r23_2 # 24| r24_1(glval) = VariableAddress[obj_initlist] : # 24| r24_2(MyClass) = NewObj : # 24| r24_3() = FunctionAddress[MyClass] : -# 24| v24_4(Void) = Call : func:r24_3, this:r24_2 +# 24| v24_4(Void) = Call[MyClass] : func:r24_3, this:r24_2 # 24| mu24_5() = ^CallSideEffect : ~m? # 24| r24_6(Int32) = Constant[101] : # 24| r24_7(glval) = FieldAddress[x] : r24_2 @@ -1302,9 +1302,9 @@ obj_creation.cs: # 27| r27_2(MyClass) = NewObj : # 27| r27_3() = FunctionAddress[MyClass] : # 27| r27_4(Int32) = Constant[100] : -# 27| v27_5(Void) = Call : func:r27_3, this:r27_2, 0:r27_4 +# 27| v27_5(Void) = Call[MyClass] : func:r27_3, this:r27_2, 0:r27_4 # 27| mu27_6() = ^CallSideEffect : ~m? -# 27| v27_7(Void) = Call : func:r27_1, 0:r27_2 +# 27| v27_7(Void) = Call[SomeFun] : func:r27_1, 0:r27_2 # 27| mu27_8() = ^CallSideEffect : ~m? # 21| v21_3(Void) = ReturnVoid : # 21| v21_4(Void) = AliasedUse : ~m? @@ -1321,7 +1321,7 @@ pointers.cs: # 5| r5_2(glval) = VariableAddress[arr] : # 5| r5_3(Int32[]) = Load : &:r5_2, ~m? # 5| r5_4() = FunctionAddress[get_Length] : -# 5| r5_5(Int32) = Call : func:r5_4, this:r5_3 +# 5| r5_5(Int32) = Call[get_Length] : func:r5_4, this:r5_3 # 5| mu5_6() = ^CallSideEffect : ~m? # 5| mu5_7(Int32) = Store : &:r5_1, r5_5 # 6| r6_1(glval) = VariableAddress[b] : @@ -1377,13 +1377,13 @@ pointers.cs: # 26| r26_1(glval) = VariableAddress[o] : # 26| r26_2(MyClass) = NewObj : # 26| r26_3() = FunctionAddress[MyClass] : -# 26| v26_4(Void) = Call : func:r26_3, this:r26_2 +# 26| v26_4(Void) = Call[MyClass] : func:r26_3, this:r26_2 # 26| mu26_5() = ^CallSideEffect : ~m? # 26| mu26_6(MyClass) = Store : &:r26_1, r26_2 # 27| r27_1(glval) = VariableAddress[s] : # 27| r27_2(MyStruct) = NewObj : # 27| r27_3() = FunctionAddress[MyStruct] : -# 27| v27_4(Void) = Call : func:r27_3, this:r27_2 +# 27| v27_4(Void) = Call[MyStruct] : func:r27_3, this:r27_2 # 27| mu27_5() = ^CallSideEffect : ~m? # 27| r27_6(MyStruct) = Load : &:r27_2, ~m? # 27| mu27_7(MyStruct) = Store : &:r27_1, r27_6 @@ -1431,7 +1431,7 @@ pointers.cs: # 40| r40_1() = FunctionAddress[addone] : # 40| r40_2(glval) = VariableAddress[arr] : # 40| r40_3(Int32[]) = Load : &:r40_2, ~m? -# 40| v40_4(Void) = Call : func:r40_1, 0:r40_3 +# 40| v40_4(Void) = Call[addone] : func:r40_1, 0:r40_3 # 40| mu40_5() = ^CallSideEffect : ~m? # 25| v25_3(Void) = ReturnVoid : # 25| v25_4(Void) = AliasedUse : ~m? @@ -1446,7 +1446,7 @@ prop.cs: # 9| r9_1(glval) = VariableAddress[#return] : # 9| r9_2(PropClass) = CopyValue : r7_3 # 9| r9_3() = FunctionAddress[func] : -# 9| r9_4(Int32) = Call : func:r9_3, this:r9_2 +# 9| r9_4(Int32) = Call[func] : func:r9_3, this:r9_2 # 9| mu9_5() = ^CallSideEffect : ~m? # 9| mu9_6(Int32) = Store : &:r9_1, r9_4 # 7| r7_4(glval) = VariableAddress[#return] : @@ -1489,20 +1489,20 @@ prop.cs: # 28| r28_1(glval) = VariableAddress[obj] : # 28| r28_2(PropClass) = NewObj : # 28| r28_3() = FunctionAddress[PropClass] : -# 28| v28_4(Void) = Call : func:r28_3, this:r28_2 +# 28| v28_4(Void) = Call[PropClass] : func:r28_3, this:r28_2 # 28| mu28_5() = ^CallSideEffect : ~m? # 28| mu28_6(PropClass) = Store : &:r28_1, r28_2 # 29| r29_1(glval) = VariableAddress[obj] : # 29| r29_2(PropClass) = Load : &:r29_1, ~m? # 29| r29_3() = FunctionAddress[set_Prop] : # 29| r29_4(Int32) = Constant[5] : -# 29| v29_5(Void) = Call : func:r29_3, this:r29_2, 0:r29_4 +# 29| v29_5(Void) = Call[set_Prop] : func:r29_3, this:r29_2, 0:r29_4 # 29| mu29_6() = ^CallSideEffect : ~m? # 30| r30_1(glval) = VariableAddress[x] : # 30| r30_2(glval) = VariableAddress[obj] : # 30| r30_3(PropClass) = Load : &:r30_2, ~m? # 30| r30_4() = FunctionAddress[get_Prop] : -# 30| r30_5(Int32) = Call : func:r30_4, this:r30_3 +# 30| r30_5(Int32) = Call[get_Prop] : func:r30_4, this:r30_3 # 30| mu30_6() = ^CallSideEffect : ~m? # 30| mu30_7(Int32) = Store : &:r30_1, r30_5 # 26| v26_3(Void) = ReturnVoid : @@ -1529,7 +1529,7 @@ simple_call.cs: # 10| r10_3(glval) = InitializeThis : # 12| r12_1(glval) = VariableAddress[#return] : # 12| r12_2() = FunctionAddress[f] : -# 12| r12_3(Int32) = Call : func:r12_2 +# 12| r12_3(Int32) = Call[f] : func:r12_2 # 12| mu12_4() = ^CallSideEffect : ~m? # 12| mu12_5(Int32) = Store : &:r12_1, r12_3 # 10| r10_4(glval) = VariableAddress[#return] : @@ -1624,7 +1624,7 @@ stmts.cs: # 24| r24_1(glval) = VariableAddress[caseSwitch] : # 24| r24_2(Object) = NewObj : # 24| r24_3() = FunctionAddress[Object] : -# 24| v24_4(Void) = Call : func:r24_3, this:r24_2 +# 24| v24_4(Void) = Call[Object] : func:r24_3, this:r24_2 # 24| mu24_5() = ^CallSideEffect : ~m? # 24| mu24_6(Object) = Store : &:r24_1, r24_2 # 25| r25_1(glval) = VariableAddress[select] : @@ -1709,7 +1709,7 @@ stmts.cs: # 52| r52_1(glval) = VariableAddress[#throw52:17] : # 52| r52_2(Exception) = NewObj : # 52| r52_3() = FunctionAddress[Exception] : -# 52| v52_4(Void) = Call : func:r52_3, this:r52_2 +# 52| v52_4(Void) = Call[Exception] : func:r52_3, this:r52_2 # 52| mu52_5() = ^CallSideEffect : ~m? # 52| mu52_6(Exception) = Store : &:r52_1, r52_2 # 52| v52_7(Void) = ThrowValue : &:r52_1, ~m? @@ -1919,35 +1919,35 @@ using.cs: # 14| r14_1(glval) = VariableAddress[o1] : # 14| r14_2(MyDisposable) = NewObj : # 14| r14_3() = FunctionAddress[MyDisposable] : -# 14| v14_4(Void) = Call : func:r14_3, this:r14_2 +# 14| v14_4(Void) = Call[MyDisposable] : func:r14_3, this:r14_2 # 14| mu14_5() = ^CallSideEffect : ~m? # 14| mu14_6(MyDisposable) = Store : &:r14_1, r14_2 # 16| r16_1(glval) = VariableAddress[o1] : # 16| r16_2(MyDisposable) = Load : &:r16_1, ~m? # 16| r16_3() = FunctionAddress[DoSomething] : -# 16| v16_4(Void) = Call : func:r16_3, this:r16_2 +# 16| v16_4(Void) = Call[DoSomething] : func:r16_3, this:r16_2 # 16| mu16_5() = ^CallSideEffect : ~m? # 19| r19_1(glval) = VariableAddress[o2] : # 19| r19_2(MyDisposable) = NewObj : # 19| r19_3() = FunctionAddress[MyDisposable] : -# 19| v19_4(Void) = Call : func:r19_3, this:r19_2 +# 19| v19_4(Void) = Call[MyDisposable] : func:r19_3, this:r19_2 # 19| mu19_5() = ^CallSideEffect : ~m? # 19| mu19_6(MyDisposable) = Store : &:r19_1, r19_2 # 22| r22_1(glval) = VariableAddress[o2] : # 22| r22_2(MyDisposable) = Load : &:r22_1, ~m? # 22| r22_3() = FunctionAddress[DoSomething] : -# 22| v22_4(Void) = Call : func:r22_3, this:r22_2 +# 22| v22_4(Void) = Call[DoSomething] : func:r22_3, this:r22_2 # 22| mu22_5() = ^CallSideEffect : ~m? # 25| r25_1(glval) = VariableAddress[o3] : # 25| r25_2(MyDisposable) = NewObj : # 25| r25_3() = FunctionAddress[MyDisposable] : -# 25| v25_4(Void) = Call : func:r25_3, this:r25_2 +# 25| v25_4(Void) = Call[MyDisposable] : func:r25_3, this:r25_2 # 25| mu25_5() = ^CallSideEffect : ~m? # 25| mu25_6(MyDisposable) = Store : &:r25_1, r25_2 # 26| r26_1(glval) = VariableAddress[o3] : # 26| r26_2(MyDisposable) = Load : &:r26_1, ~m? # 26| r26_3() = FunctionAddress[DoSomething] : -# 26| v26_4(Void) = Call : func:r26_3, this:r26_2 +# 26| v26_4(Void) = Call[DoSomething] : func:r26_3, this:r26_2 # 26| mu26_5() = ^CallSideEffect : ~m? # 12| v12_3(Void) = ReturnVoid : # 12| v12_4(Void) = AliasedUse : ~m? diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected index 63b053f20f7..3c27e96311b 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected @@ -8,16 +8,16 @@ edges | CollectionFlow.cs:17:18:17:20 | access to local variable as [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | | CollectionFlow.cs:18:20:18:22 | access to local variable as [[]] : A | CollectionFlow.cs:18:14:18:23 | call to method First | | CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:33:53:33:53 | access to local variable a : A | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] | -| CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | | CollectionFlow.cs:33:53:33:53 | access to local variable a : A | CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | -| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | +| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | CollectionFlow.cs:34:14:34:20 | access to array element | -| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | +| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | -| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | +| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | CollectionFlow.cs:36:14:36:24 | call to method First | | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:52:18:52:18 | access to local variable a : A | | CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | CollectionFlow.cs:53:14:53:16 | access to local variable as [[]] : A | @@ -51,59 +51,64 @@ edges | CollectionFlow.cs:109:14:109:17 | access to local variable list [[]] : A | CollectionFlow.cs:109:14:109:20 | access to indexer | | CollectionFlow.cs:110:22:110:25 | access to local variable list [[]] : A | CollectionFlow.cs:376:49:376:52 | list [[]] : A | | CollectionFlow.cs:111:24:111:27 | access to local variable list [[]] : A | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | -| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] | -| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] | CollectionFlow.cs:128:14:128:20 | access to indexer | -| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] | CollectionFlow.cs:378:61:378:64 | dict [[], Value] | -| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | -| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | -| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | -| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] | -| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] | CollectionFlow.cs:150:14:150:20 | access to indexer | -| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] | CollectionFlow.cs:378:61:378:64 | dict [[], Value] | -| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | -| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | -| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | -| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] | -| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] | CollectionFlow.cs:171:14:171:20 | access to indexer | -| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] | CollectionFlow.cs:378:61:378:64 | dict [[], Value] | -| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | -| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | -| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | -| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] | -| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:127:19:127:19 | access to local variable a : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:19:127:19 | access to local variable a : A | CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | +| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:128:14:128:20 | access to indexer | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | +| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:149:52:149:52 | access to local variable a : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:52:149:52 | access to local variable a : A | CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:150:14:150:20 | access to indexer | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | +| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:170:53:170:53 | access to local variable a : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:53:170:53 | access to local variable a : A | CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:171:14:171:20 | access to indexer | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | +| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | +| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:192:49:192:49 | access to local variable a : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:49:192:49 | access to local variable a : A | CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | CollectionFlow.cs:193:14:193:30 | call to method First | -| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] | CollectionFlow.cs:380:59:380:62 | dict [[], Key] | -| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | -| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | -| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] | -| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | +| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | +| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | +| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | +| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:211:48:211:48 | access to local variable a : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:48:211:48 | access to local variable a : A | CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | CollectionFlow.cs:212:14:212:30 | call to method First | -| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] | CollectionFlow.cs:380:59:380:62 | dict [[], Key] | -| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | -| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | +| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | +| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | +| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | | CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:230:27:230:27 | access to local variable a : A | | CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | CollectionFlow.cs:231:27:231:29 | access to local variable as [[]] : A | | CollectionFlow.cs:230:27:230:27 | access to local variable a : A | CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | @@ -122,10 +127,10 @@ edges | CollectionFlow.cs:264:26:264:45 | call to method GetEnumerator [Current] : A | CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | | CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | CollectionFlow.cs:266:18:266:35 | access to property Current | | CollectionFlow.cs:280:17:280:23 | object creation of type A : A | CollectionFlow.cs:282:43:282:43 | access to local variable a : A | -| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] | CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] | -| CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] | +| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | +| CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | | CollectionFlow.cs:282:43:282:43 | access to local variable a : A | CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | -| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | +| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | | CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | CollectionFlow.cs:285:18:285:24 | access to property Key | | CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:308:23:308:23 | access to local variable a : A | @@ -155,11 +160,17 @@ edges | CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | CollectionFlow.cs:374:52:374:56 | access to array element | | CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | CollectionFlow.cs:374:52:374:56 | access to array element | | CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | -| CollectionFlow.cs:378:61:378:64 | dict [[], Value] | CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] | -| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] | CollectionFlow.cs:378:75:378:81 | access to indexer | -| CollectionFlow.cs:380:59:380:62 | dict [[], Key] | CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] | -| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | +| CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | +| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | CollectionFlow.cs:378:75:378:81 | access to indexer | +| CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | +| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | CollectionFlow.cs:380:73:380:89 | call to method First | | CollectionFlow.cs:396:49:396:52 | args [[]] : A | CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | | CollectionFlow.cs:396:49:396:52 | args [[]] : A | CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | @@ -175,16 +186,16 @@ nodes | CollectionFlow.cs:18:14:18:23 | call to method First | semmle.label | call to method First | | CollectionFlow.cs:18:20:18:22 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | | CollectionFlow.cs:32:17:32:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | semmle.label | { ..., ... } [As, []] | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | semmle.label | { ..., ... } [As, []] : A | | CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | | CollectionFlow.cs:33:53:33:53 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] | semmle.label | access to local variable c [As, []] | +| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | | CollectionFlow.cs:34:14:34:20 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] | semmle.label | access to local variable c [As, []] | +| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | | CollectionFlow.cs:36:14:36:24 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] | semmle.label | access to local variable c [As, []] | +| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | semmle.label | object creation of type A : A | | CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | semmle.label | [post] access to local variable as [[]] : A | @@ -219,58 +230,63 @@ nodes | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | semmle.label | call to method ListFirst | | CollectionFlow.cs:111:24:111:27 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | semmle.label | [post] access to local variable dict [[], Value] | -| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | semmle.label | [post] access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:19:127:19 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:128:14:128:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | -| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | semmle.label | { ..., ... } [[], Value] | -| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | semmle.label | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:149:52:149:52 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:150:14:150:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | -| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | semmle.label | { ..., ... } [[], Value] | -| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | semmle.label | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:170:53:170:53 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:171:14:171:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | -| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:191:17:191:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | semmle.label | { ..., ... } [[], Key] | -| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | semmle.label | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:192:49:192:49 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | | CollectionFlow.cs:193:14:193:30 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | semmle.label | call to method DictKeysFirst | -| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | semmle.label | call to method DictFirstKey | -| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:210:17:210:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | semmle.label | { ..., ... } [[], Key] | -| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | semmle.label | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:211:48:211:48 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | | CollectionFlow.cs:212:14:212:30 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | semmle.label | call to method DictKeysFirst | -| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | semmle.label | call to method DictFirstKey | -| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:229:17:229:23 | object creation of type A : A | semmle.label | object creation of type A : A | | CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | | CollectionFlow.cs:230:27:230:27 | access to local variable a : A | semmle.label | access to local variable a : A | @@ -292,10 +308,10 @@ nodes | CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | semmle.label | access to local variable enumerator [Current] : A | | CollectionFlow.cs:266:18:266:35 | access to property Current | semmle.label | access to property Current | | CollectionFlow.cs:280:17:280:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] | semmle.label | [post] access to local variable list [[], Key] | +| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | semmle.label | [post] access to local variable list [[], Key] : A | | CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | semmle.label | object creation of type KeyValuePair [Key] : A | | CollectionFlow.cs:282:43:282:43 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] | semmle.label | access to local variable list [[], Key] | +| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | semmle.label | access to local variable list [[], Key] : A | | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | semmle.label | kvp [Key] : A | | CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | semmle.label | access to parameter kvp [Key] : A | | CollectionFlow.cs:285:18:285:24 | access to property Key | semmle.label | access to property Key | @@ -327,13 +343,19 @@ nodes | CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | semmle.label | access to parameter ts [[]] : A | | CollectionFlow.cs:374:52:374:56 | access to array element | semmle.label | access to array element | | CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | | CollectionFlow.cs:376:63:376:69 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:378:61:378:64 | dict [[], Value] | semmle.label | dict [[], Value] | -| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] | semmle.label | access to parameter dict [[], Value] | +| CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | semmle.label | dict [[], Value] : A | +| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | semmle.label | access to parameter dict [[], Value] : A | | CollectionFlow.cs:378:75:378:81 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:380:59:380:62 | dict [[], Key] | semmle.label | dict [[], Key] | -| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] | semmle.label | access to parameter dict [[], Key] | +| CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | semmle.label | dict [[], Key] : A | +| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | semmle.label | access to parameter dict [[], Key] : A | | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | | CollectionFlow.cs:380:73:380:89 | call to method First | semmle.label | call to method First | | CollectionFlow.cs:396:49:396:52 | args [[]] : A | semmle.label | args [[]] : A | diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql index 7c2c322f47f..7d02ffa7bcb 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql @@ -16,6 +16,8 @@ class Conf extends DataFlow::Configuration { mc.getAnArgument() = sink.asExpr() ) } + + override int fieldFlowBranchLimit() { result = 10 } } from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs index 7a50bcc057d..9479cca940d 100644 --- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs @@ -99,5 +99,13 @@ class DelegateFlow M2(LocalFunction); } + public void M15() + { + Func f = () => 42; + new Lazy(f); + f = () => 43; + new Lazy(f); + } + public delegate void MyDelegate(); } diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected index ecdd7f9e5f8..c8893ec9b0e 100644 --- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected @@ -1,3 +1,7 @@ +summaryDelegateCall +| file://:0:0:0:0 | [summary] valueFactory | DelegateFlow.cs:104:23:104:30 | (...) => ... | DelegateFlow.cs:105:23:105:23 | access to local variable f | +| file://:0:0:0:0 | [summary] valueFactory | DelegateFlow.cs:106:13:106:20 | (...) => ... | DelegateFlow.cs:107:23:107:23 | access to local variable f | +delegateCall | DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:17:12:17:13 | delegate creation of type Action | | DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:22:12:22:12 | access to parameter a | | DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... | DelegateFlow.cs:16:12:16:19 | (...) => ... | diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql index 2b0386491ea..c01feabc2f9 100644 --- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql @@ -1,5 +1,22 @@ import csharp +import semmle.code.csharp.dataflow.internal.DataFlowPrivate +import semmle.code.csharp.dataflow.internal.DelegateDataFlow -from DelegateCall dc, Callable c, CallContext::CallContext cc -where c = dc.getARuntimeTarget(cc) -select dc, c, cc +private class NodeAdjusted extends TNode { + string toString() { result = this.(DataFlow::Node).toString() } + + Location getLocation() { + exists(Location l | + l = this.(DataFlow::Node).getLocation() and + if l instanceof SourceLocation then result = l else result instanceof EmptyLocation + ) + } +} + +query predicate summaryDelegateCall(NodeAdjusted sink, Callable c, CallContext::CallContext cc) { + c = sink.(SummaryDelegateParameterSink).getARuntimeTarget(cc) +} + +query predicate delegateCall(DelegateCall dc, Callable c, CallContext::CallContext cc) { + c = dc.getARuntimeTarget(cc) +} diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index ff675293cf7..8f1a95d58c7 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -27,49 +27,49 @@ edges | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | A.cs:98:22:98:36 | ... ? ... : ... [c] : C | | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | | A.cs:97:19:97:25 | object creation of type C : C | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | -| A.cs:98:13:98:16 | [post] this access [b, c] | A.cs:105:17:105:29 | object creation of type D [b, c] | +| A.cs:98:13:98:16 | [post] this access [b, c] : C | A.cs:105:17:105:29 | object creation of type D [b, c] : C | | A.cs:98:13:98:16 | [post] this access [b] : B | A.cs:105:17:105:29 | object creation of type D [b] : B | | A.cs:98:22:98:36 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [b] : B | -| A.cs:98:22:98:36 | ... ? ... : ... [c] : C | A.cs:98:13:98:16 | [post] this access [b, c] | +| A.cs:98:22:98:36 | ... ? ... : ... [c] : C | A.cs:98:13:98:16 | [post] this access [b, c] : C | | A.cs:98:30:98:36 | object creation of type B : B | A.cs:98:22:98:36 | ... ? ... : ... : B | | A.cs:104:17:104:23 | object creation of type B : B | A.cs:105:23:105:23 | access to local variable b : B | -| A.cs:105:17:105:29 | object creation of type D [b, c] | A.cs:107:14:107:14 | access to local variable d [b, c] | +| A.cs:105:17:105:29 | object creation of type D [b, c] : C | A.cs:107:14:107:14 | access to local variable d [b, c] : C | | A.cs:105:17:105:29 | object creation of type D [b] : B | A.cs:106:14:106:14 | access to local variable d [b] : B | | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | A.cs:108:14:108:14 | access to local variable b [c] : C | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:105:17:105:29 | object creation of type D [b] : B | | A.cs:106:14:106:14 | access to local variable d [b] : B | A.cs:106:14:106:16 | access to field b | -| A.cs:107:14:107:14 | access to local variable d [b, c] | A.cs:107:14:107:16 | access to field b [c] : C | +| A.cs:107:14:107:14 | access to local variable d [b, c] : C | A.cs:107:14:107:16 | access to field b [c] : C | | A.cs:107:14:107:16 | access to field b [c] : C | A.cs:107:14:107:18 | access to field c | | A.cs:108:14:108:14 | access to local variable b [c] : C | A.cs:108:14:108:16 | access to field c | | A.cs:113:17:113:23 | object creation of type B : B | A.cs:114:29:114:29 | access to local variable b : B | | A.cs:114:18:114:54 | object creation of type MyList [head] : B | A.cs:115:35:115:36 | access to local variable l1 [head] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:114:18:114:54 | object creation of type MyList [head] : B | -| A.cs:115:18:115:37 | object creation of type MyList [next, head] | A.cs:116:35:116:36 | access to local variable l2 [next, head] | -| A.cs:115:35:115:36 | access to local variable l1 [head] : B | A.cs:115:18:115:37 | object creation of type MyList [next, head] | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | -| A.cs:116:35:116:36 | access to local variable l2 [next, head] | A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | -| A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | A.cs:119:14:119:20 | access to field next [next, head] | -| A.cs:119:14:119:20 | access to field next [next, head] | A.cs:119:14:119:25 | access to field next [head] : B | +| A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | +| A.cs:115:35:115:36 | access to local variable l1 [head] : B | A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | A.cs:119:14:119:20 | access to field next [next, head] : B | +| A.cs:119:14:119:20 | access to field next [next, head] : B | A.cs:119:14:119:25 | access to field next [head] : B | | A.cs:119:14:119:25 | access to field next [head] : B | A.cs:119:14:119:30 | access to field head | -| A.cs:121:41:121:41 | access to local variable l [next, head] | A.cs:121:41:121:46 | access to field next [head] : B | -| A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | A.cs:121:41:121:46 | access to field next [next, head] | +| A.cs:121:41:121:41 | access to local variable l [next, head] : B | A.cs:121:41:121:46 | access to field next [head] : B | +| A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | A.cs:121:41:121:46 | access to field next [next, head] : B | | A.cs:121:41:121:46 | access to field next [head] : B | A.cs:123:18:123:18 | access to local variable l [head] : B | -| A.cs:121:41:121:46 | access to field next [next, head] | A.cs:121:41:121:41 | access to local variable l [next, head] | +| A.cs:121:41:121:46 | access to field next [next, head] : B | A.cs:121:41:121:41 | access to local variable l [next, head] : B | | A.cs:123:18:123:18 | access to local variable l [head] : B | A.cs:123:18:123:23 | access to field head | | B.cs:5:17:5:26 | object creation of type Elem : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | -| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | -| B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | -| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | +| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | +| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | B.cs:8:14:8:26 | access to field elem1 | | B.cs:14:17:14:26 | object creation of type Elem : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | -| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | -| B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | -| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | +| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | +| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | B.cs:18:14:18:26 | access to field elem2 | | C.cs:3:18:3:19 | [post] this access [s1] : Elem | C.cs:12:15:12:21 | object creation of type C [s1] : Elem | | C.cs:3:23:3:32 | object creation of type Elem : Elem | C.cs:3:18:3:19 | [post] this access [s1] : Elem | @@ -138,37 +138,37 @@ edges | F.cs:23:32:23:32 | access to local variable o : Object | F.cs:23:21:23:34 | { ..., ... } [Field2] : Object | | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | F.cs:25:14:25:21 | access to field Field2 | | G.cs:7:18:7:27 | object creation of type Elem : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | -| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | -| G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | -| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:15:18:15:27 | object creation of type Elem : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | -| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | -| G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | -| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:23:18:23:27 | object creation of type Elem : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | -| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | -| G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | -| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:31:18:31:27 | object creation of type Elem : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | -| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | -| G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | -| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | -| G.cs:37:38:37:39 | b2 [Box1, Elem] | G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | -| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | +| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | +| G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | G.cs:39:14:39:35 | call to method GetElem | | G.cs:44:18:44:27 | object creation of type Elem : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | -| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | -| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | -| G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | +| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | +| G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | -| G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | -| G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | -| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | -| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | +| G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | +| G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | +| G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | G.cs:52:14:52:31 | access to field Elem | | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | H.cs:24:27:24:27 | access to local variable a [FieldA] : Object | | H.cs:23:20:23:31 | object creation of type Object : Object | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | @@ -206,12 +206,12 @@ edges | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:164:22:164:22 | access to local variable o : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | +| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | -| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | | H.cs:165:17:165:28 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | +| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | H.cs:165:21:165:28 | access to field FieldA : B | | H.cs:165:21:165:28 | access to field FieldA : B | H.cs:165:17:165:28 | (...) ... : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | @@ -273,19 +273,19 @@ nodes | A.cs:89:14:89:16 | access to field c | semmle.label | access to field c | | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | semmle.label | [post] access to parameter b [c] : C | | A.cs:97:19:97:25 | object creation of type C : C | semmle.label | object creation of type C : C | -| A.cs:98:13:98:16 | [post] this access [b, c] | semmle.label | [post] this access [b, c] | +| A.cs:98:13:98:16 | [post] this access [b, c] : C | semmle.label | [post] this access [b, c] : C | | A.cs:98:13:98:16 | [post] this access [b] : B | semmle.label | [post] this access [b] : B | | A.cs:98:22:98:36 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | | A.cs:98:22:98:36 | ... ? ... : ... [c] : C | semmle.label | ... ? ... : ... [c] : C | | A.cs:98:30:98:36 | object creation of type B : B | semmle.label | object creation of type B : B | | A.cs:104:17:104:23 | object creation of type B : B | semmle.label | object creation of type B : B | -| A.cs:105:17:105:29 | object creation of type D [b, c] | semmle.label | object creation of type D [b, c] | +| A.cs:105:17:105:29 | object creation of type D [b, c] : C | semmle.label | object creation of type D [b, c] : C | | A.cs:105:17:105:29 | object creation of type D [b] : B | semmle.label | object creation of type D [b] : B | | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | semmle.label | [post] access to local variable b [c] : C | | A.cs:105:23:105:23 | access to local variable b : B | semmle.label | access to local variable b : B | | A.cs:106:14:106:14 | access to local variable d [b] : B | semmle.label | access to local variable d [b] : B | | A.cs:106:14:106:16 | access to field b | semmle.label | access to field b | -| A.cs:107:14:107:14 | access to local variable d [b, c] | semmle.label | access to local variable d [b, c] | +| A.cs:107:14:107:14 | access to local variable d [b, c] : C | semmle.label | access to local variable d [b, c] : C | | A.cs:107:14:107:16 | access to field b [c] : C | semmle.label | access to field b [c] : C | | A.cs:107:14:107:18 | access to field c | semmle.label | access to field c | | A.cs:108:14:108:14 | access to local variable b [c] : C | semmle.label | access to local variable b [c] : C | @@ -293,34 +293,34 @@ nodes | A.cs:113:17:113:23 | object creation of type B : B | semmle.label | object creation of type B : B | | A.cs:114:18:114:54 | object creation of type MyList [head] : B | semmle.label | object creation of type MyList [head] : B | | A.cs:114:29:114:29 | access to local variable b : B | semmle.label | access to local variable b : B | -| A.cs:115:18:115:37 | object creation of type MyList [next, head] | semmle.label | object creation of type MyList [next, head] | +| A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | semmle.label | object creation of type MyList [next, head] : B | | A.cs:115:35:115:36 | access to local variable l1 [head] : B | semmle.label | access to local variable l1 [head] : B | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | semmle.label | object creation of type MyList [next, next, ... (3)] | -| A.cs:116:35:116:36 | access to local variable l2 [next, head] | semmle.label | access to local variable l2 [next, head] | -| A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | semmle.label | access to local variable l3 [next, next, ... (3)] | -| A.cs:119:14:119:20 | access to field next [next, head] | semmle.label | access to field next [next, head] | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | semmle.label | object creation of type MyList [next, next, head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | semmle.label | access to local variable l2 [next, head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | semmle.label | access to local variable l3 [next, next, head] : B | +| A.cs:119:14:119:20 | access to field next [next, head] : B | semmle.label | access to field next [next, head] : B | | A.cs:119:14:119:25 | access to field next [head] : B | semmle.label | access to field next [head] : B | | A.cs:119:14:119:30 | access to field head | semmle.label | access to field head | -| A.cs:121:41:121:41 | access to local variable l [next, head] | semmle.label | access to local variable l [next, head] | -| A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | semmle.label | access to local variable l [next, next, ... (3)] | +| A.cs:121:41:121:41 | access to local variable l [next, head] : B | semmle.label | access to local variable l [next, head] : B | +| A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | semmle.label | access to local variable l [next, next, head] : B | | A.cs:121:41:121:46 | access to field next [head] : B | semmle.label | access to field next [head] : B | -| A.cs:121:41:121:46 | access to field next [next, head] | semmle.label | access to field next [next, head] | +| A.cs:121:41:121:46 | access to field next [next, head] : B | semmle.label | access to field next [next, head] : B | | A.cs:123:18:123:18 | access to local variable l [head] : B | semmle.label | access to local variable l [head] : B | | A.cs:123:18:123:23 | access to field head | semmle.label | access to field head | | B.cs:5:17:5:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | semmle.label | object creation of type Box1 [elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | semmle.label | object creation of type Box2 [box1, elem1] | +| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | semmle.label | object creation of type Box2 [box1, elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | semmle.label | access to local variable b1 [elem1] : Elem | -| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | semmle.label | access to local variable b2 [box1, elem1] | +| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | semmle.label | access to local variable b2 [box1, elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | semmle.label | access to field box1 [elem1] : Elem | | B.cs:8:14:8:26 | access to field elem1 | semmle.label | access to field elem1 | | B.cs:14:17:14:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | semmle.label | object creation of type Box1 [elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | semmle.label | object creation of type Box2 [box1, elem2] | +| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | semmle.label | object creation of type Box2 [box1, elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | semmle.label | access to local variable b1 [elem2] : Elem | -| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | semmle.label | access to local variable b2 [box1, elem2] | +| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | semmle.label | access to local variable b2 [box1, elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | semmle.label | access to field box1 [elem2] : Elem | | B.cs:18:14:18:26 | access to field elem2 | semmle.label | access to field elem2 | | C.cs:3:18:3:19 | [post] this access [s1] : Elem | semmle.label | [post] this access [s1] : Elem | @@ -399,38 +399,38 @@ nodes | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | semmle.label | access to local variable f [Field2] : Object | | F.cs:25:14:25:21 | access to field Field2 | semmle.label | access to field Field2 | | G.cs:7:18:7:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:15:18:15:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:23:18:23:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | semmle.label | [post] call to method GetBox1 [Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:31:18:31:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | semmle.label | [post] call to method GetBox1 [Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | -| G.cs:37:38:37:39 | b2 [Box1, Elem] | semmle.label | b2 [Box1, Elem] | -| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | semmle.label | access to parameter b2 [Box1, Elem] | +| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | +| G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | semmle.label | b2 [Box1, Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | semmle.label | access to parameter b2 [Box1, Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | semmle.label | call to method GetBox1 [Elem] : Elem | | G.cs:39:14:39:35 | call to method GetElem | semmle.label | call to method GetElem | | G.cs:44:18:44:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | semmle.label | [post] access to field boxfield [Box1, Elem] | -| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | semmle.label | [post] this access [boxfield, Box1, ... (3)] | +| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | semmle.label | [post] access to field boxfield [Box1, Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | semmle.label | [post] this access [boxfield, Box1, Elem] : Elem | | G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | semmle.label | this access [boxfield, Box1, ... (3)] | -| G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | semmle.label | this [boxfield, Box1, ... (3)] | -| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | semmle.label | access to field boxfield [Box1, Elem] | -| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | semmle.label | this access [boxfield, Box1, ... (3)] | +| G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | semmle.label | this access [boxfield, Box1, Elem] : Elem | +| G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | semmle.label | this [boxfield, Box1, Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | semmle.label | access to field boxfield [Box1, Elem] : Elem | +| G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | semmle.label | this access [boxfield, Box1, Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | semmle.label | access to field Box1 [Elem] : Elem | | G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem | | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object | @@ -476,12 +476,12 @@ nodes | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | semmle.label | [post] access to parameter a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | | H.cs:163:17:163:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | semmle.label | [post] access to local variable a [FieldA, FieldB] | +| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | semmle.label | [post] access to local variable a [FieldA, FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | semmle.label | [post] access to local variable a [FieldA] : B | | H.cs:164:22:164:22 | access to local variable o : Object | semmle.label | access to local variable o : Object | | H.cs:165:17:165:28 | (...) ... : B | semmle.label | (...) ... : B | | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | semmle.label | (...) ... [FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | semmle.label | access to local variable a [FieldA, FieldB] | +| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | semmle.label | access to local variable a [FieldA, FieldB] : Object | | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | semmle.label | access to local variable a [FieldA] : B | | H.cs:165:21:165:28 | access to field FieldA : B | semmle.label | access to field FieldA : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | semmle.label | access to field FieldA [FieldB] : Object | diff --git a/csharp/ql/test/library-tests/dataflow/global/Common.qll b/csharp/ql/test/library-tests/dataflow/global/Common.qll index 1ecaebd9f94..17d4b220876 100644 --- a/csharp/ql/test/library-tests/dataflow/global/Common.qll +++ b/csharp/ql/test/library-tests/dataflow/global/Common.qll @@ -10,10 +10,9 @@ class Config extends DataFlow::Configuration { } override predicate isSink(DataFlow::Node sink) { - sink.asExpr() instanceof Access and exists(MethodCall mc | mc.getTarget().getName() = "Check" and - mc.getAnArgument() = sink.asExpr().getParent*() + mc.getAnArgument() = sink.asExpr() ) } } diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected index 6c3ddef4547..596f6f53274 100644 --- a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected @@ -54,7 +54,7 @@ | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | -| Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected index 68fdc98c16b..c13ef765d93 100644 --- a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected +++ b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected @@ -228,7 +228,8 @@ edges | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | Splitting.cs:11:19:11:19 | access to local variable x | | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | -| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value : String | +| Splitting.cs:21:28:21:32 | access to parameter value : String | Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:31:19:31:25 | [b (line 24): false] access to parameter tainted : String | @@ -436,7 +437,8 @@ nodes | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | semmle.label | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | semmle.label | access to local variable x | | Splitting.cs:21:9:21:11 | value : String | semmle.label | value : String | -| Splitting.cs:21:28:21:32 | access to parameter value | semmle.label | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | semmle.label | call to method Return | +| Splitting.cs:21:28:21:32 | access to parameter value : String | semmle.label | access to parameter value : String | | Splitting.cs:24:28:24:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | semmle.label | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | semmle.label | [b (line 24): true] access to parameter tainted : String | @@ -516,5 +518,5 @@ nodes | GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | | GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | access to parameter sinkParam8 | | GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | access to parameter sinkParam9 | -| Splitting.cs:21:28:21:32 | access to parameter value | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:28:21:32 | access to parameter value | access to parameter value | | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | access to property SinkProperty0 | +| Splitting.cs:21:21:21:33 | call to method Return | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:21:21:33 | call to method Return | call to method Return | diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected index 730e18b8726..1acaa308256 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected @@ -1,25 +1,20 @@ | Capture.cs:33:9:33:40 | call to method Select | return | Capture.cs:33:9:33:40 | call to method Select | | Capture.cs:33:9:33:40 | call to method Select | yield return | Capture.cs:33:9:33:40 | call to method Select | | Capture.cs:33:9:33:50 | call to method ToArray | return | Capture.cs:33:9:33:50 | call to method ToArray | -| Capture.cs:33:30:33:39 | [implicit call] access to local variable captureIn3 | return | Capture.cs:33:30:33:39 | [output] access to local variable captureIn3 | | Capture.cs:71:9:71:21 | call to local function CaptureOut1 | captured sink30 | Capture.cs:71:9:71:21 | SSA call def(sink30) | | Capture.cs:83:9:83:21 | [transitive] call to local function CaptureOut2 | captured sink31 | Capture.cs:83:9:83:21 | SSA call def(sink31) | -| Capture.cs:92:9:92:41 | call to method Select | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) | +| Capture.cs:92:9:92:41 | [transitive] call to method Select | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) | | Capture.cs:92:9:92:41 | call to method Select | return | Capture.cs:92:9:92:41 | call to method Select | | Capture.cs:92:9:92:41 | call to method Select | yield return | Capture.cs:92:9:92:41 | call to method Select | | Capture.cs:92:9:92:51 | call to method ToArray | return | Capture.cs:92:9:92:51 | call to method ToArray | -| Capture.cs:92:30:92:40 | [implicit call] access to local variable captureOut3 | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) | -| Capture.cs:92:30:92:40 | [implicit call] access to local variable captureOut3 | return | Capture.cs:92:30:92:40 | [output] access to local variable captureOut3 | | Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured nonSink0 | Capture.cs:121:9:121:35 | SSA call def(nonSink0) | | Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured sink40 | Capture.cs:121:9:121:35 | SSA call def(sink40) | | Capture.cs:132:9:132:25 | call to local function CaptureThrough1 | captured sink33 | Capture.cs:132:9:132:25 | SSA call def(sink33) | | Capture.cs:144:9:144:25 | [transitive] call to local function CaptureThrough2 | captured sink34 | Capture.cs:144:9:144:25 | SSA call def(sink34) | -| Capture.cs:153:9:153:45 | call to method Select | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) | +| Capture.cs:153:9:153:45 | [transitive] call to method Select | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) | | Capture.cs:153:9:153:45 | call to method Select | return | Capture.cs:153:9:153:45 | call to method Select | | Capture.cs:153:9:153:45 | call to method Select | yield return | Capture.cs:153:9:153:45 | call to method Select | | Capture.cs:153:9:153:55 | call to method ToArray | return | Capture.cs:153:9:153:55 | call to method ToArray | -| Capture.cs:153:30:153:44 | [implicit call] access to local variable captureThrough3 | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) | -| Capture.cs:153:30:153:44 | [implicit call] access to local variable captureThrough3 | return | Capture.cs:153:30:153:44 | [output] access to local variable captureThrough3 | | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 | return | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 | | Capture.cs:168:9:168:32 | call to local function CaptureThrough5 | captured sink37 | Capture.cs:168:9:168:32 | SSA call def(sink37) | | Capture.cs:191:20:191:22 | call to local function M | return | Capture.cs:191:20:191:22 | call to local function M | @@ -32,6 +27,7 @@ | GlobalDataFlow.cs:32:9:32:29 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:32:9:32:29 | access to property NonSinkProperty1 | | GlobalDataFlow.cs:33:15:33:35 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:33:15:33:35 | access to property NonSinkProperty1 | | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 | +| GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | qualifier | GlobalDataFlow.cs:37:26:37:41 | [post] typeof(...) | | GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | return | GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 | | GlobalDataFlow.cs:39:9:39:37 | call to method Invoke | return | GlobalDataFlow.cs:39:9:39:37 | call to method Invoke | @@ -41,11 +37,14 @@ | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 | return | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 | | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 | | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 | +| GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | qualifier | GlobalDataFlow.cs:65:9:65:18 | [post] this access | | GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | return | GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 | return | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 | +| GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | qualifier | GlobalDataFlow.cs:68:9:68:21 | [post] this access | | GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | return | GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | | GlobalDataFlow.cs:71:21:71:46 | call to method Return | return | GlobalDataFlow.cs:71:21:71:46 | call to method Return | | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 | +| GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | qualifier | GlobalDataFlow.cs:73:29:73:44 | [post] typeof(...) | | GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | return | GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke | return | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke | | GlobalDataFlow.cs:76:9:76:46 | call to method ReturnOut | out | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) | @@ -58,21 +57,14 @@ | GlobalDataFlow.cs:83:22:83:87 | call to method Select | return | GlobalDataFlow.cs:83:22:83:87 | call to method Select | | GlobalDataFlow.cs:83:22:83:87 | call to method Select | yield return | GlobalDataFlow.cs:83:22:83:87 | call to method Select | | GlobalDataFlow.cs:83:22:83:95 | call to method First | return | GlobalDataFlow.cs:83:22:83:95 | call to method First | -| GlobalDataFlow.cs:83:76:83:86 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:83:76:83:86 | [output] delegate creation of type Func | | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | yield return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | | GlobalDataFlow.cs:85:22:85:136 | call to method First | return | GlobalDataFlow.cs:85:22:85:136 | call to method First | -| GlobalDataFlow.cs:85:117:85:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:85:117:85:127 | [output] (...) => ... | | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | yield return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | | GlobalDataFlow.cs:87:22:87:136 | call to method First | return | GlobalDataFlow.cs:87:22:87:136 | call to method First | -| GlobalDataFlow.cs:87:117:87:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:87:117:87:127 | [output] (...) => ... | | GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate | return | GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate | -| GlobalDataFlow.cs:89:83:89:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:89:83:89:101 | [output] (...) => ... | -| GlobalDataFlow.cs:89:104:89:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:89:104:89:109 | [output] (...) => ... | | GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate | return | GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate | -| GlobalDataFlow.cs:91:83:91:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:91:83:91:101 | [output] (...) => ... | -| GlobalDataFlow.cs:91:104:91:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:91:104:91:109 | [output] (...) => ... | | GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | out | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) | | GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | ref | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) | | GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | return | GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | @@ -80,6 +72,7 @@ | GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | ref | GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) | | GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | return | GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | | GlobalDataFlow.cs:101:24:101:33 | call to method Return | return | GlobalDataFlow.cs:101:24:101:33 | call to method Return | +| GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | qualifier | GlobalDataFlow.cs:103:28:103:43 | [post] typeof(...) | | GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | return | GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | | GlobalDataFlow.cs:103:28:103:103 | call to method Invoke | return | GlobalDataFlow.cs:103:28:103:103 | call to method Invoke | | GlobalDataFlow.cs:105:9:105:49 | call to method ReturnOut | out | GlobalDataFlow.cs:105:27:105:34 | SSA def(nonSink0) | @@ -95,24 +88,15 @@ | GlobalDataFlow.cs:115:20:115:82 | call to method Select | return | GlobalDataFlow.cs:115:20:115:82 | call to method Select | | GlobalDataFlow.cs:115:20:115:82 | call to method Select | yield return | GlobalDataFlow.cs:115:20:115:82 | call to method Select | | GlobalDataFlow.cs:115:20:115:90 | call to method First | return | GlobalDataFlow.cs:115:20:115:90 | call to method First | -| GlobalDataFlow.cs:115:76:115:81 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:115:76:115:81 | [output] (...) => ... | | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | yield return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | | GlobalDataFlow.cs:117:20:117:134 | call to method First | return | GlobalDataFlow.cs:117:20:117:134 | call to method First | -| GlobalDataFlow.cs:117:115:117:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:117:115:117:125 | [output] (...) => ... | | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | yield return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | | GlobalDataFlow.cs:119:20:119:134 | call to method First | return | GlobalDataFlow.cs:119:20:119:134 | call to method First | -| GlobalDataFlow.cs:119:115:119:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:119:115:119:125 | [output] (...) => ... | | GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate | return | GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate | -| GlobalDataFlow.cs:121:81:121:95 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:121:81:121:95 | [output] (...) => ... | -| GlobalDataFlow.cs:121:98:121:103 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:121:98:121:103 | [output] (...) => ... | | GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate | return | GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate | -| GlobalDataFlow.cs:123:81:123:99 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:123:81:123:99 | [output] (...) => ... | -| GlobalDataFlow.cs:123:102:123:108 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:123:102:123:108 | [output] (...) => ... | | GlobalDataFlow.cs:125:20:125:107 | call to method Aggregate | return | GlobalDataFlow.cs:125:20:125:107 | call to method Aggregate | -| GlobalDataFlow.cs:125:86:125:98 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:125:86:125:98 | [output] (...) => ... | -| GlobalDataFlow.cs:125:101:125:106 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:125:101:125:106 | [output] (...) => ... | | GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | out | GlobalDataFlow.cs:128:38:128:45 | SSA def(nonSink2) | | GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | ref | GlobalDataFlow.cs:128:38:128:45 | SSA def(nonSink2) | | GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | return | GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | @@ -125,20 +109,28 @@ | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc | | GlobalDataFlow.cs:148:20:148:40 | call to method ApplyFunc | return | GlobalDataFlow.cs:148:20:148:40 | call to method ApplyFunc | | GlobalDataFlow.cs:150:20:150:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:150:20:150:44 | call to method ApplyFunc | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out | qualifier | GlobalDataFlow.cs:154:21:154:25 | [post] this access | | GlobalDataFlow.cs:154:21:154:25 | call to method Out | return | GlobalDataFlow.cs:154:21:154:25 | call to method Out | | GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | out | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) | +| GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | qualifier | GlobalDataFlow.cs:157:9:157:25 | [post] this access | | GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | ref | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) | | GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | out | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) | +| GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | qualifier | GlobalDataFlow.cs:160:9:160:25 | [post] this access | | GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | ref | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | qualifier | GlobalDataFlow.cs:162:22:162:31 | [post] this access | | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | yield return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | | GlobalDataFlow.cs:162:22:162:39 | call to method First | return | GlobalDataFlow.cs:162:22:162:39 | call to method First | | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam | return | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam | +| GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | qualifier | GlobalDataFlow.cs:168:20:168:27 | [post] this access | | GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | return | GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | | GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | out | GlobalDataFlow.cs:170:23:170:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | qualifier | GlobalDataFlow.cs:170:9:170:31 | [post] this access | | GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | ref | GlobalDataFlow.cs:170:23:170:30 | SSA def(nonSink0) | | GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | out | GlobalDataFlow.cs:172:23:172:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | qualifier | GlobalDataFlow.cs:172:9:172:31 | [post] this access | | GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | ref | GlobalDataFlow.cs:172:23:172:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | qualifier | GlobalDataFlow.cs:174:20:174:32 | [post] this access | | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | yield return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | | GlobalDataFlow.cs:174:20:174:40 | call to method First | return | GlobalDataFlow.cs:174:20:174:40 | call to method First | @@ -146,12 +138,14 @@ | GlobalDataFlow.cs:181:21:181:26 | delegate call | return | GlobalDataFlow.cs:181:21:181:26 | delegate call | | GlobalDataFlow.cs:186:20:186:27 | delegate call | return | GlobalDataFlow.cs:186:20:186:27 | delegate call | | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy | return | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value | qualifier | GlobalDataFlow.cs:190:22:190:42 | [post] object creation of type Lazy | | GlobalDataFlow.cs:190:22:190:48 | access to property Value | return | GlobalDataFlow.cs:190:22:190:48 | access to property Value | -| GlobalDataFlow.cs:190:39:190:41 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:190:39:190:41 | [output] delegate creation of type Func | | GlobalDataFlow.cs:194:20:194:43 | object creation of type Lazy | return | GlobalDataFlow.cs:194:20:194:43 | object creation of type Lazy | +| GlobalDataFlow.cs:194:20:194:49 | access to property Value | qualifier | GlobalDataFlow.cs:194:20:194:43 | [post] object creation of type Lazy | | GlobalDataFlow.cs:194:20:194:49 | access to property Value | return | GlobalDataFlow.cs:194:20:194:49 | access to property Value | -| GlobalDataFlow.cs:194:37:194:42 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:194:37:194:42 | [output] delegate creation of type Func | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | qualifier | GlobalDataFlow.cs:198:22:198:32 | [post] this access | | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | return | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | +| GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | qualifier | GlobalDataFlow.cs:202:20:202:33 | [post] this access | | GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | return | GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | | GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable | return | GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable | | GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable | return | GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable | @@ -159,52 +153,207 @@ | GlobalDataFlow.cs:213:22:213:39 | call to method Select | return | GlobalDataFlow.cs:213:22:213:39 | call to method Select | | GlobalDataFlow.cs:213:22:213:39 | call to method Select | yield return | GlobalDataFlow.cs:213:22:213:39 | call to method Select | | GlobalDataFlow.cs:213:22:213:47 | call to method First | return | GlobalDataFlow.cs:213:22:213:47 | call to method First | -| GlobalDataFlow.cs:213:37:213:38 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:213:37:213:38 | [output] access to local variable f1 | | GlobalDataFlow.cs:215:22:215:39 | call to method Select | return | GlobalDataFlow.cs:215:22:215:39 | call to method Select | | GlobalDataFlow.cs:215:22:215:47 | call to method First | return | GlobalDataFlow.cs:215:22:215:47 | call to method First | -| GlobalDataFlow.cs:215:37:215:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:215:37:215:38 | [output] access to local variable f2 | | GlobalDataFlow.cs:217:22:217:49 | call to method Select | return | GlobalDataFlow.cs:217:22:217:49 | call to method Select | | GlobalDataFlow.cs:217:22:217:49 | call to method Select | yield return | GlobalDataFlow.cs:217:22:217:49 | call to method Select | | GlobalDataFlow.cs:217:22:217:57 | call to method First | return | GlobalDataFlow.cs:217:22:217:57 | call to method First | -| GlobalDataFlow.cs:217:37:217:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:217:37:217:48 | [output] delegate creation of type Func | | GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck | return | GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck | | GlobalDataFlow.cs:223:23:223:43 | call to method Select | return | GlobalDataFlow.cs:223:23:223:43 | call to method Select | | GlobalDataFlow.cs:223:23:223:43 | call to method Select | yield return | GlobalDataFlow.cs:223:23:223:43 | call to method Select | | GlobalDataFlow.cs:223:23:223:51 | call to method First | return | GlobalDataFlow.cs:223:23:223:51 | call to method First | -| GlobalDataFlow.cs:223:41:223:42 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:223:41:223:42 | [output] access to local variable f1 | | GlobalDataFlow.cs:225:19:225:39 | call to method Select | return | GlobalDataFlow.cs:225:19:225:39 | call to method Select | | GlobalDataFlow.cs:225:19:225:47 | call to method First | return | GlobalDataFlow.cs:225:19:225:47 | call to method First | -| GlobalDataFlow.cs:225:37:225:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:225:37:225:38 | [output] access to local variable f2 | | GlobalDataFlow.cs:227:19:227:39 | call to method Select | return | GlobalDataFlow.cs:227:19:227:39 | call to method Select | | GlobalDataFlow.cs:227:19:227:39 | call to method Select | yield return | GlobalDataFlow.cs:227:19:227:39 | call to method Select | | GlobalDataFlow.cs:227:19:227:47 | call to method First | return | GlobalDataFlow.cs:227:19:227:47 | call to method First | -| GlobalDataFlow.cs:227:37:227:38 | [implicit call] access to local variable f3 | return | GlobalDataFlow.cs:227:37:227:38 | [output] access to local variable f3 | | GlobalDataFlow.cs:229:19:229:39 | call to method Select | return | GlobalDataFlow.cs:229:19:229:39 | call to method Select | | GlobalDataFlow.cs:229:19:229:47 | call to method First | return | GlobalDataFlow.cs:229:19:229:47 | call to method First | -| GlobalDataFlow.cs:229:37:229:38 | [implicit call] access to local variable f4 | return | GlobalDataFlow.cs:229:37:229:38 | [output] access to local variable f4 | | GlobalDataFlow.cs:231:19:231:49 | call to method Select | return | GlobalDataFlow.cs:231:19:231:49 | call to method Select | | GlobalDataFlow.cs:231:19:231:49 | call to method Select | yield return | GlobalDataFlow.cs:231:19:231:49 | call to method Select | | GlobalDataFlow.cs:231:19:231:57 | call to method First | return | GlobalDataFlow.cs:231:19:231:57 | call to method First | -| GlobalDataFlow.cs:231:37:231:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:231:37:231:48 | [output] delegate creation of type Func | | GlobalDataFlow.cs:238:20:238:49 | call to method Run | return | GlobalDataFlow.cs:238:20:238:49 | call to method Run | -| GlobalDataFlow.cs:238:29:238:48 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:238:29:238:48 | [output] (...) => ... | +| GlobalDataFlow.cs:239:22:239:32 | access to property Result | qualifier | GlobalDataFlow.cs:239:22:239:25 | [post] access to local variable task | | GlobalDataFlow.cs:239:22:239:32 | access to property Result | return | GlobalDataFlow.cs:239:22:239:32 | access to property Result | | GlobalDataFlow.cs:245:16:245:33 | call to method Run | return | GlobalDataFlow.cs:245:16:245:33 | call to method Run | -| GlobalDataFlow.cs:245:25:245:32 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:245:25:245:32 | [output] (...) => ... | +| GlobalDataFlow.cs:246:24:246:34 | access to property Result | qualifier | GlobalDataFlow.cs:246:24:246:27 | [post] access to local variable task | | GlobalDataFlow.cs:246:24:246:34 | access to property Result | return | GlobalDataFlow.cs:246:24:246:34 | access to property Result | | GlobalDataFlow.cs:297:17:297:38 | call to method ApplyFunc | return | GlobalDataFlow.cs:297:17:297:38 | call to method ApplyFunc | | GlobalDataFlow.cs:386:16:386:19 | delegate call | return | GlobalDataFlow.cs:386:16:386:19 | delegate call | -| GlobalDataFlow.cs:451:44:451:47 | delegate call | return | GlobalDataFlow.cs:451:44:451:47 | delegate call | +| GlobalDataFlow.cs:445:9:445:20 | call to method Append | qualifier | GlobalDataFlow.cs:445:9:445:10 | [post] access to parameter sb | +| GlobalDataFlow.cs:445:9:445:20 | call to method Append | return | GlobalDataFlow.cs:445:9:445:20 | call to method Append | +| GlobalDataFlow.cs:450:18:450:36 | object creation of type StringBuilder | return | GlobalDataFlow.cs:450:18:450:36 | object creation of type StringBuilder | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString | qualifier | GlobalDataFlow.cs:452:22:452:23 | [post] access to local variable sb | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString | return | GlobalDataFlow.cs:452:22:452:34 | call to method ToString | +| GlobalDataFlow.cs:455:9:455:18 | call to method Clear | qualifier | GlobalDataFlow.cs:455:9:455:10 | [post] access to local variable sb | +| GlobalDataFlow.cs:455:9:455:18 | call to method Clear | return | GlobalDataFlow.cs:455:9:455:18 | call to method Clear | +| GlobalDataFlow.cs:456:23:456:35 | call to method ToString | qualifier | GlobalDataFlow.cs:456:23:456:24 | [post] access to local variable sb | +| GlobalDataFlow.cs:456:23:456:35 | call to method ToString | return | GlobalDataFlow.cs:456:23:456:35 | call to method ToString | +| GlobalDataFlow.cs:462:22:462:65 | call to method Join | return | GlobalDataFlow.cs:462:22:462:65 | call to method Join | +| GlobalDataFlow.cs:465:23:465:65 | call to method Join | return | GlobalDataFlow.cs:465:23:465:65 | call to method Join | +| GlobalDataFlow.cs:477:44:477:47 | delegate call | return | GlobalDataFlow.cs:477:44:477:47 | delegate call | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | | Splitting.cs:20:22:20:30 | call to method Return | return | Splitting.cs:20:22:20:30 | call to method Return | | Splitting.cs:21:21:21:33 | call to method Return | return | Splitting.cs:21:21:21:33 | call to method Return | +| Splitting.cs:30:9:30:13 | [b (line 24): false] dynamic access to element | qualifier | Splitting.cs:30:9:30:9 | [post] [b (line 24): false] access to local variable d | | Splitting.cs:30:9:30:13 | [b (line 24): false] dynamic access to element | return | Splitting.cs:30:9:30:13 | [b (line 24): false] dynamic access to element | +| Splitting.cs:30:9:30:13 | [b (line 24): true] dynamic access to element | qualifier | Splitting.cs:30:9:30:9 | [post] [b (line 24): true] access to local variable d | | Splitting.cs:30:9:30:13 | [b (line 24): true] dynamic access to element | return | Splitting.cs:30:9:30:13 | [b (line 24): true] dynamic access to element | +| Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element | qualifier | Splitting.cs:31:17:31:17 | [post] [b (line 24): false] access to local variable d | | Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element | return | Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element | +| Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element | qualifier | Splitting.cs:31:17:31:17 | [post] [b (line 24): true] access to local variable d | | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element | return | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element | | Splitting.cs:32:9:32:16 | [b (line 24): false] dynamic call to method Check | return | Splitting.cs:32:9:32:16 | [b (line 24): false] dynamic call to method Check | | Splitting.cs:32:9:32:16 | [b (line 24): true] dynamic call to method Check | return | Splitting.cs:32:9:32:16 | [b (line 24): true] dynamic call to method Check | | Splitting.cs:34:13:34:20 | dynamic call to method Check | return | Splitting.cs:34:13:34:20 | dynamic call to method Check | +| This.cs:12:9:12:20 | call to method M | qualifier | This.cs:12:9:12:12 | [post] this access | +| This.cs:13:9:13:15 | call to method M | qualifier | This.cs:13:9:13:15 | [post] this access | +| This.cs:15:9:15:21 | call to method M | qualifier | This.cs:15:9:15:12 | [post] this access | +| This.cs:16:9:16:16 | call to method M | qualifier | This.cs:16:9:16:16 | [post] this access | | This.cs:17:9:17:18 | object creation of type This | return | This.cs:17:9:17:18 | object creation of type This | +| This.cs:26:13:26:24 | call to method M | qualifier | This.cs:26:13:26:16 | [post] this access | +| This.cs:27:13:27:24 | call to method M | qualifier | This.cs:27:13:27:16 | [post] base access | | This.cs:28:13:28:21 | object creation of type Sub | return | This.cs:28:13:28:21 | object creation of type Sub | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Lazy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Lazy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Lazy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToDictionary | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToDictionary] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToDictionary | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToDictionary] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToLookup | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToLookup] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToLookup | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToLookup] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Zip | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Zip] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Zip | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Zip] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql index e3cee19f724..a850a6af8d5 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql @@ -1,5 +1,29 @@ import csharp import semmle.code.csharp.dataflow.internal.DataFlowDispatch +import semmle.code.csharp.dataflow.internal.DataFlowPrivate -from DataFlowCall call, ReturnKind kind -select call, kind, getAnOutNode(call, kind) +private class DataFlowCallAdjusted extends TDataFlowCall { + string toString() { result = this.(DataFlowCall).toString() } + + Location getLocation() { + exists(Location l | + l = this.(DataFlowCall).getLocation() and + if l instanceof SourceLocation then result = l else result instanceof EmptyLocation + ) + } +} + +private class NodeAdjusted extends TNode { + string toString() { result = this.(DataFlow::Node).toString() } + + Location getLocation() { + exists(Location l | + l = this.(DataFlow::Node).getLocation() and + if l instanceof SourceLocation then result = l else result instanceof EmptyLocation + ) + } +} + +from DataFlowCallAdjusted call, NodeAdjusted n, ReturnKind kind +where n = getAnOutNode(call, kind) +select call, kind, n diff --git a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs index f62f0915a21..35c38fb809b 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs @@ -439,6 +439,32 @@ public class DataFlow { get { return ""; } } + + static void AppendToStringBuilder(StringBuilder sb, string s) + { + sb.Append(s); + } + + void TestStringBuilderFlow() + { + var sb = new StringBuilder(); + AppendToStringBuilder(sb, "taint source"); + var sink43 = sb.ToString(); + Check(sink43); + + sb.Clear(); + var nonSink = sb.ToString(); + Check(nonSink); + } + + void TestStringFlow() + { + var sink44 = string.Join(",", "whatever", "taint source"); + Check(sink44); + + var nonSink = string.Join(",", "whatever", "not tainted"); + Check(nonSink); + } } static class IEnumerableExtensions diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected index fbd507ea781..aee973d855b 100644 --- a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected +++ b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected @@ -55,10 +55,12 @@ | GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | | GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | +| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | +| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | -| Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected index 5f4a898112b..a0ac519a6c6 100644 --- a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected +++ b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected @@ -129,9 +129,7 @@ edges | GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | | GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:85:59:85:64 | access to local variable sink14 : String | | GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:89:59:89:64 | access to local variable sink14 : String | -| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | -| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | -| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:91:75:91:80 | access to local variable sink14 : String | | GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | GlobalDataFlow.cs:83:22:83:87 | call to method Select [[]] : String | | GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | GlobalDataFlow.cs:312:31:312:40 | sinkParam8 : String | | GlobalDataFlow.cs:83:57:83:66 | { ..., ... } [[]] : String | GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | @@ -147,9 +145,18 @@ edges | GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | GlobalDataFlow.cs:87:22:87:128 | call to method Zip [[]] : String | | GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | | GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | -| GlobalDataFlow.cs:89:23:89:66 | (...) ... [[]] : String | GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | +| GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate : String | GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | +| GlobalDataFlow.cs:89:23:89:66 | (...) ... [[]] : String | GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate : String | | GlobalDataFlow.cs:89:57:89:66 | { ..., ... } [[]] : String | GlobalDataFlow.cs:89:23:89:66 | (...) ... [[]] : String | | GlobalDataFlow.cs:89:59:89:64 | access to local variable sink14 : String | GlobalDataFlow.cs:89:57:89:66 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | GlobalDataFlow.cs:94:24:94:29 | access to local variable sink18 : String | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | GlobalDataFlow.cs:97:23:97:28 | access to local variable sink18 : String | +| GlobalDataFlow.cs:91:75:91:80 | access to local variable sink14 : String | GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | +| GlobalDataFlow.cs:94:24:94:29 | access to local variable sink18 : String | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) : Int32 | +| GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) : Int32 | GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | +| GlobalDataFlow.cs:97:23:97:28 | access to local variable sink18 : String | GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) : Boolean | +| GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) : Boolean | GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | | GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | @@ -228,6 +235,12 @@ edges | GlobalDataFlow.cs:402:16:402:21 | access to local variable sink11 : String | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | | GlobalDataFlow.cs:424:9:424:11 | value : String | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | | GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | +| GlobalDataFlow.cs:451:31:451:32 | [post] access to local variable sb [[]] : String | GlobalDataFlow.cs:452:22:452:23 | access to local variable sb [[]] : String | +| GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | GlobalDataFlow.cs:451:31:451:32 | [post] access to local variable sb [[]] : String | +| GlobalDataFlow.cs:452:22:452:23 | access to local variable sb [[]] : String | GlobalDataFlow.cs:452:22:452:34 | call to method ToString : String | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString : String | GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | +| GlobalDataFlow.cs:462:22:462:65 | call to method Join : String | GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | +| GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | GlobalDataFlow.cs:462:22:462:65 | call to method Join : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | @@ -235,7 +248,8 @@ edges | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | Splitting.cs:11:19:11:19 | access to local variable x | | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | -| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value : String | +| Splitting.cs:21:28:21:32 | access to parameter value : String | Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:31:19:31:25 | [b (line 24): false] access to parameter tainted : String | @@ -338,12 +352,19 @@ nodes | GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | | GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | semmle.label | access to local variable sink15 : String | | GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | semmle.label | access to local variable sink16 | +| GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate : String | semmle.label | call to method Aggregate : String | | GlobalDataFlow.cs:89:23:89:66 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | | GlobalDataFlow.cs:89:57:89:66 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | | GlobalDataFlow.cs:89:59:89:64 | access to local variable sink14 : String | semmle.label | access to local variable sink14 : String | | GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | semmle.label | access to local variable sink17 | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | semmle.label | call to method Aggregate : String | +| GlobalDataFlow.cs:91:75:91:80 | access to local variable sink14 : String | semmle.label | access to local variable sink14 : String | | GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | semmle.label | access to local variable sink18 | +| GlobalDataFlow.cs:94:24:94:29 | access to local variable sink18 : String | semmle.label | access to local variable sink18 : String | +| GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) : Int32 | semmle.label | SSA def(sink21) : Int32 | | GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | semmle.label | access to local variable sink21 | +| GlobalDataFlow.cs:97:23:97:28 | access to local variable sink18 : String | semmle.label | access to local variable sink18 : String | +| GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) : Boolean | semmle.label | SSA def(sink22) : Boolean | | GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | semmle.label | access to local variable sink22 | | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | semmle.label | delegate call : String | | GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | @@ -441,6 +462,14 @@ nodes | GlobalDataFlow.cs:424:9:424:11 | value : String | semmle.label | value : String | | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | | GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:451:31:451:32 | [post] access to local variable sb [[]] : String | semmle.label | [post] access to local variable sb [[]] : String | +| GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:452:22:452:23 | access to local variable sb [[]] : String | semmle.label | access to local variable sb [[]] : String | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString : String | semmle.label | call to method ToString : String | +| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | semmle.label | access to local variable sink43 | +| GlobalDataFlow.cs:462:22:462:65 | call to method Join : String | semmle.label | call to method Join : String | +| GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | semmle.label | access to local variable sink44 | | Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String | @@ -450,7 +479,8 @@ nodes | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | semmle.label | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | semmle.label | access to local variable x | | Splitting.cs:21:9:21:11 | value : String | semmle.label | value : String | -| Splitting.cs:21:28:21:32 | access to parameter value | semmle.label | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | semmle.label | call to method Return | +| Splitting.cs:21:28:21:32 | access to parameter value : String | semmle.label | access to parameter value : String | | Splitting.cs:24:28:24:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | semmle.label | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | semmle.label | [b (line 24): true] access to parameter tainted : String | @@ -525,10 +555,12 @@ nodes | GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | access to parameter sinkParam11 | | GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | access to local variable sink11 | | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | access to local variable sink20 | +| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | access to local variable sink43 | +| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | access to local variable sink44 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x | -| Splitting.cs:21:28:21:32 | access to parameter value | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:28:21:32 | access to parameter value | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:21:21:33 | call to method Return | call to method Return | | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:34:19:34:19 | access to local variable x | access to local variable x | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected index e1b4a0b52b2..baa9311a09b 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected @@ -4,11 +4,11 @@ | in | Capture.cs:8:13:8:13 | x | Capture.cs:15:13:15:17 | SSA def(x) | Capture.cs:19:24:23:13 | SSA capture def(x) | Capture.cs:25:13:25:15 | delegate call | false | | in | Capture.cs:8:13:8:13 | x | Capture.cs:43:9:43:13 | SSA def(x) | Capture.cs:19:24:23:13 | SSA capture def(x) | Capture.cs:44:9:44:12 | call to method M | true | | in | Capture.cs:17:17:17:17 | y | Capture.cs:17:17:17:21 | SSA def(y) | Capture.cs:19:24:23:13 | SSA capture def(y) | Capture.cs:25:13:25:15 | delegate call | false | -| in | Capture.cs:59:13:59:13 | i | Capture.cs:59:13:59:17 | SSA def(i) | Capture.cs:60:31:60:38 | SSA capture def(i) | Capture.cs:61:9:61:25 | call to method Select | false | -| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:68:32:68:49 | SSA capture def(c) | Capture.cs:68:18:68:50 | call to method Where | false | -| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:69:9:69:62 | SSA capture def(c) | Capture.cs:70:9:70:25 | call to method Select | false | -| in | Capture.cs:75:13:75:13 | i | Capture.cs:75:13:75:17 | SSA def(i) | Capture.cs:76:67:76:81 | SSA capture def(i) | Capture.cs:77:9:77:25 | call to method Select | false | -| in | Capture.cs:85:13:85:13 | b | Capture.cs:85:13:85:20 | SSA def(b) | Capture.cs:86:68:86:73 | SSA capture def(b) | Capture.cs:87:9:87:24 | call to method Where | false | +| in | Capture.cs:59:13:59:13 | i | Capture.cs:59:13:59:17 | SSA def(i) | Capture.cs:60:31:60:38 | SSA capture def(i) | Capture.cs:61:9:61:25 | call to method Select | true | +| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:68:32:68:49 | SSA capture def(c) | Capture.cs:68:18:68:50 | call to method Where | true | +| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:69:9:69:62 | SSA capture def(c) | Capture.cs:70:9:70:25 | call to method Select | true | +| in | Capture.cs:75:13:75:13 | i | Capture.cs:75:13:75:17 | SSA def(i) | Capture.cs:76:67:76:81 | SSA capture def(i) | Capture.cs:77:9:77:25 | call to method Select | true | +| in | Capture.cs:85:13:85:13 | b | Capture.cs:85:13:85:20 | SSA def(b) | Capture.cs:86:68:86:73 | SSA capture def(b) | Capture.cs:87:9:87:24 | call to method Where | true | | in | Capture.cs:94:13:94:13 | y | Capture.cs:94:13:94:18 | SSA def(y) | Capture.cs:96:12:100:9 | SSA capture def(y) | Capture.cs:96:9:100:10 | call to local function fn | true | | in | Capture.cs:94:13:94:13 | y | Capture.cs:94:13:94:18 | SSA def(y) | Capture.cs:96:12:100:9 | SSA capture def(y) | Capture.cs:103:9:107:10 | call to local function fn | true | | in | Capture.cs:114:13:114:13 | a | Capture.cs:114:13:114:18 | SSA def(a) | Capture.cs:115:9:119:9 | SSA capture def(a) | Capture.cs:120:9:120:12 | call to local function M1 | false | @@ -16,23 +16,23 @@ | in | Capture.cs:182:17:182:17 | i | Capture.cs:188:13:188:17 | SSA def(i) | Capture.cs:183:13:186:13 | SSA capture def(i) | Capture.cs:189:13:189:17 | call to local function M11 | false | | in | Capture.cs:197:17:197:17 | i | Capture.cs:197:17:197:21 | SSA def(i) | Capture.cs:198:33:198:44 | SSA capture def(i) | Capture.cs:200:13:200:19 | delegate call | false | | in | Capture.cs:197:17:197:17 | i | Capture.cs:197:17:197:21 | SSA def(i) | Capture.cs:203:34:203:45 | SSA capture def(i) | Capture.cs:200:13:200:19 | delegate call | false | -| in | Capture.cs:209:17:209:17 | i | Capture.cs:209:17:209:21 | SSA def(i) | Capture.cs:212:39:212:71 | SSA capture def(i) | Capture.cs:213:17:213:24 | access to event Exited | false | +| in | Capture.cs:209:17:209:17 | i | Capture.cs:209:17:209:21 | SSA def(i) | Capture.cs:212:39:212:71 | SSA capture def(i) | Capture.cs:213:17:213:24 | access to event Exited | true | | in | Capture.cs:229:13:229:13 | i | Capture.cs:232:9:232:13 | SSA def(i) | Capture.cs:231:9:231:49 | SSA capture def(i) | Capture.cs:233:9:233:12 | call to local function M2 | false | | in | Fields.cs:77:13:77:13 | f | Fields.cs:77:13:77:45 | SSA def(f) | Fields.cs:78:27:78:54 | SSA capture def(f) | Fields.cs:81:9:81:11 | delegate call | false | | in | Fields.cs:77:13:77:13 | f | Fields.cs:77:13:77:45 | SSA def(f) | Fields.cs:78:27:78:54 | SSA capture def(f) | Fields.cs:86:9:86:47 | call to method Select | true | -| in | Fields.cs:78:23:78:23 | a | Fields.cs:78:23:78:54 | SSA def(a) | Fields.cs:86:24:86:46 | SSA capture def(a) | Fields.cs:86:9:86:47 | call to method Select | false | -| in | Fields.cs:79:23:79:23 | b | Fields.cs:79:23:79:35 | SSA def(b) | Fields.cs:89:24:89:46 | SSA capture def(b) | Fields.cs:89:9:89:47 | call to method Select | false | +| in | Fields.cs:78:23:78:23 | a | Fields.cs:78:23:78:54 | SSA def(a) | Fields.cs:86:24:86:46 | SSA capture def(a) | Fields.cs:86:9:86:47 | call to method Select | true | +| in | Fields.cs:79:23:79:23 | b | Fields.cs:79:23:79:35 | SSA def(b) | Fields.cs:89:24:89:46 | SSA capture def(b) | Fields.cs:89:9:89:47 | call to method Select | true | | in | Properties.cs:73:13:73:13 | f | Properties.cs:73:13:73:32 | SSA def(f) | Properties.cs:74:27:74:54 | SSA capture def(f) | Properties.cs:77:9:77:11 | delegate call | false | | in | Properties.cs:73:13:73:13 | f | Properties.cs:73:13:73:32 | SSA def(f) | Properties.cs:74:27:74:54 | SSA capture def(f) | Properties.cs:82:9:82:47 | call to method Select | true | -| in | Properties.cs:74:23:74:23 | a | Properties.cs:74:23:74:54 | SSA def(a) | Properties.cs:82:24:82:46 | SSA capture def(a) | Properties.cs:82:9:82:47 | call to method Select | false | -| in | Properties.cs:75:23:75:23 | b | Properties.cs:75:23:75:35 | SSA def(b) | Properties.cs:85:24:85:46 | SSA capture def(b) | Properties.cs:85:9:85:47 | call to method Select | false | +| in | Properties.cs:74:23:74:23 | a | Properties.cs:74:23:74:54 | SSA def(a) | Properties.cs:82:24:82:46 | SSA capture def(a) | Properties.cs:82:9:82:47 | call to method Select | true | +| in | Properties.cs:75:23:75:23 | b | Properties.cs:75:23:75:35 | SSA def(b) | Properties.cs:85:24:85:46 | SSA capture def(b) | Properties.cs:85:9:85:47 | call to method Select | true | | out | Capture.cs:6:16:6:16 | i | Capture.cs:13:13:13:17 | SSA def(i) | Capture.cs:38:9:38:11 | SSA call def(i) | Capture.cs:38:9:38:11 | delegate call | false | | out | Capture.cs:8:13:8:13 | x | Capture.cs:15:13:15:17 | SSA def(x) | Capture.cs:38:9:38:11 | SSA call def(x) | Capture.cs:38:9:38:11 | delegate call | false | | out | Capture.cs:8:13:8:13 | x | Capture.cs:15:13:15:17 | SSA def(x) | Capture.cs:44:9:44:12 | SSA call def(x) | Capture.cs:44:9:44:12 | call to method M | true | | out | Capture.cs:29:13:29:13 | z | Capture.cs:30:28:30:32 | SSA def(z) | Capture.cs:32:9:32:11 | SSA call def(z) | Capture.cs:32:9:32:11 | delegate call | false | | out | Capture.cs:50:20:50:20 | a | Capture.cs:52:28:52:40 | SSA def(a) | Capture.cs:53:9:53:11 | SSA call def(a) | Capture.cs:53:9:53:11 | delegate call | false | -| out | Capture.cs:59:13:59:13 | i | Capture.cs:60:36:60:38 | SSA def(i) | Capture.cs:61:9:61:25 | SSA call def(i) | Capture.cs:61:9:61:25 | call to method Select | false | -| out | Capture.cs:75:13:75:13 | i | Capture.cs:76:80:76:80 | SSA def(i) | Capture.cs:77:9:77:25 | SSA call def(i) | Capture.cs:77:9:77:25 | call to method Select | false | +| out | Capture.cs:59:13:59:13 | i | Capture.cs:60:36:60:38 | SSA def(i) | Capture.cs:61:9:61:25 | SSA call def(i) | Capture.cs:61:9:61:25 | call to method Select | true | +| out | Capture.cs:75:13:75:13 | i | Capture.cs:76:80:76:80 | SSA def(i) | Capture.cs:77:9:77:25 | SSA call def(i) | Capture.cs:77:9:77:25 | call to method Select | true | | out | Capture.cs:130:13:130:13 | c | Capture.cs:133:13:133:17 | SSA def(c) | Capture.cs:136:9:136:12 | SSA call def(c) | Capture.cs:136:9:136:12 | call to local function M3 | false | | out | Capture.cs:139:13:139:13 | d | Capture.cs:142:13:142:17 | SSA def(d) | Capture.cs:144:9:144:12 | SSA call def(d) | Capture.cs:144:9:144:12 | call to local function M4 | false | | out | Capture.cs:168:13:168:13 | h | Capture.cs:174:17:174:21 | SSA def(h) | Capture.cs:176:13:176:16 | SSA call def(h) | Capture.cs:176:13:176:16 | call to local function M9 | false | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected b/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected index 223fcf616c2..ccfaf885369 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected @@ -1,25 +1,45 @@ edges -| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:32:41:32:52 | access to local variable destFilePath | -| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:36:45:36:56 | access to local variable destFilePath | -| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:40:41:40:52 | access to local variable destFilePath | -| ZipSlip.cs:19:31:19:44 | access to property FullName : String | ZipSlip.cs:24:41:24:52 | access to local variable destFileName | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:69:74:69:85 | access to local variable destFilePath | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:76:71:76:82 | access to local variable destFilePath | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:83:57:83:68 | access to local variable destFilePath | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:91:58:91:69 | access to local variable destFilePath | -| ZipSlipBad.cs:9:59:9:72 | access to property FullName : String | ZipSlipBad.cs:10:29:10:40 | access to local variable destFileName | +| ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | ZipSlip.cs:31:71:31:78 | access to local variable fullPath : String | +| ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | ZipSlip.cs:39:81:39:88 | access to local variable fullPath : String | +| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | +| ZipSlip.cs:19:31:19:44 | access to property FullName : String | ZipSlip.cs:23:71:23:74 | access to local variable file : String | +| ZipSlip.cs:23:43:23:75 | call to method Combine : String | ZipSlip.cs:24:41:24:52 | access to local variable destFileName | +| ZipSlip.cs:23:71:23:74 | access to local variable file : String | ZipSlip.cs:23:43:23:75 | call to method Combine : String | +| ZipSlip.cs:31:43:31:79 | call to method Combine : String | ZipSlip.cs:32:41:32:52 | access to local variable destFilePath | +| ZipSlip.cs:31:43:31:79 | call to method Combine : String | ZipSlip.cs:36:45:36:56 | access to local variable destFilePath | +| ZipSlip.cs:31:71:31:78 | access to local variable fullPath : String | ZipSlip.cs:31:43:31:79 | call to method Combine : String | +| ZipSlip.cs:39:36:39:90 | call to method GetFullPath : String | ZipSlip.cs:40:41:40:52 | access to local variable destFilePath | +| ZipSlip.cs:39:53:39:89 | call to method Combine : String | ZipSlip.cs:39:36:39:90 | call to method GetFullPath : String | +| ZipSlip.cs:39:81:39:88 | access to local variable fullPath : String | ZipSlip.cs:39:53:39:89 | call to method Combine : String | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:69:74:69:85 | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:76:71:76:82 | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:83:57:83:68 | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:91:58:91:69 | access to local variable destFilePath | +| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:62:47:62:86 | call to method Combine : String | +| ZipSlipBad.cs:9:31:9:73 | call to method Combine : String | ZipSlipBad.cs:10:29:10:40 | access to local variable destFileName | +| ZipSlipBad.cs:9:59:9:72 | access to property FullName : String | ZipSlipBad.cs:9:31:9:73 | call to method Combine : String | nodes +| ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | semmle.label | call to method GetFullPath : String | | ZipSlip.cs:16:52:16:65 | access to property FullName : String | semmle.label | access to property FullName : String | | ZipSlip.cs:19:31:19:44 | access to property FullName : String | semmle.label | access to property FullName : String | +| ZipSlip.cs:23:43:23:75 | call to method Combine : String | semmle.label | call to method Combine : String | +| ZipSlip.cs:23:71:23:74 | access to local variable file : String | semmle.label | access to local variable file : String | | ZipSlip.cs:24:41:24:52 | access to local variable destFileName | semmle.label | access to local variable destFileName | +| ZipSlip.cs:31:43:31:79 | call to method Combine : String | semmle.label | call to method Combine : String | +| ZipSlip.cs:31:71:31:78 | access to local variable fullPath : String | semmle.label | access to local variable fullPath : String | | ZipSlip.cs:32:41:32:52 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:36:45:36:56 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | +| ZipSlip.cs:39:36:39:90 | call to method GetFullPath : String | semmle.label | call to method GetFullPath : String | +| ZipSlip.cs:39:53:39:89 | call to method Combine : String | semmle.label | call to method Combine : String | +| ZipSlip.cs:39:81:39:88 | access to local variable fullPath : String | semmle.label | access to local variable fullPath : String | | ZipSlip.cs:40:41:40:52 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | semmle.label | call to method Combine : String | | ZipSlip.cs:62:72:62:85 | access to property FullName : String | semmle.label | access to property FullName : String | | ZipSlip.cs:69:74:69:85 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:76:71:76:82 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:83:57:83:68 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:91:58:91:69 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | +| ZipSlipBad.cs:9:31:9:73 | call to method Combine : String | semmle.label | call to method Combine : String | | ZipSlipBad.cs:9:59:9:72 | access to property FullName : String | semmle.label | access to property FullName : String | | ZipSlipBad.cs:10:29:10:40 | access to local variable destFileName | semmle.label | access to local variable destFileName | #select diff --git a/csharp/ql/test/query-tests/Security Features/CWE-078/CommandInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-078/CommandInjection.expected index a87ff6764ce..254f5581744 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-078/CommandInjection.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-078/CommandInjection.expected @@ -1,13 +1,15 @@ edges -| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:26:27:26:47 | ... + ... | -| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:26:50:26:66 | ... + ... | -| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:28:63:28:71 | access to local variable userInput | -| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:28:74:28:82 | access to local variable userInput | -| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:32:39:32:47 | access to local variable userInput | -| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:33:40:33:48 | access to local variable userInput | -| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:34:47:34:55 | access to local variable userInput | +| CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | CommandInjection.cs:25:32:25:51 | access to property Text : String | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | CommandInjection.cs:26:27:26:47 | ... + ... | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | CommandInjection.cs:26:50:26:66 | ... + ... | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | CommandInjection.cs:28:63:28:71 | access to local variable userInput | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | CommandInjection.cs:28:74:28:82 | access to local variable userInput | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | CommandInjection.cs:32:39:32:47 | access to local variable userInput | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | CommandInjection.cs:33:40:33:48 | access to local variable userInput | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | CommandInjection.cs:34:47:34:55 | access to local variable userInput | nodes | CommandInjection.cs:25:32:25:46 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox | +| CommandInjection.cs:25:32:25:51 | access to property Text : String | semmle.label | access to property Text : String | | CommandInjection.cs:26:27:26:47 | ... + ... | semmle.label | ... + ... | | CommandInjection.cs:26:50:26:66 | ... + ... | semmle.label | ... + ... | | CommandInjection.cs:28:63:28:71 | access to local variable userInput | semmle.label | access to local variable userInput | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected b/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected index 81946f1054e..36d8ecfb339 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected @@ -2,11 +2,16 @@ edges | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:27:30:27:34 | access to local variable sayHi | | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:37:40:37:44 | access to local variable sayHi | | XSSAspNet.cs:44:28:44:46 | access to property QueryString : NameValueCollection | XSSAspNet.cs:44:28:44:55 | access to indexer | -| XSSAspNetCore.cs:21:52:21:64 | access to property Query : IQueryCollection | XSSAspNetCore.cs:21:52:21:76 | call to operator implicit conversion | +| XSSAspNetCore.cs:21:52:21:64 | access to property Query : IQueryCollection | XSSAspNetCore.cs:21:52:21:76 | access to indexer : StringValues | +| XSSAspNetCore.cs:21:52:21:76 | access to indexer : StringValues | XSSAspNetCore.cs:21:52:21:76 | call to operator implicit conversion | | XSSAspNetCore.cs:40:56:40:58 | foo : String | XSSAspNetCore.cs:44:51:44:53 | access to parameter foo | -| XSSAspNetCore.cs:58:43:58:55 | access to property Query : IQueryCollection | XSSAspNetCore.cs:58:43:58:73 | call to method ToString | +| XSSAspNetCore.cs:58:43:58:55 | access to property Query : IQueryCollection | XSSAspNetCore.cs:58:43:58:62 | access to indexer : StringValues | +| XSSAspNetCore.cs:58:43:58:62 | access to indexer : StringValues | XSSAspNetCore.cs:58:43:58:73 | call to method ToString | +| XSSAspNetCore.cs:61:44:61:56 | access to property Query : IQueryCollection | XSSAspNetCore.cs:61:44:61:63 | access to indexer : StringValues | | XSSAspNetCore.cs:61:44:61:56 | access to property Query : IQueryCollection | XSSAspNetCore.cs:61:44:61:66 | access to indexer | -| XSSAspNetCore.cs:72:51:72:65 | access to property Headers : IHeaderDictionary | XSSAspNetCore.cs:72:51:72:72 | call to operator implicit conversion | +| XSSAspNetCore.cs:61:44:61:63 | access to indexer : StringValues | XSSAspNetCore.cs:61:44:61:66 | access to indexer | +| XSSAspNetCore.cs:72:51:72:65 | access to property Headers : IHeaderDictionary | XSSAspNetCore.cs:72:51:72:72 | access to indexer : StringValues | +| XSSAspNetCore.cs:72:51:72:72 | access to indexer : StringValues | XSSAspNetCore.cs:72:51:72:72 | call to operator implicit conversion | #select | XSSAspNet.cs:27:30:27:34 | access to local variable sayHi | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:27:30:27:34 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteral() method. | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | User-provided value | | XSSAspNet.cs:37:40:37:44 | access to local variable sayHi | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:37:40:37:44 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteralTo() method. | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | User-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected index b035cc46b1b..61dffee741f 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected @@ -1,12 +1,16 @@ edges -| SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | SqlInjection.cs:39:50:39:55 | access to local variable query1 | -| SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:74:56:74:61 | access to local variable query1 | -| SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:75:55:75:60 | access to local variable query1 | +| SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | SqlInjection.cs:38:21:38:40 | access to property Text : String | +| SqlInjection.cs:38:21:38:40 | access to property Text : String | SqlInjection.cs:39:50:39:55 | access to local variable query1 | +| SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:73:33:73:52 | access to property Text : String | +| SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:74:56:74:61 | access to local variable query1 | +| SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:75:55:75:60 | access to local variable query1 | | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | nodes | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox | +| SqlInjection.cs:38:21:38:40 | access to property Text : String | semmle.label | access to property Text : String | | SqlInjection.cs:39:50:39:55 | access to local variable query1 | semmle.label | access to local variable query1 | | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox | +| SqlInjection.cs:73:33:73:52 | access to property Text : String | semmle.label | access to property Text : String | | SqlInjection.cs:74:56:74:61 | access to local variable query1 | semmle.label | access to local variable query1 | | SqlInjection.cs:75:55:75:60 | access to local variable query1 | semmle.label | access to local variable query1 | | SqlInjection.cs:87:21:87:29 | access to property Text : String | semmle.label | access to property Text : String | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected b/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected index b9abafda2f4..38c31c17825 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected @@ -1,16 +1,26 @@ edges -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:29:26:29:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:37:26:37:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:47:26:47:58 | object creation of type StringReader | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:18:43:18:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:23:43:23:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:29:43:29:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:37:43:37:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:47:43:47:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:18:43:18:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | +| MissingXMLValidation.cs:23:43:23:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | +| MissingXMLValidation.cs:29:43:29:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:29:26:29:58 | object creation of type StringReader | +| MissingXMLValidation.cs:37:43:37:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:37:26:37:58 | object creation of type StringReader | +| MissingXMLValidation.cs:47:43:47:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:47:26:47:58 | object creation of type StringReader | nodes | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:18:43:18:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:23:43:23:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:29:26:29:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:29:43:29:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:37:26:37:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:37:43:37:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:47:26:47:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:47:43:47:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | #select | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | $@ flows to here and is processed as XML without validation because there is no 'XmlReaderSettings' instance specifying schema validation. | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString | User-provided value | | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | $@ flows to here and is processed as XML without validation because the 'XmlReaderSettings' instance does not specify the 'ValidationType' as 'Schema'. | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString | User-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected b/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected index 8e051c94464..138451c31dc 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected @@ -7,7 +7,8 @@ edges | InsecureRandomness.cs:29:57:29:60 | access to local variable data [[]] : Int32 | InsecureRandomness.cs:29:27:29:61 | call to method GetString : String | | InsecureRandomness.cs:31:16:31:21 | access to local variable result [[]] : String | InsecureRandomness.cs:31:16:31:32 | call to method ToString : String | | InsecureRandomness.cs:31:16:31:32 | call to method ToString : String | InsecureRandomness.cs:12:27:12:50 | call to method InsecureRandomString | -| InsecureRandomness.cs:60:31:60:39 | call to method Next : Int32 | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | +| InsecureRandomness.cs:60:31:60:39 | call to method Next : Int32 | InsecureRandomness.cs:62:16:62:21 | access to local variable result : String | +| InsecureRandomness.cs:62:16:62:21 | access to local variable result : String | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | InsecureRandomness.cs:13:20:13:56 | call to method InsecureRandomStringFromSelection | | InsecureRandomness.cs:72:31:72:39 | call to method Next : Int32 | InsecureRandomness.cs:74:16:74:21 | access to local variable result : String | | InsecureRandomness.cs:74:16:74:21 | access to local variable result : String | InsecureRandomness.cs:14:20:14:54 | call to method InsecureRandomStringFromIndexer | @@ -24,6 +25,7 @@ nodes | InsecureRandomness.cs:31:16:31:21 | access to local variable result [[]] : String | semmle.label | access to local variable result [[]] : String | | InsecureRandomness.cs:31:16:31:32 | call to method ToString : String | semmle.label | call to method ToString : String | | InsecureRandomness.cs:60:31:60:39 | call to method Next : Int32 | semmle.label | call to method Next : Int32 | +| InsecureRandomness.cs:62:16:62:21 | access to local variable result : String | semmle.label | access to local variable result : String | | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | semmle.label | call to method ToString : String | | InsecureRandomness.cs:72:31:72:39 | call to method Next : Int32 | semmle.label | call to method Next : Int32 | | InsecureRandomness.cs:74:16:74:21 | access to local variable result : String | semmle.label | access to local variable result : String | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected index afcc216fc02..2ed9af3f8f2 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected @@ -4,15 +4,19 @@ edges | UrlRedirect.cs:39:44:39:66 | access to property QueryString : NameValueCollection | UrlRedirect.cs:39:44:39:74 | access to indexer | | UrlRedirect.cs:40:47:40:69 | access to property QueryString : NameValueCollection | UrlRedirect.cs:40:47:40:77 | access to indexer | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:18:22:18:26 | access to parameter value | -| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:21:44:21:48 | call to operator implicit conversion | -| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:27:46:27:50 | call to operator implicit conversion | +| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:21:44:21:48 | access to parameter value : String | +| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:27:46:27:50 | access to parameter value : String | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:33:66:33:70 | access to parameter value | -| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:36:49:36:53 | call to operator implicit conversion | +| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:36:49:36:53 | access to parameter value : String | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:39:69:39:73 | access to parameter value | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:42:39:42:53 | ... + ... | +| UrlRedirectCore.cs:21:44:21:48 | access to parameter value : String | UrlRedirectCore.cs:21:44:21:48 | call to operator implicit conversion | +| UrlRedirectCore.cs:27:46:27:50 | access to parameter value : String | UrlRedirectCore.cs:27:46:27:50 | call to operator implicit conversion | +| UrlRedirectCore.cs:36:49:36:53 | access to parameter value : String | UrlRedirectCore.cs:36:49:36:53 | call to operator implicit conversion | | UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:50:28:50:32 | access to parameter value | -| UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:55:32:55:45 | object creation of type Uri | +| UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:55:40:55:44 | access to parameter value : String | | UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:58:31:58:35 | access to parameter value | +| UrlRedirectCore.cs:55:40:55:44 | access to parameter value : String | UrlRedirectCore.cs:55:32:55:45 | object creation of type Uri | nodes | UrlRedirect.cs:14:31:14:53 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | UrlRedirect.cs:14:31:14:61 | access to indexer | semmle.label | access to indexer | @@ -24,15 +28,19 @@ nodes | UrlRedirect.cs:49:29:49:31 | access to local variable url | semmle.label | access to local variable url | | UrlRedirectCore.cs:15:44:15:48 | value : String | semmle.label | value : String | | UrlRedirectCore.cs:18:22:18:26 | access to parameter value | semmle.label | access to parameter value | +| UrlRedirectCore.cs:21:44:21:48 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:21:44:21:48 | call to operator implicit conversion | semmle.label | call to operator implicit conversion | +| UrlRedirectCore.cs:27:46:27:50 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:27:46:27:50 | call to operator implicit conversion | semmle.label | call to operator implicit conversion | | UrlRedirectCore.cs:33:66:33:70 | access to parameter value | semmle.label | access to parameter value | +| UrlRedirectCore.cs:36:49:36:53 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:36:49:36:53 | call to operator implicit conversion | semmle.label | call to operator implicit conversion | | UrlRedirectCore.cs:39:69:39:73 | access to parameter value | semmle.label | access to parameter value | | UrlRedirectCore.cs:42:39:42:53 | ... + ... | semmle.label | ... + ... | | UrlRedirectCore.cs:47:51:47:55 | value : String | semmle.label | value : String | | UrlRedirectCore.cs:50:28:50:32 | access to parameter value | semmle.label | access to parameter value | | UrlRedirectCore.cs:55:32:55:45 | object creation of type Uri | semmle.label | object creation of type Uri | +| UrlRedirectCore.cs:55:40:55:44 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:58:31:58:35 | access to parameter value | semmle.label | access to parameter value | #select | UrlRedirect.cs:14:31:14:61 | access to indexer | UrlRedirect.cs:14:31:14:53 | access to property QueryString : NameValueCollection | UrlRedirect.cs:14:31:14:61 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:14:31:14:53 | access to property QueryString | user-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected b/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected index 7898c01c3b5..72c7464ba38 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected @@ -1,23 +1,45 @@ edges | ConditionalBypass.cs:14:26:14:48 | access to property QueryString : NameValueCollection | ConditionalBypass.cs:18:13:18:30 | ... == ... | -| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:24:13:24:45 | call to method Equals | -| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:29:13:29:40 | ... == ... | -| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:46:13:46:46 | ... == ... | -| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:51:13:51:29 | access to property HostName | -| ConditionalBypass.cs:72:34:72:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:74:13:74:40 | ... == ... | -| ConditionalBypass.cs:85:34:85:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:86:13:86:40 | ... == ... | +| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:24:13:24:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:29:13:29:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:24:13:24:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:24:13:24:29 | access to property Value : String | +| ConditionalBypass.cs:24:13:24:29 | access to property Value : String | ConditionalBypass.cs:24:13:24:45 | call to method Equals | +| ConditionalBypass.cs:29:13:29:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:29:13:29:29 | access to property Value : String | +| ConditionalBypass.cs:29:13:29:29 | access to property Value : String | ConditionalBypass.cs:29:13:29:40 | ... == ... | +| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:46:13:46:20 | access to local variable hostInfo : IPHostEntry | +| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:51:13:51:20 | access to local variable hostInfo : IPHostEntry | +| ConditionalBypass.cs:46:13:46:20 | access to local variable hostInfo : IPHostEntry | ConditionalBypass.cs:46:13:46:29 | access to property HostName : String | +| ConditionalBypass.cs:46:13:46:29 | access to property HostName : String | ConditionalBypass.cs:46:13:46:46 | ... == ... | +| ConditionalBypass.cs:51:13:51:20 | access to local variable hostInfo : IPHostEntry | ConditionalBypass.cs:51:13:51:29 | access to property HostName | +| ConditionalBypass.cs:72:34:72:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:74:13:74:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:74:13:74:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:74:13:74:29 | access to property Value : String | +| ConditionalBypass.cs:74:13:74:29 | access to property Value : String | ConditionalBypass.cs:74:13:74:40 | ... == ... | +| ConditionalBypass.cs:85:34:85:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:86:13:86:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:86:13:86:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:86:13:86:29 | access to property Value : String | +| ConditionalBypass.cs:86:13:86:29 | access to property Value : String | ConditionalBypass.cs:86:13:86:40 | ... == ... | nodes | ConditionalBypass.cs:14:26:14:48 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | ConditionalBypass.cs:18:13:18:30 | ... == ... | semmle.label | ... == ... | | ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | semmle.label | access to property Cookies : HttpCookieCollection | +| ConditionalBypass.cs:24:13:24:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:24:13:24:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:24:13:24:45 | call to method Equals | semmle.label | call to method Equals | +| ConditionalBypass.cs:29:13:29:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:29:13:29:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:29:13:29:40 | ... == ... | semmle.label | ... == ... | | ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | semmle.label | call to method GetHostByAddress : IPHostEntry | +| ConditionalBypass.cs:46:13:46:20 | access to local variable hostInfo : IPHostEntry | semmle.label | access to local variable hostInfo : IPHostEntry | +| ConditionalBypass.cs:46:13:46:29 | access to property HostName : String | semmle.label | access to property HostName : String | | ConditionalBypass.cs:46:13:46:46 | ... == ... | semmle.label | ... == ... | +| ConditionalBypass.cs:51:13:51:20 | access to local variable hostInfo : IPHostEntry | semmle.label | access to local variable hostInfo : IPHostEntry | | ConditionalBypass.cs:51:13:51:29 | access to property HostName | semmle.label | access to property HostName | | ConditionalBypass.cs:72:34:72:52 | access to property Cookies : HttpCookieCollection | semmle.label | access to property Cookies : HttpCookieCollection | +| ConditionalBypass.cs:74:13:74:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:74:13:74:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:74:13:74:40 | ... == ... | semmle.label | ... == ... | | ConditionalBypass.cs:85:34:85:52 | access to property Cookies : HttpCookieCollection | semmle.label | access to property Cookies : HttpCookieCollection | +| ConditionalBypass.cs:86:13:86:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:86:13:86:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:86:13:86:40 | ... == ... | semmle.label | ... == ... | #select | ConditionalBypass.cs:19:13:19:33 | call to method login | ConditionalBypass.cs:14:26:14:48 | access to property QueryString : NameValueCollection | ConditionalBypass.cs:18:13:18:30 | ... == ... | Sensitive method may not be executed depending on $@, which flows from $@. | ConditionalBypass.cs:18:13:18:30 | ... == ... | this condition | ConditionalBypass.cs:14:26:14:48 | access to property QueryString | user input | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected b/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected index 3bdac85df06..0dd5f0c1e2c 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected @@ -3,7 +3,8 @@ edges | InappropriateEncoding.cs:15:28:15:40 | call to method Encode : String | InappropriateEncoding.cs:20:46:20:51 | access to local variable query1 | | InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:37:32:37:43 | access to local variable encodedValue | | InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:38:22:38:59 | ... + ... | -| InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:39:22:39:71 | call to method Format | +| InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:39:59:39:70 | access to local variable encodedValue : String | +| InappropriateEncoding.cs:39:59:39:70 | access to local variable encodedValue : String | InappropriateEncoding.cs:39:22:39:71 | call to method Format | | InappropriateEncoding.cs:57:28:57:56 | call to method HtmlEncode : String | InappropriateEncoding.cs:58:31:58:42 | access to local variable encodedValue | | InappropriateEncoding.cs:68:16:68:42 | call to method Replace : String | InappropriateEncoding.cs:15:28:15:40 | call to method Encode : String | | SqlEncode.cs:16:62:16:87 | call to method Replace : String | SqlEncode.cs:17:46:17:50 | access to local variable query | @@ -20,6 +21,7 @@ nodes | InappropriateEncoding.cs:37:32:37:43 | access to local variable encodedValue | semmle.label | access to local variable encodedValue | | InappropriateEncoding.cs:38:22:38:59 | ... + ... | semmle.label | ... + ... | | InappropriateEncoding.cs:39:22:39:71 | call to method Format | semmle.label | call to method Format | +| InappropriateEncoding.cs:39:59:39:70 | access to local variable encodedValue : String | semmle.label | access to local variable encodedValue : String | | InappropriateEncoding.cs:57:28:57:56 | call to method HtmlEncode : String | semmle.label | call to method HtmlEncode : String | | InappropriateEncoding.cs:58:31:58:42 | access to local variable encodedValue | semmle.label | access to local variable encodedValue | | InappropriateEncoding.cs:68:16:68:42 | call to method Replace : String | semmle.label | call to method Replace : String | diff --git a/docs/language/global-sphinx-files/_static/custom.css_t b/docs/language/global-sphinx-files/_static/custom.css_t index 0ba4ee628ce..927660034fe 100644 --- a/docs/language/global-sphinx-files/_static/custom.css_t +++ b/docs/language/global-sphinx-files/_static/custom.css_t @@ -7,19 +7,8 @@ * For the classes provided by the primer, see https://unpkg.com/@primer/css/dist/primer.css */ -/* -- FOOTER ------------------------------------------------------------------------------- */ -div.footer { - width: 100%; -} - -/* -- PRIVACY NOTICE ----------------------------------------------------------------------- */ - -div.privacy { - text-align: right; - padding-right: 5%; - padding-bottom: 20px; -} +/* -- CODE SNIPPETS ----------------------------------------------------------------------- */ code { font-size: 0.9em !important; /* makes code snippets in headings the correct size */ @@ -27,19 +16,28 @@ code { /* -- MAIN BODY ---------------------------------------------------------------------------- */ +main { + min-height: calc(100vh - 68px); +} + div.body { max-width: 100%; min-width: unset; + padding: 0; } div.body li { margin: 0 0 0.5em 0; /* Increase spacing between list items */ } +article { + min-height: calc(100vh - 145px); /* Makes sure GitHub footer stays at bottom of viewport */ +} + /* -- SIDEBAR ------------------------------------------------------------------------------- */ .SideNav { - height: 100vh; + max-height: 100vh; /* Makes sure sidebar doesn't cover GitHub footer */ } .SideNav li { diff --git a/docs/language/global-sphinx-files/_templates/layout.html b/docs/language/global-sphinx-files/_templates/layout.html index 029b7c851b2..d064b4fe6b8 100644 --- a/docs/language/global-sphinx-files/_templates/layout.html +++ b/docs/language/global-sphinx-files/_templates/layout.html @@ -36,7 +36,7 @@ {% endblock %} {%- block content %} - - -