Skip to content

Commit d79eaff

Browse files
criemenaschackmull
authored andcommitted
Prune unreachable paths in the Java dataflow library based on call context.
We now detect patterns like f(bool cond){ if(cond) then A else B and prune branches for calls like f(true) or f(false). This pruning is done both in the local (bigstep) flow graph as well as in the inter-procedural dataflow graph.
1 parent dba93b3 commit d79eaff

File tree

4 files changed

+145
-58
lines changed

4 files changed

+145
-58
lines changed

java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -905,31 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
905905
*/
906906
pragma[nomagic]
907907
private predicate localFlowStepPlus(
908-
Node node1, Node node2, boolean preservesValue, Configuration config
908+
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
909909
) {
910+
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
911+
(
910912
localFlowEntry(node1, config) and
911913
(
912914
localFlowStep(node1, node2, config) and preservesValue = true
913915
or
914916
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
915917
) and
916918
node1 != node2 and
919+
cc.validFor(node1) and
917920
nodeCand(node2, unbind(config))
918921
or
919922
exists(Node mid |
920-
localFlowStepPlus(node1, mid, preservesValue, config) and
923+
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
921924
localFlowStep(mid, node2, config) and
922925
not mid instanceof CastNode and
923926
nodeCand(node2, unbind(config))
924927
)
925928
or
926929
exists(Node mid |
927-
localFlowStepPlus(node1, mid, _, config) and
930+
localFlowStepPlus(node1, mid, _, config, cc) and
928931
additionalLocalFlowStep(mid, node2, config) and
929932
not mid instanceof CastNode and
930933
preservesValue = false and
931934
nodeCand(node2, unbind(config))
932935
)
936+
)
933937
}
934938

935939
/**
@@ -938,9 +942,9 @@ private predicate localFlowStepPlus(
938942
*/
939943
pragma[noinline]
940944
private predicate localFlowBigStep(
941-
Node node1, Node node2, boolean preservesValue, Configuration config
945+
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
942946
) {
943-
localFlowStepPlus(node1, node2, preservesValue, config) and
947+
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
944948
localFlowExit(node2, config)
945949
}
946950

@@ -1000,7 +1004,7 @@ private class AccessPathFrontNilNode extends Node {
10001004
(
10011005
any(Configuration c).isSource(this)
10021006
or
1003-
localFlowBigStep(_, this, false, _)
1007+
localFlowBigStep(_, this, false, _, _)
10041008
or
10051009
additionalJumpStep(_, this, _)
10061010
)
@@ -1023,12 +1027,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
10231027
(
10241028
exists(Node mid |
10251029
flowCandFwd(mid, fromArg, apf, config) and
1026-
localFlowBigStep(mid, node, true, config)
1030+
localFlowBigStep(mid, node, true, config, _)
10271031
)
10281032
or
10291033
exists(Node mid, AccessPathFrontNil nil |
10301034
flowCandFwd(mid, fromArg, nil, config) and
1031-
localFlowBigStep(mid, node, false, config) and
1035+
localFlowBigStep(mid, node, false, config, _) and
10321036
apf = node.(AccessPathFrontNilNode).getApf()
10331037
)
10341038
or
@@ -1122,13 +1126,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
11221126
apf instanceof AccessPathFrontNil
11231127
or
11241128
exists(Node mid |
1125-
localFlowBigStep(node, mid, true, config) and
1129+
localFlowBigStep(node, mid, true, config, _) and
11261130
flowCand(mid, toReturn, apf, config)
11271131
)
11281132
or
11291133
exists(Node mid, AccessPathFrontNil nil |
11301134
flowCandFwd(node, _, apf, config) and
1131-
localFlowBigStep(node, mid, false, config) and
1135+
localFlowBigStep(node, mid, false, config, _) and
11321136
flowCand(mid, toReturn, nil, config) and
11331137
apf instanceof AccessPathFrontNil
11341138
)
@@ -1363,12 +1367,12 @@ private predicate flowFwd0(
13631367
(
13641368
exists(Node mid |
13651369
flowFwd(mid, fromArg, apf, ap, config) and
1366-
localFlowBigStep(mid, node, true, config)
1370+
localFlowBigStep(mid, node, true, config, _)
13671371
)
13681372
or
13691373
exists(Node mid, AccessPathNil nil |
13701374
flowFwd(mid, fromArg, _, nil, config) and
1371-
localFlowBigStep(mid, node, false, config) and
1375+
localFlowBigStep(mid, node, false, config, _) and
13721376
ap = node.(AccessPathNilNode).getAp() and
13731377
apf = ap.(AccessPathNil).getFront()
13741378
)
@@ -1472,13 +1476,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
14721476
ap instanceof AccessPathNil
14731477
or
14741478
exists(Node mid |
1475-
localFlowBigStep(node, mid, true, config) and
1479+
localFlowBigStep(node, mid, true, config, _) and
14761480
flow(mid, toReturn, ap, config)
14771481
)
14781482
or
14791483
exists(Node mid, AccessPathNil nil |
14801484
flowFwd(node, _, _, ap, config) and
1481-
localFlowBigStep(node, mid, false, config) and
1485+
localFlowBigStep(node, mid, false, config, _) and
14821486
flow(mid, toReturn, nil, config) and
14831487
ap instanceof AccessPathNil
14841488
)
@@ -1664,8 +1668,11 @@ module PathGraph {
16641668
*/
16651669
private class PathNodeMid extends PathNode, TPathNodeMid {
16661670
Node node;
1671+
16671672
CallContext cc;
1673+
16681674
AccessPath ap;
1675+
16691676
Configuration config;
16701677

16711678
PathNodeMid() { this = TPathNodeMid(node, cc, ap, config) }
@@ -1711,6 +1718,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
17111718
*/
17121719
private class PathNodeSink extends PathNode, TPathNodeSink {
17131720
Node node;
1721+
17141722
Configuration config;
17151723

17161724
PathNodeSink() { this = TPathNodeSink(node, config) }
@@ -1729,15 +1737,18 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
17291737
* a callable is recorded by `cc`.
17301738
*/
17311739
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
1732-
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
1740+
exists(LocalCallContext localCC | localCC.matchesCallContext(cc) |
1741+
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration(), localCC) and
17331742
cc = mid.getCallContext() and
17341743
ap = mid.getAp()
17351744
or
1736-
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
1745+
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration(), localCC) and
17371746
cc = mid.getCallContext() and
17381747
mid.getAp() instanceof AccessPathNil and
17391748
ap = node.(AccessPathNilNode).getAp()
1740-
or
1749+
) or
1750+
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
1751+
(
17411752
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
17421753
cc instanceof CallContextAny and
17431754
ap = mid.getAp()
@@ -1760,6 +1771,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
17601771
pathThroughCallable(mid, node, cc, ap)
17611772
or
17621773
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
1774+
)
17631775
}
17641776

17651777
pragma[noinline]
@@ -1880,7 +1892,7 @@ private predicate pathIntoCallable(
18801892
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
18811893
p.isParameterOf(callable, i)
18821894
|
1883-
if reducedViableImplInCallContext(_, callable, call)
1895+
if recordDataFlowCallSite(call, callable)
18841896
then innercc = TSpecificCall(call, i, emptyAp)
18851897
else innercc = TSomeCall(p, emptyAp)
18861898
)
@@ -2180,8 +2192,11 @@ private module FlowExploration {
21802192

21812193
private class PartialPathNodePriv extends PartialPathNode {
21822194
Node node;
2195+
21832196
CallContext cc;
2197+
21842198
PartialAccessPath ap;
2199+
21852200
Configuration config;
21862201

21872202
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, ap, config) }
@@ -2378,7 +2393,7 @@ private module FlowExploration {
23782393
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
23792394
p.isParameterOf(callable, i)
23802395
|
2381-
if reducedViableImplInCallContext(_, callable, call)
2396+
if recordDataFlowCallSite(call, callable)
23822397
then innercc = TSpecificCall(call, i, emptyAp)
23832398
else innercc = TSomeCall(p, emptyAp)
23842399
)
@@ -2446,7 +2461,6 @@ private module FlowExploration {
24462461
)
24472462
}
24482463
}
2449-
24502464
import FlowExploration
24512465

24522466
private predicate partialFlow(

java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ private module ImplCommon {
125125
outercc = TSomeCall(getAParameter(c), _)
126126
or
127127
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
128-
reducedViableImplInCallContext(_, c, other)
128+
recordDataFlowCallSite(other, c)
129129
)
130130
)
131131
}
@@ -152,7 +152,7 @@ private module ImplCommon {
152152
exists(int i, DataFlowCallable callable |
153153
viableParamArg1(p, callable, i, arg, outercc, call)
154154
|
155-
if reducedViableImplInCallContext(_, callable, call)
155+
if recordDataFlowCallSite(call, callable)
156156
then innercc = TSpecificCall(call, i, true)
157157
else innercc = TSomeCall(p, true)
158158
)
@@ -164,7 +164,7 @@ private module ImplCommon {
164164
exists(DataFlowCall call, int i, DataFlowCallable callable |
165165
result = TSpecificCall(call, i, _) and
166166
p.isParameterOf(callable, i) and
167-
reducedViableImplInCallContext(_, callable, call)
167+
recordDataFlowCallSite(call, callable)
168168
)
169169
}
170170

@@ -575,11 +575,21 @@ private module ImplCommon {
575575
exists(ArgumentNode arg | arg.argumentOf(call, -1))
576576
}
577577

578+
/**
579+
* Record a call site in the dataflow graph if it either improves
580+
* virtual dispatch or if we can remove unreachable edges by recoring this call site
581+
*/
582+
cached
583+
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
584+
reducedViableImplInCallContext(_, callable, call) or
585+
exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
586+
}
587+
578588
cached
579589
newtype TCallContext =
580590
TAnyCallContext() or
581591
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
582-
reducedViableImplInCallContext(_, _, call) and
592+
recordDataFlowCallSite(call, _) and
583593
(emptyAp = true or emptyAp = false) and
584594
(
585595
exists(call.getArgument(i))
@@ -593,6 +603,11 @@ private module ImplCommon {
593603
cached
594604
newtype TReturnPosition =
595605
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
606+
607+
cached
608+
newtype TLocalFlowCallContext =
609+
TAnyLocalCall() or
610+
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
596611
}
597612

598613
pragma[noinline]
@@ -609,7 +624,8 @@ private module ImplCommon {
609624
* - `TAnyCallContext()` : No restrictions on method flow.
610625
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
611626
* parameter at the given `call`. This call improves the set of viable
612-
* dispatch targets for at least one method call in the current callable.
627+
* dispatch targets for at least one method call in the current callable
628+
* or helps pruning unreachable nodes from the data flow graph.
613629
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
614630
* originating call does not improve the set of dispatch targets for any
615631
* method call in the current callable and was therefore not recorded.
@@ -633,6 +649,8 @@ private module ImplCommon {
633649
result = "CcCall(" + call + ", " + i + ")"
634650
)
635651
}
652+
653+
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
636654
}
637655

638656
class CallContextSomeCall extends CallContextCall, TSomeCall {
@@ -645,9 +663,53 @@ private module ImplCommon {
645663
}
646664
}
647665

666+
/**
667+
* A call context which is used to restrict local data flow nodes
668+
* to nodes which are actually reachable in a call context.
669+
*/
670+
abstract class LocalCallContext extends TLocalFlowCallContext {
671+
abstract string toString();
672+
673+
abstract predicate matchesCallContext(CallContext ctx);
674+
675+
abstract predicate validFor(Node n);
676+
}
677+
678+
class LocalCallContextAny extends LocalCallContext, TAnyLocalCall {
679+
override string toString() { result = "LocalCcAny" }
680+
681+
override predicate matchesCallContext(CallContext ctx) {
682+
not ctx instanceof CallContextSpecificCall or
683+
not exists(TSpecificLocalCall(ctx.(CallContextSpecificCall).getCall()))
684+
}
685+
686+
override predicate validFor(Node n) { any() }
687+
}
688+
689+
class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
690+
LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
691+
692+
DataFlowCall call;
693+
694+
DataFlowCall getCall() { result = call }
695+
696+
override string toString() { result = "LocalCcCall(" + call + ")" }
697+
698+
override predicate matchesCallContext(CallContext ctx) {
699+
ctx.(CallContextSpecificCall).getCall() = call
700+
}
701+
702+
override predicate validFor(Node n) {
703+
exists(Node n2 |
704+
isUnreachableInCall(n2, call) and n2.getEnclosingCallable() = n.getEnclosingCallable()
705+
)
706+
}
707+
}
708+
648709
/** A callable tagged with a relevant return kind. */
649710
class ReturnPosition extends TReturnPosition0 {
650711
private DataFlowCallable c;
712+
651713
private ReturnKind kind;
652714

653715
ReturnPosition() { this = TReturnPosition0(c, kind) }

0 commit comments

Comments
 (0)