Skip to content

Commit b0ff1da

Browse files
committed
Java: Add support for data flow through thrown exceptions.
1 parent 56a5c78 commit b0ff1da

File tree

9 files changed

+381
-38
lines changed

9 files changed

+381
-38
lines changed

java/ql/lib/semmle/code/java/dataflow/FlowSummary.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java
66
private import internal.FlowSummaryImpl as Impl
77
private import internal.DataFlowUtil
8+
private import internal.DataFlowPrivate
89

910
class SummaryComponent = Impl::Public::SummaryComponent;
1011

@@ -28,7 +29,7 @@ module SummaryComponent {
2829
SummaryComponent mapValue() { result = content(any(MapValueContent c)) }
2930

3031
/** Gets a summary component that represents the return value of a call. */
31-
SummaryComponent return() { result = return(_) }
32+
SummaryComponent return() { result = return(any(NormalReturnKind n)) }
3233
}
3334

3435
class SummaryComponentStack = Impl::Public::SummaryComponentStack;

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ private module Cached {
5656
} or
5757
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
5858
TFieldValueNode(Field f) or
59-
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn)
59+
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn) or
60+
TExceptionOutNode(Call call) or
61+
TExceptionReturnNode(Callable callable) or
62+
TCatchTypeTestNode(CatchClause catch) or
63+
TCatchParameterNode(CatchClause catch) or
64+
TUncaughtNode(TryStmt try) { ExceptionFlow::tryCatch(try, _) }
6065

6166
cached
6267
newtype TContent =
@@ -133,6 +138,16 @@ module Public {
133138
result = this.(CaptureNode).getTypeImpl()
134139
or
135140
result = this.(FieldValueNode).getField().getType()
141+
or
142+
result instanceof TypeThrowable and this instanceof ExceptionOutNode
143+
or
144+
result instanceof TypeThrowable and this instanceof ExceptionReturnNode
145+
or
146+
result instanceof TypeThrowable and this instanceof CatchTypeTestNode
147+
or
148+
result = this.(CatchParameterNode).getVariable().getType()
149+
or
150+
result instanceof TypeThrowable and this instanceof UncaughtNode
136151
}
137152

138153
/** Gets the callable in which this node occurs. */
@@ -335,6 +350,27 @@ module Public {
335350
/** Holds if this is an access to an object's own instance. */
336351
predicate isOwnInstanceAccess() { this.getInstanceAccess().isOwnInstanceAccess() }
337352
}
353+
354+
/**
355+
* A node representing a thrown exception as the result of a call.
356+
*/
357+
class ExceptionOutNode extends Node, TExceptionOutNode {
358+
override string toString() { result = "Exception out: " + this.getCall().toString() }
359+
360+
override Location getLocation() { result = this.getCall().getLocation() }
361+
362+
/** Gets the associated call. */
363+
Call getCall() { this = TExceptionOutNode(result) }
364+
}
365+
366+
/**
367+
* A node representing a thrown exception being returned from a callable.
368+
*/
369+
class ExceptionReturnNode extends Node, TExceptionReturnNode {
370+
override string toString() { result = "Exception return" }
371+
372+
override Location getLocation() { result = this.getEnclosingCallable().getLocation() }
373+
}
338374
}
339375

340376
private import Public
@@ -378,7 +414,12 @@ module Private {
378414
result = nodeGetEnclosingCallable(n.(ImplicitPostUpdateNode).getPreUpdateNode()) or
379415
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
380416
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
381-
result.asFieldScope() = n.(FieldValueNode).getField()
417+
result.asFieldScope() = n.(FieldValueNode).getField() or
418+
result.asCallable() = n.(ExceptionOutNode).getCall().getEnclosingCallable() or
419+
n = TExceptionReturnNode(result.asCallable()) or
420+
result.asCallable() = n.(CatchTypeTestNode).getCatch().getEnclosingCallable() or
421+
result.asCallable() = n.(CatchParameterNode).getCatch().getEnclosingCallable() or
422+
result.asCallable() = n.(UncaughtNode).getTry().getEnclosingCallable()
382423
}
383424

384425
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
@@ -429,15 +470,23 @@ module Private {
429470
DataFlowCall getCall() { this.argumentOf(result, _) }
430471
}
431472

432-
/** A data flow node that occurs as the result of a `ReturnStmt`. */
473+
/**
474+
* A data flow node that occurs as the result of a `ReturnStmt` or an
475+
* exception being returned.
476+
*/
433477
class ReturnNode extends Node {
434478
ReturnNode() {
435479
exists(ReturnStmt ret | this.asExpr() = ret.getResult()) or
436-
this.(FlowSummaryNode).isReturn()
480+
this.(FlowSummaryNode).isReturn() or
481+
this instanceof ExceptionReturnNode
437482
}
438483

439484
/** Gets the kind of this returned value. */
440-
ReturnKind getKind() { any() }
485+
ReturnKind getKind() {
486+
if this instanceof ExceptionReturnNode
487+
then result instanceof ExceptionReturnKind
488+
else result instanceof NormalReturnKind
489+
}
441490
}
442491

443492
/** A data flow node that represents the output of a call. */
@@ -446,13 +495,24 @@ module Private {
446495
this.asExpr() instanceof MethodAccess
447496
or
448497
this.(FlowSummaryNode).isOut(_)
498+
or
499+
this instanceof ExceptionOutNode
449500
}
450501

451502
/** Gets the underlying call. */
452503
DataFlowCall getCall() {
453504
result.asCall() = this.asExpr()
454505
or
455506
this.(FlowSummaryNode).isOut(result)
507+
or
508+
result.asCall() = this.(ExceptionOutNode).getCall()
509+
}
510+
511+
/** Gets the kind of this returned value. */
512+
ReturnKind getKind() {
513+
if this instanceof ExceptionOutNode
514+
then result instanceof ExceptionReturnKind
515+
else result instanceof NormalReturnKind
456516
}
457517
}
458518

@@ -519,6 +579,66 @@ module Private {
519579
cn.isInstanceAccess() and result = cn.getEnclosingCallable().getDeclaringType()
520580
}
521581
}
582+
583+
/**
584+
* A data flow node that carries an exception and tests if it is caught in a
585+
* given catch clause.
586+
*/
587+
class CatchTypeTestNode extends Node, TCatchTypeTestNode {
588+
override string toString() { result = this.getCatch().toString() }
589+
590+
override Location getLocation() { result = this.getCatch().getLocation() }
591+
592+
/** Gets the catch clause associated with this node. */
593+
CatchClause getCatch() { this = TCatchTypeTestNode(result) }
594+
595+
Node getSuccessor(boolean match) {
596+
match = true and
597+
this.getCatch() = result.(CatchParameterNode).getCatch()
598+
or
599+
match = false and
600+
exists(TryStmt try, int i, CatchClause cc |
601+
cc = this.getCatch() and
602+
cc = try.getCatchClause(i) and
603+
// A catch-all does not allow for uncaught exceptions.
604+
not cc.getACaughtType() instanceof TypeThrowable
605+
|
606+
result.(CatchTypeTestNode).getCatch() = try.getCatchClause(i + 1)
607+
or
608+
not exists(try.getCatchClause(i + 1)) and
609+
result.(UncaughtNode).getTry() = try
610+
)
611+
}
612+
}
613+
614+
/**
615+
* A data flow node that holds the value of a variable defined in a catch
616+
* clause.
617+
*/
618+
class CatchParameterNode extends Node, TCatchParameterNode {
619+
override string toString() { result = this.getVariable().toString() }
620+
621+
override Location getLocation() { result = this.getVariable().getLocation() }
622+
623+
/** Gets the catch clause associated with this node. */
624+
CatchClause getCatch() { this = TCatchParameterNode(result) }
625+
626+
/** Gets the variable declaration associated with this node. */
627+
LocalVariableDeclExpr getVariable() { result = this.getCatch().getVariable() }
628+
}
629+
630+
/**
631+
* A data flow node that carries an exception that is uncaught by a try-catch
632+
* statement.
633+
*/
634+
class UncaughtNode extends Node, TUncaughtNode {
635+
override string toString() { result = "Uncaught exception" }
636+
637+
override Location getLocation() { result = this.getTry().getLocation() }
638+
639+
/** Gets the try statement associated with this node. */
640+
TryStmt getTry() { this = TUncaughtNode(result) }
641+
}
522642
}
523643

524644
private import Private

0 commit comments

Comments
 (0)