Thanks to visit codestin.com
Credit goes to github.com

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

4 files changed

Lines changed: 145 additions & 58 deletions

File tree

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)