Skip to content
Closed
Show file tree
Hide file tree
Changes from 12 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
Expand Up @@ -76,7 +76,8 @@ private module Cached {
} or
TSummaryCall(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
} or
TLambdaSynthCall(Node creation) { lambdaCreation(creation, _, _) }

/** Gets a viable run-time target for the call `call`. */
cached
Expand Down Expand Up @@ -497,6 +498,24 @@ class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
override Location getLocation() { result = c.getLocation() }
}

class LambdaSynthCall extends DataFlowCall, TLambdaSynthCall {
private NodeImpl creation;

LambdaSynthCall() { this = TLambdaSynthCall(creation) }

override DataFlowCallable getARuntimeTarget() { none() }

override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() }

override DataFlow::Node getNode() { none() }

override DataFlowCallable getEnclosingCallable() { result = creation.getEnclosingCallableImpl() }

override string toString() { result = "[lambda] call to " + creation }

override Location getLocation() { result = creation.getLocation() }
}

/** A parameter position. */
class ParameterPosition extends TParameterPosition {
/** Gets the underlying integer position, if any. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,12 @@
* Needed for flow through captured variables, where we treat local functions
* as if they were lambdas.
*/
abstract private class LocalFunctionCreationNode extends NodeImpl, TLocalFunctionCreationNode {
private class LocalFunctionCreationNode extends NodeImpl, TLocalFunctionCreationNode {
ControlFlow::Nodes::ElementNode cfn;
LocalFunction function;
boolean isPostUpdate;

LocalFunctionCreationNode() {
this = TLocalFunctionCreationNode(cfn, isPostUpdate) and
this = TLocalFunctionCreationNode(cfn) and
function = cfn.getAstNode().(LocalFunctionStmt).getLocalFunction()
}

Expand All @@ -156,10 +155,6 @@
ControlFlow::Nodes::ElementNode getUnderlyingControlFlowNode() { result = cfn }

override Location getLocationImpl() { result = cfn.getLocation() }
}

private class LocalFunctionCreationPreNode extends LocalFunctionCreationNode {
LocalFunctionCreationPreNode() { isPostUpdate = false }

override string toStringImpl() { result = cfn.toString() }
}
Expand Down Expand Up @@ -419,17 +414,14 @@
result.(Flow::ExprNode).getExpr() =
[
n.(ExprNode).getControlFlowNode(),
n.(LocalFunctionCreationPreNode).getUnderlyingControlFlowNode()
n.(LocalFunctionCreationNode).getUnderlyingControlFlowNode()
]
or
result.(Flow::VariableWriteSourceNode).getVariableWrite().getRhs() =
n.(ExprNode).getControlFlowNode()
or
result.(Flow::ExprPostUpdateNode).getExpr() =
[
n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode(),
n.(LocalFunctionCreationPostUpdateNode).getUnderlyingControlFlowNode()
]
[n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode(),]

Check warning

Code scanning / CodeQL

Singleton set literal Warning

Singleton set literal can be replaced by its member.
or
result.(Flow::ParameterNode).getParameter().getParameterNode() = n
or
Expand Down Expand Up @@ -767,6 +759,8 @@
VariableCapture::valueStep(nodeFrom, nodeTo)
or
nodeTo = nodeFrom.(LocalFunctionCreationNode).getAnAccess(true)
or
delegateCreationStep(nodeFrom, nodeTo)
) and
model = ""
or
Expand Down Expand Up @@ -1073,7 +1067,7 @@
l = c.getARelevantLocation()
} or
TDelegateSelfReferenceNode(Callable c) { lambdaCreationExpr(_, c) } or
TLocalFunctionCreationNode(ControlFlow::Nodes::ElementNode cfn, Boolean isPostUpdate) {
TLocalFunctionCreationNode(ControlFlow::Nodes::ElementNode cfn) {
cfn.getAstNode() instanceof LocalFunctionStmt
} or
TYieldReturnNode(ControlFlow::Nodes::ElementNode cfn) {
Expand Down Expand Up @@ -1150,6 +1144,14 @@
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
TDelegateCallArgumentContent(int i) {
i = [0 .. max(any(DelegateLikeCall dc).getNumberOfArguments()) - 1]
or
i in [0 .. 1000] // todo
or
// exists(ArgumentPosition apos |
// FlowSummaryImpl::Private::summaryArgumentNode(_, _, apos) and
// i = apos.getPosition()
// )
i = -1
} or
TDelegateCallReturnContent()

Expand Down Expand Up @@ -2600,7 +2602,7 @@
or
[
n.asExpr().(ControlFlowElement),
n.(LocalFunctionCreationPreNode).getUnderlyingControlFlowNode().getAstNode()
n.(LocalFunctionCreationNode).getUnderlyingControlFlowNode().getAstNode()
] = result.getADelegateCreation()
}

Expand Down Expand Up @@ -2835,16 +2837,6 @@
override string toStringImpl() { result = "[post] this" }
}

class LocalFunctionCreationPostUpdateNode extends LocalFunctionCreationNode, PostUpdateNode {
LocalFunctionCreationPostUpdateNode() { isPostUpdate = true }

override LocalFunctionCreationPreNode getPreUpdateNode() {
result = TLocalFunctionCreationNode(cfn, false)
}

override string toStringImpl() { result = "[post] " + cfn }
}

private class CapturePostUpdateNode extends PostUpdateNode, CaptureNode {
private CaptureNode pre;

Expand Down Expand Up @@ -2908,7 +2900,11 @@
* Holds if access paths with `c` at their head always should be tracked at high
* precision. This disables adaptive access path precision for such access paths.
*/
predicate forceHighPrecision(Content c) { c instanceof ElementContent }
predicate forceHighPrecision(Content c) {
c instanceof ElementContent or
c instanceof DelegateCallArgumentContent or
c instanceof DelegateCallReturnContent
}

private predicate lambdaCreationExpr(ControlFlowElement creation, Callable c) {
c =
Expand All @@ -2924,10 +2920,46 @@

/** Holds if `creation` is an expression that creates a delegate for `c`. */
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
lambdaCreationExpr(creation.asExpr(), c.asCallable(_)) and
(
lambdaCreationExpr(creation.asExpr(), c.asCallable(_))
or
creation.(LocalFunctionCreationNode).getFunction() = c.asCallable(_)
) and
exists(kind)
}

/** Holds if `creation` is an expression that creates a delegate for `c`. */
predicate lambdaCreation(
Node creation, LambdaCallKind kind, DataFlowCallable c, DataFlowCall synthCall
) {
lambdaCreation(creation, kind, c) and
synthCall = TLambdaSynthCall(creation)
}

Content getLambdaReturnContent(LambdaCallKind kind, ReturnKind rk) {
result = TDelegateCallReturnContent() and
exists(kind) and
rk = TNormalReturnKind()
}

Content getLambdaArgumentContent(LambdaCallKind kind, ArgumentPosition pos) {
(
result = TDelegateCallArgumentContent(pos.getPosition())
or
result = TDelegateCallArgumentContent(-1) and
pos.isDelegateSelf()
) and
exists(kind)
}

predicate isLambdaInstanceParameter(ParameterNode p) {
exists(DataFlowCallable c, ParameterPosition ppos |
lambdaCreation(_, _, c) and
isParameterNode(p, c, ppos) and
ppos.isDelegateSelf()
)
}

private predicate isLocalFunctionCallReceiver(
LocalFunctionCall call, LocalFunctionAccess receiver, LocalFunction f
) {
Expand Down Expand Up @@ -2973,9 +3005,7 @@
/** Holds if `call` is a lambda call where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
lambdaCallExpr(call, receiver.(ExprNode).getControlFlowNode()) and
// local function calls can be resolved directly without a flow analysis
not call.getControlFlowNode().getAstNode() instanceof LocalFunctionCall
lambdaCallExpr(call, receiver.(ExprNode).getControlFlowNode()) //and
or
receiver.(FlowSummaryNode).getSummaryNode() = call.(SummaryCall).getReceiver()
) and
Expand Down Expand Up @@ -3052,6 +3082,8 @@
or
VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(DelegateSelfReferenceNode)
.getCallable())
or
p.getType() instanceof SystemLinqExpressions::DelegateExtType
}

/** An approximated `Content`. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ private import DataFlowDispatch
private import DataFlowPrivate
private import semmle.code.csharp.controlflow.Guards
private import semmle.code.csharp.Unification
private import semmle.code.csharp.frameworks.system.linq.Expressions

/**
* An element, viewed as a node in a data flow graph. Either an expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

private import csharp
private import semmle.code.csharp.commons.QualifiedName
private import semmle.code.csharp.frameworks.system.linq.Expressions
private import codeql.dataflow.internal.FlowSummaryImpl
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
private import DataFlowImplSpecific as DataFlowImplSpecific
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand All @@ -78,7 +81,9 @@ private module Cached {
TMapKeyContentApprox() or
TMapValueContentApprox() or
TCapturedVariableContentApprox(CapturedVariable v) or
TSyntheticFieldApproxContent()
TSyntheticFieldApproxContent() or
TLambdaReturnApprox(Method m) or
TLambdaArgumentApprox(Method m, ArgumentPosition pos)
}

import Cached
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@
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 {
Expand Down Expand Up @@ -526,6 +527,19 @@
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" }

Expand Down Expand Up @@ -585,8 +599,7 @@

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) {

Check warning

Code scanning / CodeQL

Candidate predicate not marked as `nomagic` Warning

Candidate predicate to
lambdaCreation
is not marked as nomagic.
exists(ClassInstanceExpr func, Interface t, FunctionalInterface interface |
creation.asExpr() = func and
func.getAnonymousClass().getAMethod() = c.asCallable() and
Expand All @@ -597,6 +610,12 @@
)
}

/** 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
Expand Down Expand Up @@ -755,6 +774,13 @@
)
or
c instanceof SyntheticFieldContent and result = TSyntheticFieldApproxContent()
or
exists(Method m |
c = TLambdaReturn(m) and result = TLambdaReturnApprox(m))
or
exists(Method m, ArgumentPosition pos |
c = TLambdaArgument(m, pos) and result = TLambdaArgumentApprox(m, pos))

}

/**
Expand All @@ -766,3 +792,15 @@
c instanceof MapKeyContent or
c instanceof MapValueContent
}

Content getLambdaReturnContent(LambdaCallKind kind, ReturnKind k) {
result = TLambdaReturn(kind) and exists(k)
}

Content getLambdaArgumentContent(LambdaCallKind kind, ArgumentPosition pos) {
result = TLambdaArgument(kind, pos)
}

predicate isLambdaInstanceParameter(ParameterNode p) {
exists(DataFlowCallable c | lambdaCreationHelper(_, _, c) and p.isParameterOf(c, -1))
}
26 changes: 26 additions & 0 deletions java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -359,6 +360,31 @@
override string toString() { result = s.toString() }
}

class LambdaReturnContent extends Content, TLambdaReturn {

Check warning on line 363 in java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll

View workflow job for this annotation

GitHub Actions / qldoc

Missing QLdoc for class DataFlowUtil::LambdaReturnContent
Method m;

LambdaReturnContent() { this = TLambdaReturn(m) }

override DataFlowType getType() {
result = getErasedRepr(m.getReturnType())
}

override string toString() { result = "<lambda-return>" }
}

class LambdaArgumentContent extends Content, TLambdaArgument {

Check warning on line 375 in java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll

View workflow job for this annotation

GitHub Actions / qldoc

Missing QLdoc for class DataFlowUtil::LambdaArgumentContent
Method m;
ArgumentPosition pos;

LambdaArgumentContent() {
this = TLambdaArgument(m, pos)
}

override DataFlowType getType() {
result = getErasedRepr(m.getParameter(pos).getType())
}
override string toString() { result = "<lambda-argument>" + pos.toString() }
}
/**
* An entity that represents a set of `Content`s.
*
Expand Down
Loading
Loading