mirror of
https://github.com/github/codeql.git
synced 2026-04-23 15:55:18 +02:00
Merge pull request #17876 from hvitved/dataflow/param-flow-call-ctx
Data flow: Track call contexts in `parameterValueFlow`
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
| A.java:5:12:5:15 | this | A.java:5:12:5:19 | this.foo | A.java:2:7:2:9 | foo |
|
||||
| A.java:21:13:21:13 | a | A.java:21:13:21:22 | getFoo(...) | A.java:2:7:2:9 | foo |
|
||||
| A.java:23:9:23:9 | a | A.java:23:9:23:19 | aGetter(...) | A.java:2:7:2:9 | foo |
|
||||
| A.java:24:9:24:10 | a2 | A.java:24:9:24:23 | notAGetter(...) | A.java:2:7:2:9 | foo |
|
||||
| A.java:45:12:45:38 | maybeIdWrap(...) | A.java:45:12:45:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
|
||||
| A.java:49:12:49:38 | maybeIdWrap(...) | A.java:49:12:49:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
|
||||
|
||||
@@ -678,6 +678,8 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcReturn = CallContextReturn;
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
@@ -1338,6 +1340,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
* or summarized as a single read step with before and after types recorded
|
||||
* in the `ReadStepTypesOption` parameter.
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
* - Call contexts are taken into account.
|
||||
*/
|
||||
private module Final {
|
||||
/**
|
||||
@@ -1348,8 +1351,12 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read, string model) {
|
||||
parameterValueFlow0(p, node, read, model) and
|
||||
predicate parameterValueFlow(
|
||||
ParamNode p, Node node, ReadStepTypesOption read, string model,
|
||||
CachedCallContextSensitivity::CcNoCall ctx
|
||||
) {
|
||||
parameterValueFlow0(p, node, read, model, ctx) and
|
||||
Cand::cand(p, node) and
|
||||
if node instanceof CastingNode
|
||||
then
|
||||
// normal flow through
|
||||
@@ -1369,16 +1376,18 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0(
|
||||
ParamNode p, Node node, ReadStepTypesOption read, string model
|
||||
ParamNode p, Node node, ReadStepTypesOption read, string model,
|
||||
CachedCallContextSensitivity::CcNoCall ctx
|
||||
) {
|
||||
p = node and
|
||||
Cand::cand(p, _) and
|
||||
read = TReadStepTypesNone() and
|
||||
model = ""
|
||||
model = "" and
|
||||
CachedCallContextSensitivity::viableImplNotCallContextReducedReverse(ctx)
|
||||
or
|
||||
// local flow
|
||||
exists(Node mid, string model1, string model2 |
|
||||
parameterValueFlow(p, mid, read, model1) and
|
||||
parameterValueFlow(p, mid, read, model1, ctx) and
|
||||
simpleLocalFlowStep(mid, node, model2) and
|
||||
validParameterAliasStep(mid, node) and
|
||||
model = mergeModels(model1, model2)
|
||||
@@ -1386,50 +1395,106 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
or
|
||||
// read
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, TReadStepTypesNone(), model) and
|
||||
parameterValueFlow(p, mid, TReadStepTypesNone(), model, ctx) and
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypesFilter(getNodeDataFlowType(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read, model)
|
||||
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read, model, ctx)
|
||||
}
|
||||
|
||||
bindingset[ctx1, ctx2]
|
||||
pragma[inline_late]
|
||||
private CachedCallContextSensitivity::CcNoCall mergeContexts(
|
||||
CachedCallContextSensitivity::CcNoCall ctx1, CachedCallContextSensitivity::CcNoCall ctx2
|
||||
) {
|
||||
if CachedCallContextSensitivity::viableImplNotCallContextReducedReverse(ctx1)
|
||||
then result = ctx2
|
||||
else
|
||||
if CachedCallContextSensitivity::viableImplNotCallContextReducedReverse(ctx2)
|
||||
then result = ctx1
|
||||
else
|
||||
// check that `ctx1` is compatible with `ctx2` for at least _some_ outer call,
|
||||
// and then (arbitrarily) continue with `ctx2`
|
||||
exists(DataFlowCall someOuterCall, DataFlowCallable callable |
|
||||
someOuterCall =
|
||||
CachedCallContextSensitivity::viableImplCallContextReducedReverse(callable, ctx1) and
|
||||
someOuterCall =
|
||||
CachedCallContextSensitivity::viableImplCallContextReducedReverse(callable, ctx2) and
|
||||
result = ctx2
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0_0(
|
||||
ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read,
|
||||
string model
|
||||
string model, CachedCallContextSensitivity::CcNoCall ctx
|
||||
) {
|
||||
// flow through: no prior read
|
||||
exists(ArgNode arg, string model1, string model2 |
|
||||
parameterValueFlowArg(p, arg, mustBeNone, model1) and
|
||||
argumentValueFlowsThrough(arg, read, node, model2) and
|
||||
model = mergeModels(model1, model2)
|
||||
)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
exists(ArgNode arg, string model1, string model2 |
|
||||
parameterValueFlowArg(p, arg, read, model1) and
|
||||
argumentValueFlowsThrough(arg, mustBeNone, node, model2) and
|
||||
model = mergeModels(model1, model2)
|
||||
exists(
|
||||
ArgNode arg, string model1, string model2, CachedCallContextSensitivity::CcNoCall ctx1,
|
||||
CachedCallContextSensitivity::CcNoCall ctx2
|
||||
|
|
||||
model = mergeModels(model1, model2) and
|
||||
ctx = mergeContexts(ctx1, ctx2)
|
||||
|
|
||||
// flow through: no prior read
|
||||
parameterValueFlowArg(p, arg, mustBeNone, model1, ctx1) and
|
||||
argumentValueFlowsThrough(arg, read, node, model2, ctx2)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
parameterValueFlowArg(p, arg, read, model1, ctx1) and
|
||||
argumentValueFlowsThrough(arg, mustBeNone, node, model2, ctx2)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArg(
|
||||
ParamNode p, ArgNode arg, ReadStepTypesOption read, string model
|
||||
ParamNode p, ArgNode arg, ReadStepTypesOption read, string model,
|
||||
CachedCallContextSensitivity::CcNoCall ctx
|
||||
) {
|
||||
parameterValueFlow(p, arg, read, model) and
|
||||
parameterValueFlow(p, arg, read, model, ctx) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read, string model
|
||||
DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read, string model,
|
||||
CachedCallContextSensitivity::CcNoCall outerCtx
|
||||
) {
|
||||
exists(ParamNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturn(param, kind, read, model)
|
||||
exists(
|
||||
ParamNode param, DataFlowCallable callable,
|
||||
CachedCallContextSensitivity::CcNoCall innerCtx
|
||||
|
|
||||
viableParamArg(call, param, arg) and
|
||||
parameterValueFlowReturn(param, kind, read, model, innerCtx) and
|
||||
callable = nodeGetEnclosingCallable(param) and
|
||||
outerCtx = CachedCallContextSensitivity::getCallContextReturn(callable, call)
|
||||
|
|
||||
CachedCallContextSensitivity::viableImplNotCallContextReducedReverse(innerCtx)
|
||||
or
|
||||
call =
|
||||
CachedCallContextSensitivity::viableImplCallContextReducedReverse(callable, innerCtx)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough(
|
||||
ArgNode arg, ReadStepTypesOption read, Node out, string model,
|
||||
CachedCallContextSensitivity::CcNoCall ctx
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, read, model, ctx) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypesFilter(getNodeDataFlowType(arg), getNodeDataFlowType(out))
|
||||
or
|
||||
// getter
|
||||
compatibleTypesFilter(getNodeDataFlowType(arg), read.getContainerType()) and
|
||||
compatibleTypesFilter(read.getContentType(), getNodeDataFlowType(out))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1445,18 +1510,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate argumentValueFlowsThrough(
|
||||
ArgNode arg, ReadStepTypesOption read, Node out, string model
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, read, model) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypesFilter(getNodeDataFlowType(arg), getNodeDataFlowType(out))
|
||||
or
|
||||
// getter
|
||||
compatibleTypesFilter(getNodeDataFlowType(arg), read.getContainerType()) and
|
||||
compatibleTypesFilter(read.getContentType(), getNodeDataFlowType(out))
|
||||
)
|
||||
argumentValueFlowsThrough(arg, read, out, model, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1479,10 +1533,11 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
* container type, and the content type.
|
||||
*/
|
||||
private predicate parameterValueFlowReturn(
|
||||
ParamNode p, ReturnKind kind, ReadStepTypesOption read, string model
|
||||
ParamNode p, ReturnKind kind, ReadStepTypesOption read, string model,
|
||||
CachedCallContextSensitivity::CcNoCall ctx
|
||||
) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlow(p, ret, read, model) and
|
||||
parameterValueFlow(p, ret, read, model, ctx) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
}
|
||||
@@ -1498,7 +1553,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
* node `n`, in the same callable, using only value-preserving steps.
|
||||
*/
|
||||
private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone(), _)
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone(), _, _)
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
Reference in New Issue
Block a user