diff --git a/java/ql/test/library-tests/dataflow/getter/getter.expected b/java/ql/test/library-tests/dataflow/getter/getter.expected index b5af3f91a593..9a36107f1983 100644 --- a/java/ql/test/library-tests/dataflow/getter/getter.expected +++ b/java/ql/test/library-tests/dataflow/getter/getter.expected @@ -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 | diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll index 81f9946126db..e477913a59bd 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -678,6 +678,8 @@ module MakeImplCommon Lang> { class CcNoCall = CallContextNoCall; + class CcReturn = CallContextReturn; + Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } @@ -1338,6 +1340,7 @@ module MakeImplCommon 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 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 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,67 +1395,97 @@ module MakeImplCommon 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) ) } - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - cached - predicate argumentValueFlowsThrough( - ArgNode arg, ReadStepTypesOption read, Node out, string model + 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) and + argumentValueFlowsThrough0(call, arg, kind, read, model, ctx) and out = getAnOutNode(call, kind) | // normal flow through @@ -1459,6 +1498,21 @@ module MakeImplCommon Lang> { ) } + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + cached + predicate argumentValueFlowsThrough( + ArgNode arg, ReadStepTypesOption read, Node out, string model + ) { + argumentValueFlowsThrough(arg, read, out, model, _) + } + /** * Holds if `arg` flows to `out` through a call using only * value-preserving steps and a single read step, not taking call @@ -1479,10 +1533,11 @@ module MakeImplCommon 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 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