Skip to content

Commit

Permalink
Merge pull request #17876 from hvitved/dataflow/param-flow-call-ctx
Browse files Browse the repository at this point in the history
Data flow: Track call contexts in `parameterValueFlow`
  • Loading branch information
hvitved authored Nov 21, 2024
2 parents 932ced4 + 3f56fc9 commit 6dc599c
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 41 deletions.
1 change: 0 additions & 1 deletion java/ql/test/library-tests/dataflow/getter/getter.expected
Original file line number Diff line number Diff line change
@@ -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 |
135 changes: 95 additions & 40 deletions shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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 {
/**
Expand All @@ -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
Expand All @@ -1369,84 +1376,116 @@ 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)
)
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
Expand All @@ -1459,6 +1498,21 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> 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
Expand All @@ -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()
)
}
Expand All @@ -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
Expand Down

0 comments on commit 6dc599c

Please sign in to comment.