Skip to content

Commit 3c69f8f

Browse files
committed
Java: Count second level scopes for fieldFlowBranchLimit.
1 parent 2f0987e commit 3c69f8f

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ module JavaDataFlow implements InputSig<Location> {
2020

2121
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
2222

23+
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
24+
2325
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
2426

2527
predicate viableImplInCallContext = Private::viableImplInCallContext/2;

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

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,80 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
581581

582582
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) }
583583

584-
class DataFlowSecondLevelScope = Unit;
584+
private predicate isTopLevel(Stmt s) {
585+
any(Callable c).getBody() = s
586+
or
587+
exists(BlockStmt b | s = b.getAStmt() and isTopLevel(b))
588+
}
589+
590+
private Stmt getAChainedBranch(IfStmt s) {
591+
result = s.getThen()
592+
or
593+
exists(Stmt elseBranch | s.getElse() = elseBranch |
594+
result = getAChainedBranch(elseBranch)
595+
or
596+
result = elseBranch and not elseBranch instanceof IfStmt
597+
)
598+
}
599+
600+
private newtype TDataFlowSecondLevelScope =
601+
TTopLevelIfBranch(Stmt s) {
602+
exists(IfStmt ifstmt | s = getAChainedBranch(ifstmt) and isTopLevel(ifstmt))
603+
} or
604+
TTopLevelSwitchCase(SwitchCase s) {
605+
exists(SwitchStmt switchstmt | s = switchstmt.getACase() and isTopLevel(switchstmt))
606+
}
607+
608+
private SwitchCase getPrecedingCase(Stmt s) {
609+
result = s
610+
or
611+
exists(SwitchStmt switch, int i |
612+
s = switch.getStmt(i) and
613+
not s instanceof SwitchCase and
614+
result = getPrecedingCase(switch.getStmt(i - 1))
615+
)
616+
}
617+
618+
/**
619+
* A second-level control-flow scope in a `switch` or a chained `if` statement.
620+
*
621+
* This is a `switch` case or a branch of a chained `if` statement, given that
622+
* the `switch` or `if` statement is top level, that is, it is not nested inside
623+
* other CFG constructs.
624+
*/
625+
class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
626+
/** Gets a textual representation of this element. */
627+
string toString() {
628+
exists(Stmt s | this = TTopLevelIfBranch(s) | result = s.toString())
629+
or
630+
exists(SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.toString())
631+
}
632+
633+
/**
634+
* Gets a statement directly contained in this scope. For an `if` branch, this
635+
* is the branch itself, and for a `switch case`, this is one the statements
636+
* of that case branch.
637+
*/
638+
private Stmt getAStmt() {
639+
exists(Stmt s | this = TTopLevelIfBranch(s) | result = s)
640+
or
641+
exists(SwitchCase s | this = TTopLevelSwitchCase(s) |
642+
result = s.getRuleStatement() or
643+
s = getPrecedingCase(result)
644+
)
645+
}
646+
647+
/** Gets a data-flow node nested within this scope. */
648+
Node getANode() { getRelatedExpr(result).getAnEnclosingStmt() = this.getAStmt() }
649+
}
650+
651+
private Expr getRelatedExpr(Node n) {
652+
n.asExpr() = result or
653+
n.(PostUpdateNode).getPreUpdateNode().asExpr() = result
654+
}
655+
656+
/** Gets the second-level scope containing the node `n`, if any. */
657+
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
585658

586659
/**
587660
* Holds if flow is allowed to pass from parameter `p` and back to itself as a

0 commit comments

Comments
 (0)