Skip to content

Commit 06f83fe

Browse files
committed
Data flow: Add FeatureEscapesSourceCallContext(OrEqualSourceSinkCallContext) flow feature
1 parent 600f585 commit 06f83fe

File tree

3 files changed

+166
-35
lines changed

3 files changed

+166
-35
lines changed

shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll

Lines changed: 128 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
567567
) {
568568
sourceNode(node) and
569569
(if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and
570-
summaryCtx = TSummaryCtxNone() and
570+
summaryCtx.isSourceCtx() and
571571
t = getNodeTyp(node) and
572572
ap instanceof ApNil and
573573
apa = getApprox(ap) and
@@ -611,9 +611,11 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
611611
not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext
612612
or
613613
// flow into a callable with summary context (non-linear recursion)
614-
fwdFlowInFlowThrough(node, cc, t, ap, stored) and
615-
apa = getApprox(ap) and
616-
summaryCtx = TSummaryCtxSome(node, t, ap, stored)
614+
exists(boolean mustReturn |
615+
fwdFlowInFlowThrough(node, cc, t, ap, stored, mustReturn) and
616+
apa = getApprox(ap) and
617+
summaryCtx = TSummaryCtxSome(node, t, ap, stored, mustReturn)
618+
)
617619
or
618620
// flow out of a callable
619621
fwdFlowOut(_, _, node, cc, summaryCtx, t, ap, stored) and
@@ -630,9 +632,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
630632

631633
private newtype TSummaryCtx =
632634
TSummaryCtxNone() or
633-
TSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) {
634-
fwdFlowInFlowThrough(p, _, t, ap, stored)
635-
}
635+
TSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored, Boolean mustReturn) {
636+
fwdFlowInFlowThrough(p, _, t, ap, stored, mustReturn)
637+
} or
638+
TSummaryCtxSource(Boolean mustEscape)
636639

637640
/**
638641
* A context for generating flow summaries. This represents flow entry through
@@ -644,6 +647,55 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
644647
abstract string toString();
645648

646649
abstract Location getLocation();
650+
651+
/**
652+
* Holds if this context is the unique context used at flow sources.
653+
*/
654+
predicate isSourceCtx() {
655+
exists(boolean strict |
656+
Stage1::hasFeatureEscapesSourceCallContext(strict) and
657+
this = TSummaryCtxSource(strict)
658+
)
659+
or
660+
not Stage1::hasFeatureEscapesSourceCallContext(_) and
661+
this = TSummaryCtxNone()
662+
}
663+
664+
pragma[nomagic]
665+
private predicate isSome(boolean mustReturn) {
666+
this = TSummaryCtxSome(_, _, _, _, mustReturn)
667+
}
668+
669+
/**
670+
* Holds if this context is valid as a flow-in context when no flow-through is possible.
671+
*/
672+
predicate isValidForFlowInNoThrough() {
673+
this = TSummaryCtxNone()
674+
or
675+
this.isSome(false)
676+
}
677+
678+
/**
679+
* Holds if this context is valid as a flow-in context when flow-through is possible.
680+
*
681+
* The boolean `mustReturn` indicates whether flow must return.
682+
*/
683+
predicate isValidForFlowThrough(boolean mustReturn) {
684+
this = TSummaryCtxSource(_) and
685+
mustReturn = true
686+
or
687+
this = TSummaryCtxNone() and
688+
mustReturn = false
689+
or
690+
this.isSome(mustReturn)
691+
}
692+
693+
/** Holds if this context is valid as a sink context. */
694+
predicate isASinkCtx() {
695+
this.isValidForFlowInNoThrough()
696+
or
697+
this = TSummaryCtxSource(false)
698+
}
647699
}
648700

649701
/** A summary context from which no flow summary can be generated. */
@@ -659,20 +711,43 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
659711
private Typ t;
660712
private Ap ap;
661713
private TypOption stored;
714+
private boolean mustReturn;
662715

663-
SummaryCtxSome() { this = TSummaryCtxSome(p, t, ap, stored) }
716+
SummaryCtxSome() { this = TSummaryCtxSome(p, t, ap, stored, mustReturn) }
664717

665718
ParamNd getParamNode() { result = p }
666719

667720
private string ppTyp() { result = t.toString() and result != "" }
668721

722+
private string ppMustReturn() {
723+
if mustReturn = true then result = " <mustReturn>" else result = ""
724+
}
725+
669726
override string toString() {
670-
result = p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored)
727+
result =
728+
p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored) + this.ppMustReturn()
671729
}
672730

673731
override Location getLocation() { result = p.getLocation() }
674732
}
675733

734+
/**
735+
* A special summary context that is used when the flow feature
736+
* `FeatureEscapesSourceCallContext(OrEqualSourceSinkCallContext)`
737+
* is enabled.
738+
*/
739+
private class SummaryCtxSource extends SummaryCtx, TSummaryCtxSource {
740+
private boolean mustEscape;
741+
742+
SummaryCtxSource() { this = TSummaryCtxSource(mustEscape) }
743+
744+
override string toString() {
745+
if mustEscape = true then result = "<source-must-escape>" else result = "<source>"
746+
}
747+
748+
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
749+
}
750+
676751
private predicate fwdFlowJump(Nd node, Typ t, Ap ap, TypOption stored) {
677752
exists(Nd mid |
678753
fwdFlow(mid, _, _, t, ap, stored) and
@@ -917,7 +992,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
917992
private predicate fwdFlowInNoFlowThrough(
918993
ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored
919994
) {
920-
FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _)
995+
exists(SummaryCtx summaryCtx |
996+
FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, _, innercc, summaryCtx, t, ap, stored, _) and
997+
summaryCtx.isValidForFlowInNoThrough()
998+
)
921999
}
9221000

9231001
private predicate top() { any() }
@@ -926,9 +1004,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
9261004

9271005
pragma[nomagic]
9281006
private predicate fwdFlowInFlowThrough(
929-
ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored
1007+
ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored, boolean mustReturn
9301008
) {
931-
FwdFlowInThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _)
1009+
exists(SummaryCtx outerSummaryCtx |
1010+
FwdFlowInThrough::fwdFlowIn(_, _, _, p, _, innercc, outerSummaryCtx, t, ap, stored, _) and
1011+
outerSummaryCtx.isValidForFlowThrough(mustReturn)
1012+
)
9321013
}
9331014

9341015
pragma[nomagic]
@@ -999,7 +1080,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
9991080
TypOption stored
10001081
) {
10011082
exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow |
1002-
fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and
1083+
fwdFlowIntoRet(ret, innercc, _, t, ap, stored) and
1084+
summaryCtx = TSummaryCtxNone() and
10031085
fwdFlowOutValidEdge(call, ret, innercc, inner, out, outercc, allowsFieldFlow) and
10041086
if allowsFieldFlow = false then ap instanceof ApNil else any()
10051087
)
@@ -1090,7 +1172,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
10901172
instanceofCcCall(ccc) and
10911173
fwdFlow(pragma[only_bind_into](ret), ccc, summaryCtx, t, ap, stored) and
10921174
summaryCtx =
1093-
TSummaryCtxSome(pragma[only_bind_into](p), _, pragma[only_bind_into](argAp), _) and
1175+
TSummaryCtxSome(pragma[only_bind_into](p), _, pragma[only_bind_into](argAp), _, _) and
10941176
kind = ret.getKind() and
10951177
Stage1::parameterFlowThroughAllowed(p, kind) and
10961178
PrevStage::returnMayFlowThrough(ret, kind)
@@ -1116,9 +1198,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
11161198
pragma[nomagic]
11171199
private predicate fwdFlowIsEntered0(
11181200
Call call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, ParamNd p, Typ t,
1119-
Ap ap, TypOption stored
1201+
Ap ap, TypOption stored, boolean mustReturn
11201202
) {
1121-
FwdFlowInThrough::fwdFlowIn(call, arg, _, p, cc, innerCc, summaryCtx, t, ap, stored, _)
1203+
FwdFlowInThrough::fwdFlowIn(call, arg, _, p, cc, innerCc, summaryCtx, t, ap, stored, _) and
1204+
summaryCtx.isValidForFlowThrough(mustReturn)
11221205
}
11231206

11241207
/**
@@ -1130,9 +1213,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
11301213
Call call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx,
11311214
SummaryCtxSome innerSummaryCtx
11321215
) {
1133-
exists(ParamNd p, Typ t, Ap ap, TypOption stored |
1134-
fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, t, ap, stored) and
1135-
innerSummaryCtx = TSummaryCtxSome(p, t, ap, stored)
1216+
exists(ParamNd p, Typ t, Ap ap, TypOption stored, boolean mustReturn |
1217+
fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, t, ap, stored, mustReturn) and
1218+
innerSummaryCtx = TSummaryCtxSome(p, t, ap, stored, mustReturn)
11361219
)
11371220
}
11381221

@@ -1160,7 +1243,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
11601243
TypOption argStored, Ap ap
11611244
) {
11621245
exists(Call call, boolean allowsFieldFlow |
1163-
returnFlowsThrough0(call, ccc, ap, ret, TSummaryCtxSome(p, argT, argAp, argStored)) and
1246+
returnFlowsThrough0(call, ccc, ap, ret, TSummaryCtxSome(p, argT, argAp, argStored, _)) and
11641247
flowThroughOutOfCall(call, ret, _, allowsFieldFlow) and
11651248
pos = ret.getReturnPosition() and
11661249
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1216,7 +1299,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
12161299

12171300
pragma[nomagic]
12181301
private predicate revFlow0(Nd node, ReturnCtx returnCtx, ApOption returnAp, Ap ap) {
1219-
fwdFlow(node, _, _, _, ap, _) and
1302+
fwdFlow(node, _, any(SummaryCtx sinkCtx | sinkCtx.isASinkCtx()), _, ap, _) and
12201303
sinkNode(node) and
12211304
(
12221305
if hasSinkCallCtx()
@@ -1490,7 +1573,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
14901573
exists(Ap ap0 |
14911574
parameterMayFlowThrough(p, _) and
14921575
revFlow(n, TReturnCtxMaybeFlowThrough(_), _, ap0) and
1493-
fwdFlow(n, any(CcCall ccc), TSummaryCtxSome(p, _, ap, _), _, ap0, _)
1576+
fwdFlow(n, any(CcCall ccc), TSummaryCtxSome(p, _, ap, _, _), _, ap0, _)
14941577
)
14951578
}
14961579

@@ -1962,6 +2045,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
19622045
private string ppSummaryCtx() {
19632046
summaryCtx instanceof SummaryCtxNone and result = ""
19642047
or
2048+
result = " " + summaryCtx.(SummaryCtxSource)
2049+
or
19652050
summaryCtx instanceof SummaryCtxSome and
19662051
result = " <" + summaryCtx + ">"
19672052
}
@@ -1983,14 +2068,15 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
19832068
override predicate isSource() {
19842069
sourceNode(node) and
19852070
(if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and
1986-
summaryCtx = TSummaryCtxNone() and
2071+
summaryCtx.isSourceCtx() and
19872072
t = getNodeTyp(node) and
19882073
ap instanceof ApNil
19892074
}
19902075

19912076
predicate isAtSink() {
19922077
sinkNode(node) and
19932078
ap instanceof ApNil and
2079+
summaryCtx.isASinkCtx() and
19942080
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
19952081
// is exactly what we need to check.
19962082
// For `FeatureEqualSourceSinkCallContext` the initial call
@@ -2042,10 +2128,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
20422128
override predicate isSource() { sourceNode(node) }
20432129
}
20442130

2045-
bindingset[p, t, ap, stored]
2131+
bindingset[p, t, ap, stored, mustReturn]
20462132
pragma[inline_late]
2047-
private SummaryCtxSome mkSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) {
2048-
result = TSummaryCtxSome(p, t, ap, stored)
2133+
private SummaryCtxSome mkSummaryCtxSome(
2134+
ParamNd p, Typ t, Ap ap, TypOption stored, boolean mustReturn
2135+
) {
2136+
result = TSummaryCtxSome(p, t, ap, stored, mustReturn)
20492137
}
20502138

20512139
pragma[nomagic]
@@ -2055,11 +2143,15 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
20552143
) {
20562144
FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap,
20572145
stored, _) and
2146+
outerSummaryCtx.isValidForFlowInNoThrough() and
20582147
innerSummaryCtx = TSummaryCtxNone()
20592148
or
2060-
FwdFlowInThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap,
2061-
stored, _) and
2062-
innerSummaryCtx = mkSummaryCtxSome(p, t, ap, stored)
2149+
exists(boolean mustReturn |
2150+
FwdFlowInThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap,
2151+
stored, _) and
2152+
outerSummaryCtx.isValidForFlowThrough(mustReturn) and
2153+
innerSummaryCtx = mkSummaryCtxSome(p, t, ap, stored, mustReturn)
2154+
)
20632155
}
20642156

20652157
pragma[nomagic]
@@ -2098,7 +2190,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
20982190
|
20992191
fwdFlowThroughStep0(call, arg, cc, ccc, summaryCtx, t, ap, stored, ret,
21002192
innerSummaryCtx) and
2101-
innerSummaryCtx = TSummaryCtxSome(p, innerArgT, innerArgAp, innerArgStored) and
2193+
innerSummaryCtx = TSummaryCtxSome(p, innerArgT, innerArgAp, innerArgStored, _) and
21022194
pn1 = mkPathNode(arg, cc, summaryCtx, innerArgT, innerArgAp, innerArgStored) and
21032195
pn2 =
21042196
typeStrengthenToPathNode(p, ccc, innerSummaryCtx, innerArgT, innerArgAp,
@@ -2212,11 +2304,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
22122304
)
22132305
or
22142306
// flow out of a callable
2215-
exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow |
2216-
pn1 = TPathNodeMid(ret, innercc, summaryCtx, t, ap, stored) and
2217-
fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and
2307+
exists(
2308+
RetNd ret, CcNoCall innercc, SummaryCtx innerSummaryCtx, boolean allowsFieldFlow
2309+
|
2310+
pn1 = TPathNodeMid(ret, innercc, innerSummaryCtx, t, ap, stored) and
2311+
fwdFlowIntoRet(ret, innercc, innerSummaryCtx, t, ap, stored) and
22182312
fwdFlowOutValidEdge(_, ret, innercc, _, node, cc, allowsFieldFlow) and
22192313
label = "" and
2314+
summaryCtx = TSummaryCtxNone() and
22202315
if allowsFieldFlow = false then ap instanceof ApNil else any()
22212316
)
22222317
}

shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module;
33

44
private import codeql.dataflow.DataFlow
55
private import codeql.typetracking.TypeTracking as Tt
6+
private import codeql.util.Boolean
67
private import codeql.util.Location
78
private import codeql.util.Option
89
private import codeql.util.Unit
@@ -46,10 +47,11 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
4647
}
4748
}
4849

49-
private newtype TFlowFeature =
50+
newtype TFlowFeature =
5051
TFeatureHasSourceCallContext() or
5152
TFeatureHasSinkCallContext() or
52-
TFeatureEqualSourceSinkCallContext()
53+
TFeatureEqualSourceSinkCallContext() or
54+
TFeatureEscapesSourceCallContext(Boolean strict)
5355

5456
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
5557
class FlowFeature extends TFlowFeature {
@@ -80,6 +82,34 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
8082
override string toString() { result = "FeatureEqualSourceSinkCallContext" }
8183
}
8284

85+
/**
86+
* A flow configuration feature that implies that the sink must be reached from
87+
* the source by escaping the source call context, that is, flow must either
88+
* return from the callable containing the source or use a jump-step before reaching
89+
* the sink.
90+
*/
91+
class FeatureEscapesSourceCallContext extends FlowFeature, TFeatureEscapesSourceCallContext {
92+
FeatureEscapesSourceCallContext() { this = TFeatureEscapesSourceCallContext(true) }
93+
94+
override string toString() { result = "FeatureEscapesSourceCallContext" }
95+
}
96+
97+
/**
98+
* A flow configuration feature that is the disjuction of `FeatureEscapesSourceCallContext`
99+
* and `FeatureEqualSourceSinkCallContext`.
100+
*/
101+
class FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext extends FlowFeature,
102+
TFeatureEscapesSourceCallContext
103+
{
104+
FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext() {
105+
this = TFeatureEscapesSourceCallContext(false)
106+
}
107+
108+
override string toString() {
109+
result = "FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext"
110+
}
111+
}
112+
83113
/**
84114
* Holds if `source` is a relevant data flow source.
85115
*/

0 commit comments

Comments
 (0)