Skip to content
Draft
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
136 changes: 128 additions & 8 deletions java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ private predicate deadcode(Expr e) {
module SsaFlow {
module Impl = SsaImpl::DataFlowIntegration;

private predicate ssaDefAssigns(SsaExplicitWrite def, Expr value) {
private predicate ssaDefAssigns(SsaExplicitWrite def, Node value) {
exists(VariableUpdate upd | upd = def.getDefiningExpr() |
value = upd.(VariableAssign).getSource() or
value = upd.(AssignOp) or
value = upd.(RecordBindingVariableExpr)
value.asExpr() = upd.(VariableAssign).getSource() or
value.asExpr() = upd.(AssignOp) or
value.asExpr() = upd.(RecordBindingVariableExpr) or
value.(CatchParameterNode).getVariable() = upd
)
}

Expand All @@ -49,7 +50,7 @@ module SsaFlow {
result.(Impl::WriteDefSourceNode).getDefinition().(SsaParameterInit).getParameter() = p
)
or
ssaDefAssigns(result.(Impl::WriteDefSourceNode).getDefinition(), n.asExpr())
ssaDefAssigns(result.(Impl::WriteDefSourceNode).getDefinition(), n)
}

predicate localFlowStep(SsaSourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) {
Expand Down Expand Up @@ -99,6 +100,11 @@ private module Cached {
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TFieldValueNode(Field f) or
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn) or
TExceptionOutNode(DataFlowCall call) or
TExceptionReturnNode(DataFlowCallable callable) or
TCatchTypeTestNode(CatchClause catch) or
TCatchParameterNode(CatchClause catch) or
TUncaughtNode(TryStmt try) { ExceptionFlow::tryCatch(try, _) } or
TAdditionalNode(Expr e, string id) { any(AdditionalDataFlowNode adfn).nodeAt(e, id) }

cached
Expand Down Expand Up @@ -177,6 +183,14 @@ module Public {
or
result = this.(FieldValueNode).getField().getType()
or
result instanceof TypeException and this instanceof ExceptionOutNode
or
result instanceof TypeException and this instanceof CatchTypeTestNode
or
result = this.(CatchParameterNode).getVariable().getType()
or
result instanceof TypeException and this instanceof UncaughtNode
or
result instanceof TypeObject and this instanceof AdditionalNode
or
result = this.(SsaNode).getTypeImpl()
Expand Down Expand Up @@ -383,6 +397,27 @@ module Public {
predicate isOwnInstanceAccess() { this.getInstanceAccess().isOwnInstanceAccess() }
}

/**
* A node representing a thrown exception as the result of a call.
*/
class ExceptionOutNode extends Node, TExceptionOutNode {
override string toString() { result = "Exception out: " + this.getCall().toString() }

override Location getLocation() { result = this.getCall().getLocation() }

/** Gets the associated call. */
DataFlowCall getCall() { this = TExceptionOutNode(result) }
}

/**
* A node representing a thrown exception being returned from a callable.
*/
class ExceptionReturnNode extends Node, TExceptionReturnNode {
override string toString() { result = "Exception return" }

override Location getLocation() { result = this.getEnclosingCallable().getLocation() }
}

/** A node introduced by an extension of `AdditionalDataFlowNode`. */
class AdditionalNode extends Node, TAdditionalNode {
Expr e_;
Expand Down Expand Up @@ -455,6 +490,11 @@ module Private {
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
result.asFieldScope() = n.(FieldValueNode).getField() or
result = n.(ExceptionOutNode).getCall().getEnclosingCallable() or
n = TExceptionReturnNode(result) or
result.asCallable() = n.(CatchTypeTestNode).getCatch().getEnclosingCallable() or
result.asCallable() = n.(CatchParameterNode).getCatch().getEnclosingCallable() or
result.asCallable() = n.(UncaughtNode).getTry().getEnclosingCallable() or
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable() or
result.asCallable() = n.(SsaNode).getBasicBlock().getEnclosingCallable()
}
Expand Down Expand Up @@ -507,15 +547,23 @@ module Private {
DataFlowCall getCall() { this.argumentOf(result, _) }
}

/** A data flow node that occurs as the result of a `ReturnStmt`. */
/**
* A data flow node that occurs as the result of a `ReturnStmt` or an
* exception being returned.
*/
class ReturnNode extends Node {
ReturnNode() {
exists(ReturnStmt ret | this.asExpr() = ret.getResult()) or
this.(FlowSummaryNode).isReturn()
this.(FlowSummaryNode).isReturn() or
this instanceof ExceptionReturnNode
}

/** Gets the kind of this returned value. */
ReturnKind getKind() { any() }
ReturnKind getKind() {
if this instanceof ExceptionReturnNode
then result instanceof ExceptionReturnKind
else result instanceof NormalReturnKind
}
}

/** A data flow node that represents the output of a call. */
Expand All @@ -524,13 +572,24 @@ module Private {
this.asExpr() instanceof MethodCall
or
this.(FlowSummaryNode).isOut(_)
or
this instanceof ExceptionOutNode
}

/** Gets the underlying call. */
DataFlowCall getCall() {
result.asCall() = this.asExpr()
or
this.(FlowSummaryNode).isOut(result)
or
result = this.(ExceptionOutNode).getCall()
}

/** Gets the kind of this returned value. */
ReturnKind getKind() {
if this instanceof ExceptionOutNode
then result instanceof ExceptionReturnKind
else result instanceof NormalReturnKind
}
}

Expand Down Expand Up @@ -597,6 +656,67 @@ module Private {
cn.isInstanceAccess() and result = cn.getEnclosingCallable().getDeclaringType()
}
}

/**
* A data flow node that carries an exception and tests if it is caught in a
* given catch clause.
*/
class CatchTypeTestNode extends Node, TCatchTypeTestNode {
override string toString() { result = this.getCatch().toString() }

override Location getLocation() { result = this.getCatch().getLocation() }

/** Gets the catch clause associated with this node. */
CatchClause getCatch() { this = TCatchTypeTestNode(result) }

Node getSuccessor(boolean match) {
match = true and
this.getCatch() = result.(CatchParameterNode).getCatch()
or
match = false and
exists(TryStmt try, int i, CatchClause cc |
cc = this.getCatch() and
cc = try.getCatchClause(i) and
// A catch-all does not allow for uncaught exceptions.
not cc.getACaughtType() instanceof TypeThrowable and
not cc.getACaughtType() instanceof TypeException
|
result.(CatchTypeTestNode).getCatch() = try.getCatchClause(i + 1)
or
not exists(try.getCatchClause(i + 1)) and
result.(UncaughtNode).getTry() = try
)
}
}

/**
* A data flow node that holds the value of a variable defined in a catch
* clause.
*/
class CatchParameterNode extends Node, TCatchParameterNode {
override string toString() { result = this.getVariable().toString() }

override Location getLocation() { result = this.getVariable().getLocation() }

/** Gets the catch clause associated with this node. */
CatchClause getCatch() { this = TCatchParameterNode(result) }

/** Gets the variable declaration associated with this node. */
LocalVariableDeclExpr getVariable() { result = this.getCatch().getVariable() }
}

/**
* A data flow node that carries an exception that is uncaught by a try-catch
* statement.
*/
class UncaughtNode extends Node, TUncaughtNode {
override string toString() { result = "Uncaught exception" }

override Location getLocation() { result = this.getTry().getLocation() }

/** Gets the try statement associated with this node. */
TryStmt getTry() { this = TUncaughtNode(result) }
}
}

private import Private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,28 @@ private import DataFlowNodes
private import codeql.dataflow.VariableCapture as VariableCapture
import DataFlowNodes::Private

private newtype TReturnKind = TNormalReturnKind()
private newtype TReturnKind =
TNormalReturnKind() or
TExceptionReturnKind()

/**
* A return kind. A return kind describes how a value can be returned
* from a callable. For Java, this is simply a method return.
* A return kind. A return kind describes how a value can be returned from a
* callable. For Java, this is either a normal method return or an exception
* being returned.
*/
class ReturnKind extends TReturnKind {
/** Gets a textual representation of this return kind. */
string toString() { result = "return" }
string toString() { none() }
}

/** A return kind indicating normal method return. */
class NormalReturnKind extends ReturnKind, TNormalReturnKind {
override string toString() { result = "return" }
}

/** A return kind indicating exceptional method return. */
class ExceptionReturnKind extends ReturnKind, TExceptionReturnKind {
override string toString() { result = "exception return" }
}

/**
Expand All @@ -34,7 +47,7 @@ class ReturnKind extends TReturnKind {
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result.getCall() = call and
kind = TNormalReturnKind()
result.getKind() = kind
}

/**
Expand Down Expand Up @@ -170,6 +183,8 @@ private CaptureFlow::ClosureNode asClosureNode(Node n) {
n.asExpr() = write.(VariableAssign).getSource()
or
n.asExpr() = write.(AssignOp)
or
n.(CatchParameterNode).getVariable() = write
)
}

Expand Down Expand Up @@ -203,6 +218,59 @@ predicate jumpStep(Node node1, Node node2) {
node2.(FlowSummaryNode).getSummaryNode())
}

module ExceptionFlow {
/**
* Holds if `try` has at least one catch clause and `body` is either the main
* body of the `try` or one of its resource declarations.
*/
predicate tryCatch(TryStmt try, Stmt body) {
exists(try.getACatchClause()) and
(
body = try.getBlock() or
body = try.getAResourceDecl()
)
}

/**
* Holds if `s2` is the enclosing statement of `s1` and `s1` is not directly
* wrapped in a try-catch.
*/
private predicate excStep(Stmt s1, Stmt s2) {
s1.getEnclosingStmt() = s2 and
not tryCatch(_, s1)
}

pragma[nomagic]
private DataFlowCallable excReturnGetCallable(ExceptionReturnNode n) {
result = nodeGetEnclosingCallable(n)
}

/** Holds if a thrown exception can flow locally from `node1` to `node2`. */
predicate localStep(Node node1, Node node2) {
node1.(ExceptionOutNode).getCall().(SummaryCall).getEnclosingCallable() =
excReturnGetCallable(node2)
or
exists(Stmt exc |
node1.asExpr() = exc.(ThrowStmt).getExpr() or
node1.(ExceptionOutNode).getCall().asCall().getEnclosingStmt() = exc or
node1.(UncaughtNode).getTry() = exc
|
exists(TryStmt try, Stmt body |
excStep+(exc, body) and
tryCatch(try, body) and
node2.(CatchTypeTestNode).getCatch() = try.getCatchClause(0)
)
or
exists(Callable callable |
excStep+(exc, callable.getBody()) and
excReturnGetCallable(node2).asCallable() = callable
)
)
or
node1.(CatchTypeTestNode).getSuccessor(_) = node2
}
}

/**
* Holds if `fa` is an access to an instance field that occurs as the
* destination of an assignment of the value `src`.
Expand Down Expand Up @@ -391,9 +459,9 @@ pragma[nomagic]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { erasedHaveIntersection(t1, t2) }

/** A node that performs a type cast. */
class CastNode extends ExprNode {
class CastNode extends Node {
CastNode() {
this.getExpr() instanceof CastingExpr
this.asExpr() instanceof CastingExpr
or
exists(SsaExplicitWrite upd |
upd.getDefiningExpr().(VariableAssign).getSource() =
Expand All @@ -403,6 +471,8 @@ class CastNode extends ExprNode {
] and
this.asExpr() = ssaGetAFirstUse(upd)
)
or
this instanceof CatchParameterNode
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ private predicate simpleLocalFlowStep0(Node node1, Node node2, string model) {
simpleAstFlowStep(node1.asExpr(), node2.asExpr())
or
captureValueStep(node1, node2)
or
ExceptionFlow::localStep(node1, node2)
) and
model = ""
or
Expand Down
Loading