Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data flow: Track call contexts in parameterValueFlow #17876

Merged
merged 3 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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