From 28b878614f067944634afaa29a59c6beab2f8d7f Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Wed, 13 Nov 2024 13:33:04 +0100 Subject: [PATCH] Data flow: Implement new lambda flow interface for Java --- .../java/dataflow/internal/DataFlowNodes.qll | 5 ++- .../dataflow/internal/DataFlowPrivate.qll | 37 +++++++++++++++++-- .../java/dataflow/internal/DataFlowUtil.qll | 26 +++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll index bf867d21d3cc..d3d71f429999 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll @@ -6,6 +6,7 @@ private import semmle.code.java.dataflow.TypeFlow private import semmle.code.java.dataflow.FlowSteps private import DataFlowPrivate private import DataFlowUtil +private import DataFlowDispatch private import FlowSummaryImpl as FlowSummaryImpl private import DataFlowImplCommon as DataFlowImplCommon private import semmle.code.java.controlflow.Guards @@ -68,7 +69,9 @@ private module Cached { TMapKeyContent() or TMapValueContent() or TCapturedVariableContent(CapturedVariable v) or - TSyntheticFieldContent(SyntheticField s) + TSyntheticFieldContent(SyntheticField s) or + TLambdaReturn(Method m) or + TLambdaArgument(Method m, ArgumentPosition pos) cached newtype TContentApprox = diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 589d75c3635d..3c1e953cc500 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -464,7 +464,8 @@ private newtype TDataFlowCall = TCall(Call c) or TSummaryCall(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver) { FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) - } + } or + TLambdaSynthCall(Node node) { lambdaCreationHelper(node, _, _) } /** A call relevant for data flow. Includes both source calls and synthesized calls. */ class DataFlowCall extends TDataFlowCall { @@ -526,6 +527,19 @@ class SummaryCall extends DataFlowCall, TSummaryCall { override Location getLocation() { result = c.getLocation() } } +/** A synthesized call inside a `SummarizedCallable`. */ +class LambdaSynthCall extends DataFlowCall, TLambdaSynthCall { + private Node node; + + LambdaSynthCall() { this = TLambdaSynthCall(node) } + + override DataFlowCallable getEnclosingCallable() { result.asCallable() = node.getEnclosingCallable() } + + override string toString() { result = "[synthetic] call to " + node } + + override Location getLocation() { result = node.getLocation() } +} + class NodeRegion instanceof BasicBlock { string toString() { result = "NodeRegion" } @@ -585,8 +599,7 @@ predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode } class LambdaCallKind = Method; // the "apply" method in the functional interface -/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ -predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { +predicate lambdaCreationHelper(Node creation, LambdaCallKind kind, DataFlowCallable c) { exists(ClassInstanceExpr func, Interface t, FunctionalInterface interface | creation.asExpr() = func and func.getAnonymousClass().getAMethod() = c.asCallable() and @@ -597,6 +610,12 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) ) } +/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ +predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c, DataFlowCall synthCall) { + synthCall = TLambdaSynthCall(creation) and + lambdaCreationHelper(creation, kind, c) +} + /** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { receiver.(FlowSummaryNode).getSummaryNode() = call.(SummaryCall).getReceiver() and @@ -766,3 +785,15 @@ predicate containerContent(ContentSet c) { c instanceof MapKeyContent or c instanceof MapValueContent } + +Content getLambdaReturnContent(LambdaCallKind kind) { + result = TLambdaReturn(kind) +} + +Content getLambdaArgumentContent(LambdaCallKind kind, ArgumentPosition pos) { + result = TLambdaArgument(kind, pos) +} + +predicate isLambdaInstanceParameter(ParameterNode p) { + exists(DataFlowCallable c | lambdaCreationHelper(_, _, c) and p.isParameterOf(c, -1)) +} \ No newline at end of file diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll index c1fc51bff049..d52061c43747 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -4,6 +4,7 @@ private import java private import DataFlowPrivate +private import DataFlowDispatch private import semmle.code.java.dataflow.SSA private import semmle.code.java.controlflow.Guards private import semmle.code.java.dataflow.ExternalFlow @@ -359,6 +360,31 @@ class SyntheticFieldContent extends Content, TSyntheticFieldContent { override string toString() { result = s.toString() } } +class LambdaReturnContent extends Content, TLambdaReturn { + Method m; + + LambdaReturnContent() { this = TLambdaReturn(m) } + + override DataFlowType getType() { + result = getErasedRepr(m.getReturnType()) + } + + override string toString() { result = "" } +} + +class LambdaArgumentContent extends Content, TLambdaArgument { + Method m; + ArgumentPosition pos; + + LambdaArgumentContent() { + this = TLambdaArgument(m, pos) + } + + override DataFlowType getType() { + result = getErasedRepr(m.getParameter(pos).getType()) + } + override string toString() { result = "" + pos.toString() } +} /** * An entity that represents a set of `Content`s. *