mirror of
https://github.com/github/codeql.git
synced 2026-04-17 21:14:02 +02:00
Merge branch 'main' into shared/locations-in-range-analysis
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Provides classes and predicates for computing expression-level intra-procedural control flow graphs.
|
||||
*
|
||||
* The only API exported by this library are the toplevel classes `ControlFlowNode`
|
||||
* The only API exported by this library are the toplevel classes `ControlFlow::Node`
|
||||
* and its subclass `ConditionNode`, which wrap the successor relation and the
|
||||
* concept of true- and false-successors of conditions. A cfg node may either be a
|
||||
* statement, an expression, or the enclosing callable, indicating that
|
||||
* statement, an expression, or an exit node for a callable, indicating that
|
||||
* execution of the callable terminates.
|
||||
*/
|
||||
|
||||
@@ -84,45 +84,148 @@ private import Completion
|
||||
private import controlflow.internal.Preconditions
|
||||
private import controlflow.internal.SwitchCases
|
||||
|
||||
/** A node in the expression-level control-flow graph. */
|
||||
class ControlFlowNode extends Top, @exprparent {
|
||||
/** Gets the statement containing this node, if any. */
|
||||
Stmt getEnclosingStmt() {
|
||||
result = this or
|
||||
result = this.(Expr).getEnclosingStmt()
|
||||
/** Provides the definition of control flow nodes. */
|
||||
module ControlFlow {
|
||||
private predicate hasControlFlow(Expr e) {
|
||||
not exists(ConstCase cc | e = cc.getValue(_)) and
|
||||
not e.getParent*() instanceof Annotation and
|
||||
not e instanceof TypeAccess and
|
||||
not e instanceof ArrayTypeAccess and
|
||||
not e instanceof UnionTypeAccess and
|
||||
not e instanceof IntersectionTypeAccess and
|
||||
not e instanceof WildcardTypeAccess and
|
||||
not exists(AssignExpr ae | ae.getDest() = e)
|
||||
}
|
||||
|
||||
/** Gets the immediately enclosing callable whose body contains this node. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = this or
|
||||
result = this.(Stmt).getEnclosingCallable() or
|
||||
result = this.(Expr).getEnclosingCallable()
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) { hasControlFlow(e) } or
|
||||
TStmtNode(Stmt s) or
|
||||
TExitNode(Callable c) { exists(c.getBody()) }
|
||||
|
||||
/** A node in the expression-level control-flow graph. */
|
||||
class Node extends TNode {
|
||||
/** Gets an immediate successor of this node. */
|
||||
Node getASuccessor() { result = succ(this) }
|
||||
|
||||
/** Gets an immediate predecessor of this node. */
|
||||
Node getAPredecessor() { this = succ(result) }
|
||||
|
||||
/** Gets an exception successor of this node. */
|
||||
Node getAnExceptionSuccessor() { result = succ(this, ThrowCompletion(_)) }
|
||||
|
||||
/** Gets a successor of this node that is neither an exception successor nor a jump (break, continue, return). */
|
||||
Node getANormalSuccessor() {
|
||||
result = succ(this, BooleanCompletion(_, _)) or
|
||||
result = succ(this, NormalCompletion())
|
||||
}
|
||||
|
||||
/** Gets the basic block that contains this node. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
|
||||
/** Gets the statement containing this node, if any. */
|
||||
Stmt getEnclosingStmt() { none() }
|
||||
|
||||
/** Gets the immediately enclosing callable whose body contains this node. */
|
||||
Callable getEnclosingCallable() { none() }
|
||||
|
||||
/** Gets the statement this `Node` corresponds to, if any. */
|
||||
Stmt asStmt() { this = TStmtNode(result) }
|
||||
|
||||
/** Gets the expression this `Node` corresponds to, if any. */
|
||||
Expr asExpr() { this = TExprNode(result) }
|
||||
|
||||
/** Gets the call this `Node` corresponds to, if any. */
|
||||
Call asCall() {
|
||||
result = this.asExpr() or
|
||||
result = this.asStmt()
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/**
|
||||
* Gets the most appropriate AST node for this control flow node, if any.
|
||||
*/
|
||||
ExprParent getAstNode() { none() }
|
||||
}
|
||||
|
||||
/** Gets an immediate successor of this node. */
|
||||
ControlFlowNode getASuccessor() { result = succ(this) }
|
||||
/** A control-flow node that represents the evaluation of an expression. */
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr e;
|
||||
|
||||
/** Gets an immediate predecessor of this node. */
|
||||
ControlFlowNode getAPredecessor() { this = succ(result) }
|
||||
ExprNode() { this = TExprNode(e) }
|
||||
|
||||
/** Gets an exception successor of this node. */
|
||||
ControlFlowNode getAnExceptionSuccessor() { result = succ(this, ThrowCompletion(_)) }
|
||||
override Stmt getEnclosingStmt() { result = e.getEnclosingStmt() }
|
||||
|
||||
/** Gets a successor of this node that is neither an exception successor nor a jump (break, continue, return). */
|
||||
ControlFlowNode getANormalSuccessor() {
|
||||
result = succ(this, BooleanCompletion(_, _)) or
|
||||
result = succ(this, NormalCompletion())
|
||||
override Callable getEnclosingCallable() { result = e.getEnclosingCallable() }
|
||||
|
||||
override ExprParent getAstNode() { result = e }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = e.toString() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
override Location getLocation() { result = e.getLocation() }
|
||||
}
|
||||
|
||||
/** Gets the basic block that contains this node. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
/** A control-flow node that represents a statement. */
|
||||
class StmtNode extends Node, TStmtNode {
|
||||
Stmt s;
|
||||
|
||||
StmtNode() { this = TStmtNode(s) }
|
||||
|
||||
override Stmt getEnclosingStmt() { result = s }
|
||||
|
||||
override Callable getEnclosingCallable() { result = s.getEnclosingCallable() }
|
||||
|
||||
override ExprParent getAstNode() { result = s }
|
||||
|
||||
override string toString() { result = s.toString() }
|
||||
|
||||
override Location getLocation() { result = s.getLocation() }
|
||||
}
|
||||
|
||||
/** A control flow node indicating the termination of a callable. */
|
||||
class ExitNode extends Node, TExitNode {
|
||||
Callable c;
|
||||
|
||||
ExitNode() { this = TExitNode(c) }
|
||||
|
||||
override Callable getEnclosingCallable() { result = c }
|
||||
|
||||
override ExprParent getAstNode() { result = c }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = "Exit" }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
override Location getLocation() { result = c.getLocation() }
|
||||
}
|
||||
}
|
||||
|
||||
class ControlFlowNode = ControlFlow::Node;
|
||||
|
||||
/** Gets the intra-procedural successor of `n`. */
|
||||
private ControlFlowNode succ(ControlFlowNode n) { result = succ(n, _) }
|
||||
|
||||
cached
|
||||
private module ControlFlowGraphImpl {
|
||||
private import ControlFlow
|
||||
|
||||
private class AstNode extends ExprParent {
|
||||
AstNode() { this instanceof Expr or this instanceof Stmt }
|
||||
|
||||
Stmt getEnclosingStmt() {
|
||||
result = this or
|
||||
result = this.(Expr).getEnclosingStmt()
|
||||
}
|
||||
|
||||
Node getCfgNode() { result.asExpr() = this or result.asStmt() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a label that applies to this statement.
|
||||
*/
|
||||
@@ -167,7 +270,7 @@ private module ControlFlowGraphImpl {
|
||||
* `ClassCastException` is expected, or because it is a Kotlin not-null check
|
||||
* and a `NullPointerException` is expected.
|
||||
*/
|
||||
private predicate mayThrow(ControlFlowNode n, ThrowableType t) {
|
||||
private predicate mayThrow(AstNode n, ThrowableType t) {
|
||||
t = n.(ThrowStmt).getThrownExceptionType()
|
||||
or
|
||||
exists(Call c | c = n |
|
||||
@@ -200,7 +303,7 @@ private module ControlFlowGraphImpl {
|
||||
* Bind `t` to an unchecked exception that may transfer control to a finally
|
||||
* block inside which `n` is nested.
|
||||
*/
|
||||
private predicate uncheckedExceptionFromFinally(ControlFlowNode n, ThrowableType t) {
|
||||
private predicate uncheckedExceptionFromFinally(AstNode n, ThrowableType t) {
|
||||
exists(TryStmt try |
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
@@ -214,7 +317,7 @@ private module ControlFlowGraphImpl {
|
||||
* Bind `t` to all unchecked exceptions that may be caught by some
|
||||
* `try-catch` inside which `n` is nested.
|
||||
*/
|
||||
private predicate uncheckedExceptionFromCatch(ControlFlowNode n, ThrowableType t) {
|
||||
private predicate uncheckedExceptionFromCatch(AstNode n, ThrowableType t) {
|
||||
exists(TryStmt try, UncheckedThrowableSuperType caught |
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
@@ -229,7 +332,7 @@ private module ControlFlowGraphImpl {
|
||||
* body or the resources (if any) of `try`.
|
||||
*/
|
||||
private ThrowableType thrownInBody(TryStmt try) {
|
||||
exists(ControlFlowNode n | mayThrow(n, result) |
|
||||
exists(AstNode n | mayThrow(n, result) |
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
)
|
||||
@@ -287,7 +390,7 @@ private module ControlFlowGraphImpl {
|
||||
* That is, contexts where the control-flow edges depend on `value` given that `b` ends
|
||||
* with a `booleanCompletion(value, _)`.
|
||||
*/
|
||||
private predicate inBooleanContext(ControlFlowNode b) {
|
||||
private predicate inBooleanContext(AstNode b) {
|
||||
exists(LogicExpr logexpr |
|
||||
logexpr.(BinaryExpr).getLeftOperand() = b
|
||||
or
|
||||
@@ -493,9 +596,7 @@ private module ControlFlowGraphImpl {
|
||||
* immediately before either falling through to execute successor statements or execute a rule body
|
||||
* if present. `completion` is the completion kind of the last operation.
|
||||
*/
|
||||
private predicate lastPatternCaseMatchingOp(
|
||||
PatternCase pc, ControlFlowNode last, Completion completion
|
||||
) {
|
||||
private predicate lastPatternCaseMatchingOp(PatternCase pc, Node last, Completion completion) {
|
||||
last(pc.getAPattern(), last, completion) and
|
||||
completion = NormalCompletion() and
|
||||
not exists(pc.getGuard())
|
||||
@@ -514,7 +615,7 @@ private module ControlFlowGraphImpl {
|
||||
* and `ThrowStmt`. CFG nodes without child nodes in the CFG that may complete
|
||||
* normally are also included.
|
||||
*/
|
||||
private class PostOrderNode extends ControlFlowNode {
|
||||
private class PostOrderNode extends AstNode {
|
||||
PostOrderNode() {
|
||||
// For VarAccess and ArrayAccess only read accesses (r-values) are included,
|
||||
// as write accesses aren't included in the CFG.
|
||||
@@ -576,7 +677,7 @@ private module ControlFlowGraphImpl {
|
||||
}
|
||||
|
||||
/** Gets child nodes in their order of execution. Indexing starts at either -1 or 0. */
|
||||
ControlFlowNode getChildNode(int index) {
|
||||
AstNode getChildNode(int index) {
|
||||
exists(ArrayAccess e | e = this |
|
||||
index = 0 and result = e.getArray()
|
||||
or
|
||||
@@ -649,7 +750,7 @@ private module ControlFlowGraphImpl {
|
||||
}
|
||||
|
||||
/** Gets the first child node, if any. */
|
||||
ControlFlowNode firstChild() {
|
||||
AstNode firstChild() {
|
||||
result = this.getChildNode(-1)
|
||||
or
|
||||
result = this.getChildNode(0) and not exists(this.getChildNode(-1))
|
||||
@@ -687,18 +788,18 @@ private module ControlFlowGraphImpl {
|
||||
/**
|
||||
* Determine the part of the AST node `n` that will be executed first.
|
||||
*/
|
||||
private ControlFlowNode first(ControlFlowNode n) {
|
||||
result = n and n instanceof LogicExpr
|
||||
private Node first(AstNode n) {
|
||||
result.asExpr() = n and n instanceof LogicExpr
|
||||
or
|
||||
result = n and n instanceof ConditionalExpr
|
||||
result.asExpr() = n and n instanceof ConditionalExpr
|
||||
or
|
||||
result = n and n instanceof WhenExpr
|
||||
result.asExpr() = n and n instanceof WhenExpr
|
||||
or
|
||||
result = n and n instanceof WhenBranch
|
||||
result.asStmt() = n and n instanceof WhenBranch
|
||||
or
|
||||
result = n and n instanceof StmtExpr
|
||||
result.asExpr() = n and n instanceof StmtExpr
|
||||
or
|
||||
result = n and n.(PostOrderNode).isLeafNode()
|
||||
result = n.getCfgNode() and n.(PostOrderNode).isLeafNode()
|
||||
or
|
||||
result = first(n.(PostOrderNode).firstChild())
|
||||
or
|
||||
@@ -706,12 +807,11 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
result = first(n.(SynchronizedStmt).getExpr())
|
||||
or
|
||||
result = n and
|
||||
n instanceof Stmt and
|
||||
result.asStmt() = n and
|
||||
not n instanceof PostOrderNode and
|
||||
not n instanceof SynchronizedStmt
|
||||
or
|
||||
result = n and n instanceof SwitchExpr
|
||||
result.asExpr() = n and n instanceof SwitchExpr
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -722,9 +822,7 @@ private module ControlFlowGraphImpl {
|
||||
* node in the `try` block that may not complete normally, or a node in
|
||||
* the `try` block that has no control flow successors inside the block.
|
||||
*/
|
||||
private predicate catchOrFinallyCompletion(
|
||||
TryStmt try, ControlFlowNode last, Completion completion
|
||||
) {
|
||||
private predicate catchOrFinallyCompletion(TryStmt try, Node last, Completion completion) {
|
||||
last(try.getBlock(), last, completion)
|
||||
or
|
||||
last(try.getAResource(), last, completion) and completion = ThrowCompletion(_)
|
||||
@@ -737,7 +835,7 @@ private module ControlFlowGraphImpl {
|
||||
* In other words, if `last` throws an exception it is possibly not caught by any
|
||||
* of the catch clauses.
|
||||
*/
|
||||
private predicate uncaught(TryStmt try, ControlFlowNode last, Completion completion) {
|
||||
private predicate uncaught(TryStmt try, Node last, Completion completion) {
|
||||
catchOrFinallyCompletion(try, last, completion) and
|
||||
(
|
||||
exists(ThrowableType thrown |
|
||||
@@ -767,12 +865,12 @@ private module ControlFlowGraphImpl {
|
||||
* This is similar to `uncaught`, but also includes final statements of `catch`
|
||||
* clauses.
|
||||
*/
|
||||
private predicate finallyPred(TryStmt try, ControlFlowNode last, Completion completion) {
|
||||
private predicate finallyPred(TryStmt try, Node last, Completion completion) {
|
||||
uncaught(try, last, completion) or
|
||||
last(try.getACatchClause(), last, completion)
|
||||
}
|
||||
|
||||
private predicate lastInFinally(TryStmt try, ControlFlowNode last) {
|
||||
private predicate lastInFinally(TryStmt try, Node last) {
|
||||
last(try.getFinally(), last, NormalCompletion())
|
||||
}
|
||||
|
||||
@@ -796,7 +894,7 @@ private module ControlFlowGraphImpl {
|
||||
* A `booleanCompletion` implies that `n` is an `Expr`. Any abnormal
|
||||
* completion besides `throwCompletion` implies that `n` is a `Stmt`.
|
||||
*/
|
||||
private predicate last(ControlFlowNode n, ControlFlowNode last, Completion completion) {
|
||||
private predicate last(AstNode n, Node last, Completion completion) {
|
||||
// Exceptions are propagated from any sub-expression.
|
||||
// As are any break, yield, continue, or return completions.
|
||||
exists(Expr e | e.getParent() = n |
|
||||
@@ -853,15 +951,17 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe | ioe.isPattern() and ioe = n |
|
||||
last = n and completion = basicBooleanCompletion(false)
|
||||
last.asExpr() = n and completion = basicBooleanCompletion(false)
|
||||
or
|
||||
last(ioe.getPattern(), last, NormalCompletion()) and completion = basicBooleanCompletion(true)
|
||||
)
|
||||
or
|
||||
// The last node of a node executed in post-order is the node itself.
|
||||
n.(PostOrderNode).mayCompleteNormally() and last = n and completion = NormalCompletion()
|
||||
exists(PostOrderNode p | p = n |
|
||||
p.mayCompleteNormally() and last = p.getCfgNode() and completion = NormalCompletion()
|
||||
)
|
||||
or
|
||||
last = n and completion = basicBooleanCompletion(n.(BooleanLiteral).getBooleanValue())
|
||||
last.asExpr() = n and completion = basicBooleanCompletion(n.(BooleanLiteral).getBooleanValue())
|
||||
or
|
||||
// The last statement in a block is any statement that does not complete normally,
|
||||
// or the last statement.
|
||||
@@ -997,7 +1097,7 @@ private module ControlFlowGraphImpl {
|
||||
// * On success of its guard test, if it is not a rule (boolean true)
|
||||
// (the latter two cases are accounted for by lastPatternCaseMatchingOp)
|
||||
exists(PatternCase pc | n = pc |
|
||||
last = pc and completion = basicBooleanCompletion(false)
|
||||
last.asStmt() = pc and completion = basicBooleanCompletion(false)
|
||||
or
|
||||
last(pc.getGuard(), last, completion) and
|
||||
completion = BooleanCompletion(false, _)
|
||||
@@ -1010,13 +1110,15 @@ private module ControlFlowGraphImpl {
|
||||
last(n.(SynchronizedStmt).getBlock(), last, completion)
|
||||
or
|
||||
// `return` statements give rise to a `Return` completion
|
||||
last = n.(ReturnStmt) and completion = ReturnCompletion()
|
||||
last.asStmt() = n.(ReturnStmt) and completion = ReturnCompletion()
|
||||
or
|
||||
// `throw` statements or throwing calls give rise to ` Throw` completion
|
||||
exists(ThrowableType tt | mayThrow(n, tt) | last = n and completion = ThrowCompletion(tt))
|
||||
exists(ThrowableType tt | mayThrow(n, tt) |
|
||||
last = n.getCfgNode() and completion = ThrowCompletion(tt)
|
||||
)
|
||||
or
|
||||
// `break` statements give rise to a `Break` completion
|
||||
exists(BreakStmt break | break = n and last = n |
|
||||
exists(BreakStmt break | break = n and last.asStmt() = n |
|
||||
completion = labelledBreakCompletion(MkLabel(break.getLabel()))
|
||||
or
|
||||
not exists(break.getLabel()) and completion = anonymousBreakCompletion()
|
||||
@@ -1031,7 +1133,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// `continue` statements give rise to a `Continue` completion
|
||||
exists(ContinueStmt cont | cont = n and last = n |
|
||||
exists(ContinueStmt cont | cont = n and last.asStmt() = n |
|
||||
completion = labelledContinueCompletion(MkLabel(cont.getLabel()))
|
||||
or
|
||||
not exists(cont.getLabel()) and completion = anonymousContinueCompletion()
|
||||
@@ -1067,7 +1169,7 @@ private module ControlFlowGraphImpl {
|
||||
// the last node of the condition of the last branch in the absence of an else-branch.
|
||||
exists(WhenExpr whenexpr | whenexpr = n |
|
||||
// If we have no branches then we are the last node
|
||||
last = n and
|
||||
last.asExpr() = n and
|
||||
completion = NormalCompletion() and
|
||||
not exists(whenexpr.getBranch(_))
|
||||
or
|
||||
@@ -1117,17 +1219,19 @@ private module ControlFlowGraphImpl {
|
||||
* execution finishes with the given completion.
|
||||
*/
|
||||
cached
|
||||
ControlFlowNode succ(ControlFlowNode n, Completion completion) {
|
||||
// Callables serve as their own exit nodes.
|
||||
exists(Callable c | last(c.getBody(), n, completion) | result = c)
|
||||
Node succ(Node n, Completion completion) {
|
||||
// After executing the callable body, the final node is the exit node.
|
||||
exists(Callable c | last(c.getBody(), n, completion) |
|
||||
result.(ExitNode).getEnclosingCallable() = c
|
||||
)
|
||||
or
|
||||
// Logic expressions and conditional expressions execute in AST pre-order.
|
||||
completion = NormalCompletion() and
|
||||
(
|
||||
result = first(n.(AndLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.(OrLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.(LogNotExpr).getExpr()) or
|
||||
result = first(n.(ConditionalExpr).getCondition())
|
||||
result = first(n.asExpr().(AndLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.asExpr().(OrLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.asExpr().(LogNotExpr).getExpr()) or
|
||||
result = first(n.asExpr().(ConditionalExpr).getCondition())
|
||||
)
|
||||
or
|
||||
// If a logic expression doesn't short-circuit then control flows from its left operand to its right.
|
||||
@@ -1151,9 +1255,11 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe | ioe.isPattern() |
|
||||
last(ioe.getExpr(), n, completion) and completion = NormalCompletion() and result = ioe
|
||||
last(ioe.getExpr(), n, completion) and
|
||||
completion = NormalCompletion() and
|
||||
result.asExpr() = ioe
|
||||
or
|
||||
n = ioe and
|
||||
n.asExpr() = ioe and
|
||||
result = first(ioe.getPattern()) and
|
||||
completion = basicBooleanCompletion(true)
|
||||
)
|
||||
@@ -1164,11 +1270,11 @@ private module ControlFlowGraphImpl {
|
||||
|
|
||||
result = first(p.getChildNode(i + 1))
|
||||
or
|
||||
not exists(p.getChildNode(i + 1)) and result = p
|
||||
not exists(p.getChildNode(i + 1)) and result = p.getCfgNode()
|
||||
)
|
||||
or
|
||||
// Statements within a block execute sequentially.
|
||||
result = first(n.(BlockStmt).getStmt(0)) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(BlockStmt).getStmt(0)) and completion = NormalCompletion()
|
||||
or
|
||||
exists(BlockStmt blk, int i |
|
||||
last(blk.getStmt(i), n, completion) and
|
||||
@@ -1178,7 +1284,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// Control flows to the corresponding branch depending on the boolean completion of the condition.
|
||||
exists(IfStmt s |
|
||||
n = s and result = first(s.getCondition()) and completion = NormalCompletion()
|
||||
n.asStmt() = s and result = first(s.getCondition()) and completion = NormalCompletion()
|
||||
or
|
||||
last(s.getCondition(), n, completion) and
|
||||
completion = BooleanCompletion(true, _) and
|
||||
@@ -1190,7 +1296,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// For statements:
|
||||
exists(ForStmt for, ControlFlowNode condentry |
|
||||
exists(ForStmt for, Node condentry |
|
||||
// Any part of the control flow that aims for the condition needs to hit either the condition...
|
||||
condentry = first(for.getCondition())
|
||||
or
|
||||
@@ -1198,10 +1304,10 @@ private module ControlFlowGraphImpl {
|
||||
not exists(for.getCondition()) and condentry = first(for.getStmt())
|
||||
|
|
||||
// From the entry point, which is the for statement itself, control goes to either the first init expression...
|
||||
n = for and result = first(for.getInit(0)) and completion = NormalCompletion()
|
||||
n.asStmt() = for and result = first(for.getInit(0)) and completion = NormalCompletion()
|
||||
or
|
||||
// ...or the condition if the for doesn't include init expressions.
|
||||
n = for and
|
||||
n.asStmt() = for and
|
||||
not exists(for.getAnInit()) and
|
||||
result = condentry and
|
||||
completion = NormalCompletion()
|
||||
@@ -1238,27 +1344,29 @@ private module ControlFlowGraphImpl {
|
||||
// Enhanced for statements:
|
||||
exists(EnhancedForStmt for |
|
||||
// First the expression gets evaluated...
|
||||
n = for and result = first(for.getExpr()) and completion = NormalCompletion()
|
||||
n.asStmt() = for and result = first(for.getExpr()) and completion = NormalCompletion()
|
||||
or
|
||||
// ...then the variable gets assigned...
|
||||
last(for.getExpr(), n, completion) and
|
||||
completion = NormalCompletion() and
|
||||
result = for.getVariable()
|
||||
result.asExpr() = for.getVariable()
|
||||
or
|
||||
// ...and then control goes to the body of the loop.
|
||||
n = for.getVariable() and result = first(for.getStmt()) and completion = NormalCompletion()
|
||||
n.asExpr() = for.getVariable() and
|
||||
result = first(for.getStmt()) and
|
||||
completion = NormalCompletion()
|
||||
or
|
||||
// Finally, the back edge of the loop goes to reassign the variable.
|
||||
last(for.getStmt(), n, completion) and
|
||||
continues(completion, for) and
|
||||
result = for.getVariable()
|
||||
result.asExpr() = for.getVariable()
|
||||
)
|
||||
or
|
||||
// While loops start at the condition...
|
||||
result = first(n.(WhileStmt).getCondition()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(WhileStmt).getCondition()) and completion = NormalCompletion()
|
||||
or
|
||||
// ...and do-while loops start at the body.
|
||||
result = first(n.(DoStmt).getStmt()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(DoStmt).getStmt()) and completion = NormalCompletion()
|
||||
or
|
||||
exists(LoopStmt loop | loop instanceof WhileStmt or loop instanceof DoStmt |
|
||||
// Control goes from the condition via a true-completion to the body...
|
||||
@@ -1282,7 +1390,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// After the last resource declaration, control transfers to the body.
|
||||
exists(TryStmt try | n = try and completion = NormalCompletion() |
|
||||
exists(TryStmt try | n.asStmt() = try and completion = NormalCompletion() |
|
||||
result = first(try.getResource(0))
|
||||
or
|
||||
not exists(try.getAResource()) and result = first(try.getBlock())
|
||||
@@ -1310,7 +1418,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// Catch clauses first assign their variable and then execute their block
|
||||
exists(CatchClause cc | completion = NormalCompletion() |
|
||||
n = cc and result = first(cc.getVariable())
|
||||
n.asStmt() = cc and result = first(cc.getVariable())
|
||||
or
|
||||
last(cc.getVariable(), n, completion) and result = first(cc.getBlock())
|
||||
)
|
||||
@@ -1321,7 +1429,9 @@ private module ControlFlowGraphImpl {
|
||||
switchExpr = switch.(SwitchStmt).getExpr() or switchExpr = switch.(SwitchExpr).getExpr()
|
||||
|
|
||||
// From the entry point control is transferred first to the expression...
|
||||
n = switch and result = first(switchExpr) and completion = NormalCompletion()
|
||||
n.getAstNode() = switch and
|
||||
result = first(switchExpr) and
|
||||
completion = NormalCompletion()
|
||||
or
|
||||
// ...and then to any case up to and including the first pattern case, if any.
|
||||
last(switchExpr, n, completion) and
|
||||
@@ -1345,7 +1455,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// A pattern case that completes boolean false (type test or guard failure) continues to consider other cases:
|
||||
exists(PatternCase case | completion = BooleanCompletion(false, _) |
|
||||
last(case, n, completion) and result = getASuccessorSwitchCase(case, switch)
|
||||
last(case, n, completion) and result.asStmt() = getASuccessorSwitchCase(case, switch)
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -1358,7 +1468,7 @@ private module ControlFlowGraphImpl {
|
||||
// * Variable declarations -normal-> rule execution (when there is no guard)
|
||||
// * Guard success -true-> rule execution
|
||||
exists(PatternCase pc |
|
||||
n = pc and
|
||||
n.asStmt() = pc and
|
||||
completion = basicBooleanCompletion(true) and
|
||||
result = first(pc.getAPattern())
|
||||
or
|
||||
@@ -1375,7 +1485,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// Non-pattern cases have an internal edge leading to their rule body if any when the case matches.
|
||||
exists(SwitchCase case | n = case |
|
||||
exists(SwitchCase case | n.asStmt() = case |
|
||||
not case instanceof PatternCase and
|
||||
completion = NormalCompletion() and
|
||||
(
|
||||
@@ -1387,32 +1497,32 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// Yield
|
||||
exists(YieldStmt yield | completion = NormalCompletion() |
|
||||
n = yield and result = first(yield.getValue())
|
||||
n.asStmt() = yield and result = first(yield.getValue())
|
||||
)
|
||||
or
|
||||
// Synchronized statements execute their expression _before_ synchronization, so the CFG reflects that.
|
||||
exists(SynchronizedStmt synch | completion = NormalCompletion() |
|
||||
last(synch.getExpr(), n, completion) and result = synch
|
||||
last(synch.getExpr(), n, completion) and result.asStmt() = synch
|
||||
or
|
||||
n = synch and result = first(synch.getBlock())
|
||||
n.asStmt() = synch and result = first(synch.getBlock())
|
||||
)
|
||||
or
|
||||
result = first(n.(ExprStmt).getExpr()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(ExprStmt).getExpr()) and completion = NormalCompletion()
|
||||
or
|
||||
result = first(n.(StmtExpr).getStmt()) and completion = NormalCompletion()
|
||||
result = first(n.asExpr().(StmtExpr).getStmt()) and completion = NormalCompletion()
|
||||
or
|
||||
result = first(n.(LabeledStmt).getStmt()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(LabeledStmt).getStmt()) and completion = NormalCompletion()
|
||||
or
|
||||
// Variable declarations in a variable declaration statement are executed sequentially.
|
||||
exists(LocalVariableDeclStmt s | completion = NormalCompletion() |
|
||||
n = s and result = first(s.getVariable(1))
|
||||
n.asStmt() = s and result = first(s.getVariable(1))
|
||||
or
|
||||
exists(int i | last(s.getVariable(i), n, completion) and result = first(s.getVariable(i + 1)))
|
||||
)
|
||||
or
|
||||
// When expressions:
|
||||
exists(WhenExpr whenexpr |
|
||||
n = whenexpr and
|
||||
n.asExpr() = whenexpr and
|
||||
result = first(whenexpr.getBranch(0)) and
|
||||
completion = NormalCompletion()
|
||||
or
|
||||
@@ -1425,7 +1535,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// When branches:
|
||||
exists(WhenBranch whenbranch |
|
||||
n = whenbranch and
|
||||
n.asStmt() = whenbranch and
|
||||
completion = NormalCompletion() and
|
||||
result = first(whenbranch.getCondition())
|
||||
or
|
||||
@@ -1463,7 +1573,7 @@ private module ControlFlowGraphImpl {
|
||||
* predicate `finallyPred`, since their completion is resumed after normal
|
||||
* completion of the `finally`.
|
||||
*/
|
||||
private Completion resumption(ControlFlowNode n) {
|
||||
private Completion resumption(Node n) {
|
||||
exists(TryStmt try | lastInFinally(try, n) and finallyPred(try, _, result))
|
||||
or
|
||||
not lastInFinally(_, n) and result = NormalCompletion()
|
||||
@@ -1474,9 +1584,7 @@ private module ControlFlowGraphImpl {
|
||||
*
|
||||
* That is, the `booleanCompletion` is the label of the edge in the CFG.
|
||||
*/
|
||||
private ControlFlowNode mainBranchSucc(ControlFlowNode n, boolean b) {
|
||||
result = succ(n, BooleanCompletion(_, b))
|
||||
}
|
||||
private Node mainBranchSucc(Node n, boolean b) { result = succ(n, BooleanCompletion(_, b)) }
|
||||
|
||||
/**
|
||||
* A true- or false-successor that is not tagged with a `booleanCompletion`.
|
||||
@@ -1487,8 +1595,8 @@ private module ControlFlowGraphImpl {
|
||||
* In the latter case, when `n` occurs as the last node in a finally block, there might be
|
||||
* multiple different such successors.
|
||||
*/
|
||||
private ControlFlowNode otherBranchSucc(ControlFlowNode n, boolean b) {
|
||||
exists(ControlFlowNode main | main = mainBranchSucc(n, b.booleanNot()) |
|
||||
private Node otherBranchSucc(Node n, boolean b) {
|
||||
exists(Node main | main = mainBranchSucc(n, b.booleanNot()) |
|
||||
result = succ(n, resumption(n)) and
|
||||
not result = main and
|
||||
(b = true or b = false)
|
||||
@@ -1497,7 +1605,7 @@ private module ControlFlowGraphImpl {
|
||||
|
||||
/** Gets a true- or false-successor of `n`. */
|
||||
cached
|
||||
ControlFlowNode branchSuccessor(ControlFlowNode n, boolean branch) {
|
||||
Node branchSuccessor(Node n, boolean branch) {
|
||||
result = mainBranchSucc(n, branch) or
|
||||
result = otherBranchSucc(n, branch)
|
||||
}
|
||||
@@ -1506,18 +1614,18 @@ private module ControlFlowGraphImpl {
|
||||
private import ControlFlowGraphImpl
|
||||
|
||||
/** A control-flow node that branches based on a condition. */
|
||||
class ConditionNode extends ControlFlowNode {
|
||||
class ConditionNode extends ControlFlow::Node {
|
||||
ConditionNode() { exists(branchSuccessor(this, _)) }
|
||||
|
||||
/** Gets a true- or false-successor of the `ConditionNode`. */
|
||||
ControlFlowNode getABranchSuccessor(boolean branch) { result = branchSuccessor(this, branch) }
|
||||
ControlFlow::Node getABranchSuccessor(boolean branch) { result = branchSuccessor(this, branch) }
|
||||
|
||||
/** Gets a true-successor of the `ConditionNode`. */
|
||||
ControlFlowNode getATrueSuccessor() { result = this.getABranchSuccessor(true) }
|
||||
ControlFlow::Node getATrueSuccessor() { result = this.getABranchSuccessor(true) }
|
||||
|
||||
/** Gets a false-successor of the `ConditionNode`. */
|
||||
ControlFlowNode getAFalseSuccessor() { result = this.getABranchSuccessor(false) }
|
||||
ControlFlow::Node getAFalseSuccessor() { result = this.getABranchSuccessor(false) }
|
||||
|
||||
/** Gets the condition of this `ConditionNode`. This is equal to the node itself. */
|
||||
ExprParent getCondition() { result = this }
|
||||
/** Gets the condition of this `ConditionNode`. */
|
||||
ExprParent getCondition() { result = this.asExpr() or result = this.asStmt() }
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ class Expr extends ExprParent, @expr {
|
||||
Expr getAChildExpr() { exprs(result, _, _, this, _) }
|
||||
|
||||
/** Gets the basic block in which this expression occurs, if any. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
BasicBlock getBasicBlock() { result.getANode().asExpr() = this }
|
||||
|
||||
/** Gets the `ControlFlowNode` corresponding to this expression. */
|
||||
ControlFlowNode getControlFlowNode() { result = this }
|
||||
ControlFlowNode getControlFlowNode() { result.asExpr() = this }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
string getHalsteadID() { result = this.toString() }
|
||||
|
||||
@@ -45,10 +45,10 @@ class Stmt extends StmtParent, ExprParent, @stmt {
|
||||
Stmt getAChild() { result.getParent() = this }
|
||||
|
||||
/** Gets the basic block in which this statement occurs. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
BasicBlock getBasicBlock() { result.getANode().asStmt() = this }
|
||||
|
||||
/** Gets the `ControlFlowNode` corresponding to this statement. */
|
||||
ControlFlowNode getControlFlowNode() { result = this }
|
||||
ControlFlowNode getControlFlowNode() { result.asStmt() = this }
|
||||
|
||||
/** Cast this statement to a class that provides access to metrics information. */
|
||||
MetricStmt getMetrics() { result = this }
|
||||
|
||||
@@ -66,3 +66,8 @@ class BasicBlock extends ControlFlowNode {
|
||||
/** Holds if this basic block post-dominates `node`. (This is reflexive.) */
|
||||
predicate bbPostDominates(BasicBlock node) { bbPostDominates(this, node) }
|
||||
}
|
||||
|
||||
/** A basic block that ends in an exit node. */
|
||||
class ExitBlock extends BasicBlock {
|
||||
ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode }
|
||||
}
|
||||
|
||||
@@ -9,13 +9,15 @@ import java
|
||||
*/
|
||||
|
||||
/** Entry points for control-flow. */
|
||||
private predicate flowEntry(Stmt entry) {
|
||||
exists(Callable c | entry = c.getBody())
|
||||
or
|
||||
// This disjunct is technically superfluous, but safeguards against extractor problems.
|
||||
entry instanceof BlockStmt and
|
||||
not exists(entry.getEnclosingCallable()) and
|
||||
not entry.getParent() instanceof Stmt
|
||||
private predicate flowEntry(BasicBlock entry) {
|
||||
exists(Stmt entrystmt | entrystmt = entry.getFirstNode().asStmt() |
|
||||
exists(Callable c | entrystmt = c.getBody())
|
||||
or
|
||||
// This disjunct is technically superfluous, but safeguards against extractor problems.
|
||||
entrystmt instanceof BlockStmt and
|
||||
not exists(entry.getEnclosingCallable()) and
|
||||
not entrystmt.getParent() instanceof Stmt
|
||||
)
|
||||
}
|
||||
|
||||
/** The successor relation for basic blocks. */
|
||||
@@ -31,11 +33,8 @@ predicate hasDominanceInformation(BasicBlock bb) {
|
||||
exists(BasicBlock entry | flowEntry(entry) and bbSucc*(entry, bb))
|
||||
}
|
||||
|
||||
/** Exit points for control-flow. */
|
||||
private predicate flowExit(Callable exit) { exists(ControlFlowNode s | s.getASuccessor() = exit) }
|
||||
|
||||
/** Exit points for basic-block control-flow. */
|
||||
private predicate bbSink(BasicBlock exit) { flowExit(exit.getLastNode()) }
|
||||
private predicate bbSink(BasicBlock exit) { exit.getLastNode() instanceof ControlFlow::ExitNode }
|
||||
|
||||
/** Reversed `bbSucc`. */
|
||||
private predicate bbPred(BasicBlock post, BasicBlock pre) { post = pre.getABBSuccessor() }
|
||||
|
||||
@@ -113,7 +113,7 @@ private PatternCase getClosestPrecedingPatternCase(SwitchCase case) {
|
||||
private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pred) {
|
||||
pred = sc.getControlFlowNode().getAPredecessor() and
|
||||
(
|
||||
pred.(Expr).getParent*() = sc.getSelectorExpr()
|
||||
pred.asExpr().getParent*() = sc.getSelectorExpr()
|
||||
or
|
||||
// Ambiguous: in the case of `case String _ when x: case "SomeConstant":`, the guard `x`
|
||||
// passing edge will fall through into the constant case, and the guard failing edge
|
||||
@@ -122,7 +122,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre
|
||||
exists(PatternCase previousPatternCase |
|
||||
previousPatternCase = getClosestPrecedingPatternCase(sc)
|
||||
|
|
||||
pred.(Expr).getParent*() = previousPatternCase.getGuard() and
|
||||
pred.asExpr().getParent*() = previousPatternCase.getGuard() and
|
||||
// Check there is any statement in between the previous pattern case and this one,
|
||||
// or the case is a rule, so there is no chance of a fall-through.
|
||||
(
|
||||
@@ -133,7 +133,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre
|
||||
or
|
||||
// Unambigious: on the test-passing edge there must be at least one intervening
|
||||
// declaration node, including anonymous `_` declarations.
|
||||
pred = getClosestPrecedingPatternCase(sc)
|
||||
pred.asStmt() = getClosestPrecedingPatternCase(sc)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ abstract class ActionConfiguration extends string {
|
||||
private BasicBlock actionBlock(ActionConfiguration conf) {
|
||||
exists(ControlFlowNode node | result = node.getBasicBlock() |
|
||||
conf.isAction(node) or
|
||||
callAlwaysPerformsAction(node, conf)
|
||||
callAlwaysPerformsAction(node.asCall(), conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,17 +45,17 @@ private predicate callAlwaysPerformsAction(Call call, ActionConfiguration conf)
|
||||
|
||||
/** Holds if an action dominates the exit of the callable. */
|
||||
private predicate actionDominatesExit(Callable callable, ActionConfiguration conf) {
|
||||
exists(BasicBlock exit |
|
||||
exit.getLastNode() = callable and
|
||||
exists(ExitBlock exit |
|
||||
exit.getEnclosingCallable() = callable and
|
||||
actionBlock(conf).bbDominates(exit)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a `BasicBlock` that contains an action that does not dominate the exit. */
|
||||
private BasicBlock nonDominatingActionBlock(ActionConfiguration conf) {
|
||||
exists(BasicBlock exit |
|
||||
exists(ExitBlock exit |
|
||||
result = actionBlock(conf) and
|
||||
exit.getLastNode() = result.getEnclosingCallable() and
|
||||
exit.getEnclosingCallable() = result.getEnclosingCallable() and
|
||||
not result.bbDominates(exit)
|
||||
)
|
||||
}
|
||||
@@ -80,8 +80,8 @@ private predicate postActionBlock(BasicBlock bb, ActionConfiguration conf) {
|
||||
private predicate callableAlwaysPerformsAction(Callable callable, ActionConfiguration conf) {
|
||||
actionDominatesExit(callable, conf)
|
||||
or
|
||||
exists(BasicBlock exit |
|
||||
exit.getLastNode() = callable and
|
||||
exists(ExitBlock exit |
|
||||
exit.getEnclosingCallable() = callable and
|
||||
postActionBlock(exit, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,23 +207,23 @@ class UnreachableBasicBlock extends BasicBlock {
|
||||
conditionBlock.controls(this, constant.booleanNot())
|
||||
)
|
||||
or
|
||||
// This block is not reachable in the CFG, and is not a callable, a body of a callable, an
|
||||
// expression in an annotation, an expression in an assert statement, or a catch clause.
|
||||
// This block is not reachable in the CFG, and is not the entrypoint in a callable, an
|
||||
// expression in an assert statement, or a catch clause.
|
||||
forall(BasicBlock bb | bb = this.getABBPredecessor() | bb instanceof UnreachableBasicBlock) and
|
||||
not exists(Callable c | c.getBody() = this) and
|
||||
not this instanceof Callable and
|
||||
not exists(Annotation a | a.getAChildExpr*() = this) and
|
||||
not this.(Expr).getEnclosingStmt() instanceof AssertStmt and
|
||||
not this instanceof CatchClause
|
||||
not exists(Callable c | c.getBody().getControlFlowNode() = this.getFirstNode()) and
|
||||
not this.getFirstNode().asExpr().getEnclosingStmt() instanceof AssertStmt and
|
||||
not this.getFirstNode().asStmt() instanceof CatchClause
|
||||
or
|
||||
// Switch statements with a constant comparison expression may have unreachable cases.
|
||||
exists(ConstSwitchStmt constSwitchStmt, BasicBlock failingCaseBlock |
|
||||
failingCaseBlock = constSwitchStmt.getAFailingCase().getBasicBlock()
|
||||
|
|
||||
exists(ConstSwitchStmt constSwitchStmt, BasicBlock unreachableCaseBlock |
|
||||
// Not accessible from the switch expression
|
||||
unreachableCaseBlock = constSwitchStmt.getAFailingCase().getBasicBlock() and
|
||||
// Not accessible from the successful case
|
||||
not constSwitchStmt.getMatchingCase().getBasicBlock().getABBSuccessor*() = failingCaseBlock and
|
||||
// Blocks dominated by the failing case block are unreachable
|
||||
constSwitchStmt.getAFailingCase().getBasicBlock().bbDominates(this)
|
||||
not constSwitchStmt.getMatchingCase().getBasicBlock().getABBSuccessor*() =
|
||||
unreachableCaseBlock
|
||||
|
|
||||
// Blocks dominated by an unreachable case block are unreachable
|
||||
unreachableCaseBlock.bbDominates(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@ module DataFlow {
|
||||
private import semmle.code.java.dataflow.internal.DataFlowImplSpecific
|
||||
private import codeql.dataflow.DataFlow
|
||||
import DataFlowMake<Location, JavaDataFlow>
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl1
|
||||
import Public
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
module DataFlow2 {
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl2
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
module DataFlow3 {
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl3
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
module DataFlow4 {
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl4
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
module DataFlow5 {
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl5
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
module DataFlow6 {
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl6
|
||||
}
|
||||
@@ -28,6 +28,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.ThreadLocal
|
||||
private import semmle.code.java.frameworks.ratpack.RatpackExec
|
||||
private import semmle.code.java.frameworks.stapler.Stapler
|
||||
private import semmle.code.java.security.ListOfConstantsSanitizer
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,3 +190,8 @@ private class NumberTaintPreservingCallable extends TaintPreservingCallable {
|
||||
* map-key and map-value content, so that e.g. a tainted `Map` is assumed to have tainted keys and values.
|
||||
*/
|
||||
abstract class TaintInheritingContent extends DataFlow::Content { }
|
||||
|
||||
/**
|
||||
* A sanitizer in all global taint flow configurations but not in local taint.
|
||||
*/
|
||||
abstract class DefaultTaintSanitizer extends DataFlow::Node { }
|
||||
|
||||
@@ -227,12 +227,14 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
/** Gets the control flow node associated with this instance access. */
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(ExprParent e | e = this.getAssociatedExprOrStmt() |
|
||||
e instanceof Call and result = e
|
||||
result.asCall() = e
|
||||
or
|
||||
e instanceof InstanceAccess and result = e
|
||||
e.(InstanceAccess).getControlFlowNode() = result
|
||||
or
|
||||
exists(FieldAccess fa | fa = e |
|
||||
if fa instanceof VarRead then fa = result else result.(AssignExpr).getDest() = fa
|
||||
if fa instanceof VarRead
|
||||
then fa.getControlFlowNode() = result
|
||||
else result.asExpr().(AssignExpr).getDest() = fa
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -130,8 +130,8 @@ predicate dereference(Expr e) {
|
||||
* The `VarAccess` is included for nicer error reporting.
|
||||
*/
|
||||
private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
dereference(result) and
|
||||
result = sameValue(v, va)
|
||||
dereference(result.asExpr()) and
|
||||
result.asExpr() = sameValue(v, va)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,16 +141,16 @@ private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
private ControlFlowNode ensureNotNull(SsaVariable v) {
|
||||
result = varDereference(v, _)
|
||||
or
|
||||
result.(AssertStmt).getExpr() = nullGuard(v, true, false)
|
||||
result.asStmt().(AssertStmt).getExpr() = nullGuard(v, true, false)
|
||||
or
|
||||
exists(AssertTrueMethod m | result = m.getACheck(nullGuard(v, true, false)))
|
||||
exists(AssertTrueMethod m | result.asCall() = m.getACheck(nullGuard(v, true, false)))
|
||||
or
|
||||
exists(AssertFalseMethod m | result = m.getACheck(nullGuard(v, false, false)))
|
||||
exists(AssertFalseMethod m | result.asCall() = m.getACheck(nullGuard(v, false, false)))
|
||||
or
|
||||
exists(AssertNotNullMethod m | result = m.getACheck(v.getAUse()))
|
||||
exists(AssertNotNullMethod m | result.asCall() = m.getACheck(v.getAUse()))
|
||||
or
|
||||
exists(AssertThatMethod m, MethodCall ma |
|
||||
result = m.getACheck(v.getAUse()) and ma.getControlFlowNode() = result
|
||||
result.asCall() = m.getACheck(v.getAUse()) and ma.getControlFlowNode() = result
|
||||
|
|
||||
ma.getAnArgument().(MethodCall).getMethod().getName() = "notNullValue"
|
||||
)
|
||||
@@ -279,10 +279,10 @@ private predicate enhancedForEarlyExit(EnhancedForStmt for, ControlFlowNode n1,
|
||||
exists(Expr forExpr |
|
||||
n1.getANormalSuccessor() = n2 and
|
||||
for.getExpr() = forExpr and
|
||||
forExpr.getAChildExpr*() = n1 and
|
||||
not forExpr.getAChildExpr*() = n2 and
|
||||
n1.getANormalSuccessor() = for.getVariable() and
|
||||
not n2 = for.getVariable()
|
||||
forExpr.getAChildExpr*() = n1.asExpr() and
|
||||
not forExpr.getAChildExpr*() = n2.asExpr() and
|
||||
n1.getANormalSuccessor().asExpr() = for.getVariable() and
|
||||
not n2.asExpr() = for.getVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ private predicate nullVarStep(
|
||||
not impossibleEdge(mid, bb) and
|
||||
not exists(boolean branch | nullGuard(midssa, branch, false).hasBranchEdge(mid, bb, branch)) and
|
||||
not (leavingFinally(mid, bb, true) and midstoredcompletion = true) and
|
||||
if bb.getFirstNode() = any(TryStmt try | | try.getFinally())
|
||||
if bb.getFirstNode().asStmt() = any(TryStmt try | | try.getFinally())
|
||||
then
|
||||
if bb.getFirstNode() = mid.getLastNode().getANormalSuccessor()
|
||||
then storedcompletion = false
|
||||
|
||||
@@ -211,9 +211,11 @@ module Sem implements Semantic<Location> {
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getABBSuccessor() }
|
||||
|
||||
private predicate id(BasicBlock x, BasicBlock y) { x = y }
|
||||
private predicate id(ExprParent x, ExprParent y) { x = y }
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
private predicate idOfAst(ExprParent x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) { idOfAst(x.getAstNode(), y) }
|
||||
|
||||
int getBlockId1(BasicBlock bb) { idOf(bb, result) }
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ private module SsaImpl {
|
||||
/** Holds if `n` must update the locally tracked variable `v`. */
|
||||
cached
|
||||
predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
|
||||
exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and
|
||||
b.getNode(i) = n and
|
||||
hasDominanceInformation(b)
|
||||
or
|
||||
@@ -237,8 +237,8 @@ private module SsaImpl {
|
||||
|
||||
/** Gets the definition point of a nested class in the parent scope. */
|
||||
private ControlFlowNode parentDef(NestedClass nc) {
|
||||
nc.(AnonymousClass).getClassInstanceExpr() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt() = result
|
||||
nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt().getControlFlowNode() = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +276,7 @@ private module SsaImpl {
|
||||
|
||||
/** Holds if `VarAccess` `use` of `v` occurs in `b` at index `i`. */
|
||||
private predicate variableUse(TrackedVar v, VarRead use, BasicBlock b, int i) {
|
||||
v.getAnAccess() = use and b.getNode(i) = use
|
||||
v.getAnAccess() = use and b.getNode(i) = use.getControlFlowNode()
|
||||
}
|
||||
|
||||
/** Holds if the value of `v` is captured in `b` at index `i`. */
|
||||
@@ -423,7 +423,7 @@ private module SsaImpl {
|
||||
* `f` has an update somewhere.
|
||||
*/
|
||||
private predicate updateCandidate(TrackedField f, Call call, BasicBlock b, int i) {
|
||||
b.getNode(i) = call and
|
||||
b.getNode(i).asCall() = call and
|
||||
call.getEnclosingCallable() = f.getEnclosingCallable() and
|
||||
relevantFieldUpdate(_, f.getField(), _)
|
||||
}
|
||||
@@ -550,7 +550,7 @@ private module SsaImpl {
|
||||
/** Holds if `n` might update the locally tracked variable `v`. */
|
||||
cached
|
||||
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(Call c | c = n | updatesNamedField(c, v, _)) and
|
||||
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
|
||||
b.getNode(i) = n and
|
||||
hasDominanceInformation(b)
|
||||
or
|
||||
@@ -574,12 +574,16 @@ private module SsaImpl {
|
||||
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
|
||||
cached
|
||||
predicate hasEntryDef(TrackedVar v, BasicBlock b) {
|
||||
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
|
||||
exists(LocalScopeVariable l, Callable c |
|
||||
v = TLocalVar(c, l) and c.getBody().getControlFlowNode() = b
|
||||
|
|
||||
l instanceof Parameter or
|
||||
l.getCallable() != c
|
||||
)
|
||||
or
|
||||
v instanceof SsaSourceField and v.getEnclosingCallable().getBody() = b and liveAtEntry(v, b)
|
||||
v instanceof SsaSourceField and
|
||||
v.getEnclosingCallable().getBody().getControlFlowNode() = b and
|
||||
liveAtEntry(v, b)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -882,7 +886,7 @@ private newtype TSsaVariable =
|
||||
} or
|
||||
TSsaEntryDef(TrackedVar v, BasicBlock b) { hasEntryDef(v, b) } or
|
||||
TSsaUntracked(SsaSourceField nf, ControlFlowNode n) {
|
||||
n = nf.getAnAccess().(FieldRead) and not trackField(nf)
|
||||
n = nf.getAnAccess().(FieldRead).getControlFlowNode() and not trackField(nf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -940,7 +944,7 @@ class SsaVariable extends TSsaVariable {
|
||||
/** Gets an access of this SSA variable. */
|
||||
VarRead getAUse() {
|
||||
ssaDefReachesUse(_, this, result) or
|
||||
this = TSsaUntracked(_, result)
|
||||
this = TSsaUntracked(_, result.getControlFlowNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -954,7 +958,7 @@ class SsaVariable extends TSsaVariable {
|
||||
*/
|
||||
VarRead getAFirstUse() {
|
||||
firstUse(this, result) or
|
||||
this = TSsaUntracked(_, result)
|
||||
this = TSsaUntracked(_, result.getControlFlowNode())
|
||||
}
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
@@ -990,7 +994,7 @@ class SsaUpdate extends SsaVariable {
|
||||
class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate {
|
||||
SsaExplicitUpdate() {
|
||||
exists(VariableUpdate upd |
|
||||
upd = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
upd.getControlFlowNode() = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -998,7 +1002,8 @@ class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate {
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
VariableUpdate getDefiningExpr() {
|
||||
result = this.getCfgNode() and getDestVar(result) = this.getSourceVariable()
|
||||
result.getControlFlowNode() = this.getCfgNode() and
|
||||
getDestVar(result) = this.getSourceVariable()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1038,7 +1043,7 @@ class SsaImplicitUpdate extends SsaUpdate {
|
||||
exists(SsaSourceField f, Callable setter |
|
||||
f = this.getSourceVariable() and
|
||||
relevantFieldUpdate(setter, f.getField(), result) and
|
||||
updatesNamedField(this.getCfgNode(), f, setter)
|
||||
updatesNamedField(this.getCfgNode().asCall(), f, setter)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1086,7 +1091,7 @@ class SsaImplicitInit extends SsaVariable, TSsaEntryDef {
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
|
||||
p.getCallable().getBody() = this.getCfgNode()
|
||||
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
*/
|
||||
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil::StringBuilderVarModule
|
||||
|
||||
module TaintTracking {
|
||||
import semmle.code.java.dataflow.internal.tainttracking1.TaintTrackingParameter::Public
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil
|
||||
private import semmle.code.java.dataflow.internal.DataFlowImplSpecific
|
||||
private import semmle.code.java.dataflow.internal.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.TaintTracking
|
||||
import TaintFlowMake<Location, JavaDataFlow, JavaTaintTracking>
|
||||
import semmle.code.java.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking2 {
|
||||
import semmle.code.java.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking3 {
|
||||
import semmle.code.java.dataflow.internal.tainttracking3.TaintTrackingImpl
|
||||
}
|
||||
@@ -13,24 +13,27 @@ private import semmle.code.java.dispatch.VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import codeql.typeflow.TypeFlow
|
||||
private import codeql.typeflow.UniversalFlow as UniversalFlow
|
||||
|
||||
private module Input implements TypeFlowInput<Location> {
|
||||
private newtype TTypeFlowNode =
|
||||
/** Gets `t` if it is a `RefType` or the boxed type if `t` is a primitive type. */
|
||||
private RefType boxIfNeeded(J::Type t) {
|
||||
t.(PrimitiveType).getBoxedType() = result or
|
||||
result = t
|
||||
}
|
||||
|
||||
/** Provides the input types and predicates for instantiation of `UniversalFlow`. */
|
||||
module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
private newtype TFlowNode =
|
||||
TField(Field f) { not f.getType() instanceof PrimitiveType } or
|
||||
TSsa(BaseSsaVariable ssa) { not ssa.getSourceVariable().getType() instanceof PrimitiveType } or
|
||||
TExpr(Expr e) or
|
||||
TMethod(Method m) { not m.getReturnType() instanceof PrimitiveType }
|
||||
|
||||
/** Gets `t` if it is a `RefType` or the boxed type if `t` is a primitive type. */
|
||||
private RefType boxIfNeeded(J::Type t) {
|
||||
t.(PrimitiveType).getBoxedType() = result or
|
||||
result = t
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Field`, `BaseSsaVariable`, `Expr`, or `Method`.
|
||||
*/
|
||||
class TypeFlowNode extends TTypeFlowNode {
|
||||
class FlowNode extends TFlowNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = this.asField().toString() or
|
||||
result = this.asSsa().toString() or
|
||||
@@ -38,6 +41,7 @@ private module Input implements TypeFlowInput<Location> {
|
||||
result = this.asMethod().toString()
|
||||
}
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() {
|
||||
result = this.asField().getLocation() or
|
||||
result = this.asSsa().getLocation() or
|
||||
@@ -45,14 +49,19 @@ private module Input implements TypeFlowInput<Location> {
|
||||
result = this.asMethod().getLocation()
|
||||
}
|
||||
|
||||
/** Gets the field corresponding to this node, if any. */
|
||||
Field asField() { this = TField(result) }
|
||||
|
||||
/** Gets the SSA variable corresponding to this node, if any. */
|
||||
BaseSsaVariable asSsa() { this = TSsa(result) }
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { this = TExpr(result) }
|
||||
|
||||
/** Gets the method corresponding to this node, if any. */
|
||||
Method asMethod() { this = TMethod(result) }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
RefType getType() {
|
||||
result = this.asField().getType() or
|
||||
result = this.asSsa().getSourceVariable().getType() or
|
||||
@@ -61,8 +70,6 @@ private module Input implements TypeFlowInput<Location> {
|
||||
}
|
||||
}
|
||||
|
||||
class Type = RefType;
|
||||
|
||||
private SrcCallable viableCallable_v1(Call c) {
|
||||
result = viableImpl_v1(c)
|
||||
or
|
||||
@@ -88,7 +95,7 @@ private module Input implements TypeFlowInput<Location> {
|
||||
*
|
||||
* For a given `n2`, this predicate must include all possible `n1` that can flow to `n2`.
|
||||
*/
|
||||
predicate step(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
predicate step(FlowNode n1, FlowNode n2) {
|
||||
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
|
||||
or
|
||||
exists(Field f, Expr e |
|
||||
@@ -134,7 +141,7 @@ private module Input implements TypeFlowInput<Location> {
|
||||
/**
|
||||
* Holds if `null` is the only value that flows to `n`.
|
||||
*/
|
||||
predicate isNullValue(TypeFlowNode n) {
|
||||
predicate isNullValue(FlowNode n) {
|
||||
n.asExpr() instanceof NullLiteral
|
||||
or
|
||||
exists(LocalVariableDeclExpr decl |
|
||||
@@ -144,11 +151,21 @@ private module Input implements TypeFlowInput<Location> {
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExcludedFromNullAnalysis(TypeFlowNode n) {
|
||||
predicate isExcludedFromNullAnalysis(FlowNode n) {
|
||||
// Fields that are never assigned a non-null value are probably set by
|
||||
// reflection and are thus not always null.
|
||||
exists(n.asField())
|
||||
}
|
||||
}
|
||||
|
||||
private module Input implements TypeFlowInput<Location> {
|
||||
import FlowStepsInput
|
||||
|
||||
class TypeFlowNode = FlowNode;
|
||||
|
||||
predicate isExcludedFromNullAnalysis = FlowStepsInput::isExcludedFromNullAnalysis/1;
|
||||
|
||||
class Type = RefType;
|
||||
|
||||
predicate exactTypeBase(TypeFlowNode n, RefType t) {
|
||||
exists(ClassInstanceExpr e |
|
||||
@@ -235,8 +252,8 @@ private module Input implements TypeFlowInput<Location> {
|
||||
downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and
|
||||
t1.getASourceSupertype+() = t2 and
|
||||
va = v.getAUse() and
|
||||
dominates(cast, va) and
|
||||
dominates(cast.(ControlFlowNode).getANormalSuccessor(), va)
|
||||
dominates(cast.getControlFlowNode(), va.getControlFlowNode()) and
|
||||
dominates(cast.getControlFlowNode().getANormalSuccessor(), va.getControlFlowNode())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
|
||||
/** Gets the `Callable` in which this `BaseSsaSourceVariable` is defined. */
|
||||
Callable getEnclosingCallable() { this = TLocalVar(result, _) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) |
|
||||
if c = v.getCallable()
|
||||
@@ -46,6 +47,7 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() {
|
||||
exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation())
|
||||
}
|
||||
@@ -71,15 +73,15 @@ private module SsaImpl {
|
||||
/** Holds if `n` updates the local variable `v`. */
|
||||
cached
|
||||
predicate variableUpdate(BaseSsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
|
||||
exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and
|
||||
b.getNode(i) = n and
|
||||
hasDominanceInformation(b)
|
||||
}
|
||||
|
||||
/** Gets the definition point of a nested class in the parent scope. */
|
||||
private ControlFlowNode parentDef(NestedClass nc) {
|
||||
nc.(AnonymousClass).getClassInstanceExpr() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt() = result
|
||||
nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt().getControlFlowNode() = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +121,7 @@ private module SsaImpl {
|
||||
|
||||
/** Holds if `VarAccess` `use` of `v` occurs in `b` at index `i`. */
|
||||
private predicate variableUse(BaseSsaSourceVariable v, VarRead use, BasicBlock b, int i) {
|
||||
v.getAnAccess() = use and b.getNode(i) = use
|
||||
v.getAnAccess() = use and b.getNode(i) = use.getControlFlowNode()
|
||||
}
|
||||
|
||||
/** Holds if the value of `v` is captured in `b` at index `i`. */
|
||||
@@ -162,7 +164,9 @@ private module SsaImpl {
|
||||
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
|
||||
cached
|
||||
predicate hasEntryDef(BaseSsaSourceVariable v, BasicBlock b) {
|
||||
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
|
||||
exists(LocalScopeVariable l, Callable c |
|
||||
v = TLocalVar(c, l) and c.getBody().getControlFlowNode() = b
|
||||
|
|
||||
l instanceof Parameter or
|
||||
l.getCallable() != c
|
||||
)
|
||||
@@ -482,8 +486,10 @@ class BaseSsaVariable extends TBaseSsaVariable {
|
||||
this = TSsaEntryDef(_, result)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = this.getCfgNode().getLocation() }
|
||||
|
||||
/** Gets the `BasicBlock` in which this SSA variable is defined. */
|
||||
@@ -533,7 +539,7 @@ class BaseSsaVariable extends TBaseSsaVariable {
|
||||
class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate {
|
||||
BaseSsaUpdate() {
|
||||
exists(VariableUpdate upd |
|
||||
upd = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
upd.getControlFlowNode() = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -541,7 +547,8 @@ class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate {
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
VariableUpdate getDefiningExpr() {
|
||||
result = this.getCfgNode() and getDestVar(result) = this.getSourceVariable()
|
||||
result.getControlFlowNode() = this.getCfgNode() and
|
||||
getDestVar(result) = this.getSourceVariable()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,7 +569,7 @@ class BaseSsaImplicitInit extends BaseSsaVariable, TSsaEntryDef {
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
|
||||
p.getCallable().getBody() = this.getCfgNode()
|
||||
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,361 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
deprecated import FlowStateString
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
Node node1, FlowState state1, Node node2, FlowState state2, string model
|
||||
) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1) and
|
||||
model = ""
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1 and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { none() }
|
||||
}
|
||||
|
||||
deprecated private import Impl<Config> as I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
@@ -1,361 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
deprecated import FlowStateString
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
Node node1, FlowState state1, Node node2, FlowState state2, string model
|
||||
) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1) and
|
||||
model = ""
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1 and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { none() }
|
||||
}
|
||||
|
||||
deprecated private import Impl<Config> as I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
@@ -1,361 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
deprecated import FlowStateString
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
Node node1, FlowState state1, Node node2, FlowState state2, string model
|
||||
) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1) and
|
||||
model = ""
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1 and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { none() }
|
||||
}
|
||||
|
||||
deprecated private import Impl<Config> as I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
@@ -1,361 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
deprecated import FlowStateString
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
Node node1, FlowState state1, Node node2, FlowState state2, string model
|
||||
) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1) and
|
||||
model = ""
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1 and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { none() }
|
||||
}
|
||||
|
||||
deprecated private import Impl<Config> as I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
@@ -1,361 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
deprecated import FlowStateString
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
Node node1, FlowState state1, Node node2, FlowState state2, string model
|
||||
) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1) and
|
||||
model = ""
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1 and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { none() }
|
||||
}
|
||||
|
||||
deprecated private import Impl<Config> as I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
@@ -1,361 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
deprecated import FlowStateString
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
Node node1, FlowState state1, Node node2, FlowState state2, string model
|
||||
) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1) and
|
||||
model = ""
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1 and
|
||||
model = ""
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { none() }
|
||||
}
|
||||
|
||||
deprecated private import Impl<Config> as I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
@@ -112,7 +112,7 @@ private module CaptureInput implements VariableCapture::InputSig<Location> {
|
||||
|
||||
Location getLocation() { result = super.getLocation() }
|
||||
|
||||
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i) }
|
||||
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i).asExpr() }
|
||||
}
|
||||
|
||||
class VariableWrite extends Expr instanceof VariableUpdate {
|
||||
|
||||
@@ -17,9 +17,11 @@ import DataFlowNodes::Public
|
||||
|
||||
/** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */
|
||||
private predicate thisAccess(Node n, ControlFlowNode cfgnode) {
|
||||
n.(InstanceParameterNode).getCallable().getBody() = cfgnode
|
||||
n.(InstanceParameterNode).getCallable().getBody() = cfgnode.asStmt()
|
||||
or
|
||||
exists(InstanceAccess ia | ia = n.asExpr() and ia = cfgnode and ia.isOwnInstanceAccess())
|
||||
exists(InstanceAccess ia |
|
||||
ia = n.asExpr() and ia.getControlFlowNode() = cfgnode and ia.isOwnInstanceAccess()
|
||||
)
|
||||
or
|
||||
n.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getCfgNode() = cfgnode
|
||||
}
|
||||
@@ -160,11 +162,8 @@ predicate localMustFlowStep(Node node1, Node node2) {
|
||||
or
|
||||
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
|
||||
or
|
||||
node1 =
|
||||
unique(FlowSummaryNode n1 |
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(),
|
||||
node2.(FlowSummaryNode).getSummaryNode(), true, _)
|
||||
)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1.(FlowSummaryNode).getSummaryNode(),
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
@@ -161,6 +161,7 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) {
|
||||
node instanceof DefaultTaintSanitizer or
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
node.asExpr() instanceof ValidatedVariableAccess
|
||||
|
||||
@@ -133,5 +133,5 @@ module Private {
|
||||
|
||||
predicate ssaUpdateStep = RU::ssaUpdateStep/3;
|
||||
|
||||
Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode() }
|
||||
Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode().asExpr() }
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ private module Impl {
|
||||
|
||||
Field getField(FieldAccess fa) { result = fa.getField() }
|
||||
|
||||
Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode() }
|
||||
Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode().asExpr() }
|
||||
|
||||
Guard getComparisonGuard(ComparisonExpr ce) { result = ce }
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@ class BasicBlock = BB::BasicBlock;
|
||||
/** Gets a basic block in which SSA variable `v` is read. */
|
||||
BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getAUse().getBasicBlock() }
|
||||
|
||||
private predicate id(BasicBlock x, BasicBlock y) { x = y }
|
||||
private predicate id(BB::ExprParent x, BB::ExprParent y) { x = y }
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
private predicate idOfAst(BB::ExprParent x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) { idOfAst(x.getAstNode(), y) }
|
||||
|
||||
private int getId(BasicBlock bb) { idOf(bb, result) }
|
||||
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(
|
||||
this.isSink(node) or
|
||||
this.isSink(node, _) or
|
||||
this.isAdditionalTaintStep(node, _) or
|
||||
this.isAdditionalTaintStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.java.dataflow.DataFlow::DataFlow as DataFlow
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl as DataFlowInternal
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(
|
||||
this.isSink(node) or
|
||||
this.isSink(node, _) or
|
||||
this.isAdditionalTaintStep(node, _) or
|
||||
this.isAdditionalTaintStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.java.dataflow.DataFlow2::DataFlow2 as DataFlow
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(
|
||||
this.isSink(node) or
|
||||
this.isSink(node, _) or
|
||||
this.isAdditionalTaintStep(node, _) or
|
||||
this.isAdditionalTaintStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.java.dataflow.DataFlow3::DataFlow3 as DataFlow
|
||||
}
|
||||
@@ -109,12 +109,12 @@ predicate assertFail(BasicBlock bb, ControlFlowNode n) {
|
||||
bb = n.getBasicBlock() and
|
||||
(
|
||||
exists(AssertTrueMethod m |
|
||||
n = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = false))
|
||||
n.asExpr() = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = false))
|
||||
) or
|
||||
exists(AssertFalseMethod m |
|
||||
n = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = true))
|
||||
n.asExpr() = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = true))
|
||||
) or
|
||||
exists(AssertFailMethod m | n = m.getACheck()) or
|
||||
n.(AssertStmt).getExpr().(BooleanLiteral).getBooleanValue() = false
|
||||
exists(AssertFailMethod m | n.asExpr() = m.getACheck()) or
|
||||
n.asStmt().(AssertStmt).getExpr().(BooleanLiteral).getBooleanValue() = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
import java
|
||||
import semmle.code.java.Maps
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
deprecated import semmle.code.java.dataflow.DataFlow2
|
||||
private import semmle.code.java.dataflow.DataFlow2
|
||||
|
||||
/**
|
||||
* The class `com.cedarsoftware.util.io.JsonReader`.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import java
|
||||
import SpringHttp
|
||||
private import semmle.code.java.security.RequestForgery
|
||||
|
||||
/** The class `org.springframework.web.client.RestTemplate`. */
|
||||
class SpringRestTemplate extends Class {
|
||||
@@ -27,3 +28,76 @@ class SpringWebClient extends Interface {
|
||||
this.hasQualifiedName("org.springframework.web.reactive.function.client", "WebClient")
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `getForObject` on `org.springframework.web.reactive.function.client.RestTemplate`. */
|
||||
class SpringRestTemplateGetForObjectMethod extends Method {
|
||||
SpringRestTemplateGetForObjectMethod() {
|
||||
this.getDeclaringType() instanceof SpringRestTemplate and
|
||||
this.hasName("getForObject")
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to the method `getForObject` on `org.springframework.web.reactive.function.client.RestTemplate`. */
|
||||
class SpringRestTemplateGetForObjectMethodCall extends MethodCall {
|
||||
SpringRestTemplateGetForObjectMethodCall() {
|
||||
this.getMethod() instanceof SpringRestTemplateGetForObjectMethod
|
||||
}
|
||||
|
||||
/** Gets the first argument, if it is a compile time constant. */
|
||||
CompileTimeConstantExpr getConstantUrl() { result = this.getArgument(0) }
|
||||
|
||||
/**
|
||||
* Holds if the first argument is a compile time constant and it has a
|
||||
* placeholder at offset `offset`, and there are `idx` placeholders that
|
||||
* appear before it.
|
||||
*/
|
||||
predicate urlHasPlaceholderAtOffset(int idx, int offset) {
|
||||
exists(
|
||||
this.getConstantUrl()
|
||||
.getStringValue()
|
||||
.replaceAll("\\{", " ")
|
||||
.replaceAll("\\}", " ")
|
||||
.regexpFind("\\{[^}]*\\}", idx, offset)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SpringWebClientRestTemplateGetForObject extends RequestForgerySink {
|
||||
SpringWebClientRestTemplateGetForObject() {
|
||||
exists(SpringRestTemplateGetForObjectMethodCall mc, int i |
|
||||
// Note that the first argument is modeled as a request forgery sink
|
||||
// separately. This model is for arguments beyond the first two. There
|
||||
// are two relevant overloads, one with third parameter type `Object...`
|
||||
// and one with third parameter type `Map<String, ?>`. For the latter we
|
||||
// cannot deal with MapValue content easily but there is a default
|
||||
// implicit taint read at sinks that will catch it.
|
||||
i >= 0 and
|
||||
this.asExpr() = mc.getArgument(i + 2)
|
||||
|
|
||||
// If we can determine that part of mc.getArgument(0) is a hostname
|
||||
// sanitizing prefix, then we count how many placeholders occur before it
|
||||
// and only consider that many arguments beyond the first two as sinks.
|
||||
// For the `Map<String, ?>` overload this has the effect of only
|
||||
// considering the map values as sinks if there is at least one
|
||||
// placeholder in the URL before the hostname sanitizing prefix.
|
||||
exists(int offset |
|
||||
mc.urlHasPlaceholderAtOffset(i, offset) and
|
||||
offset < mc.getConstantUrl().(HostnameSanitizingPrefix).getOffset()
|
||||
)
|
||||
or
|
||||
// If we cannot determine that part of mc.getArgument(0) is a hostname
|
||||
// sanitizing prefix, but it is a compile time constant and we can get
|
||||
// its string value, then we count how many placeholders occur in it
|
||||
// and only consider that many arguments beyond the first two as sinks.
|
||||
// For the `Map<String, ?>` overload this has the effect of only
|
||||
// considering the map values as sinks if there is at least one
|
||||
// placeholder in the URL.
|
||||
not mc.getConstantUrl() instanceof HostnameSanitizingPrefix and
|
||||
mc.urlHasPlaceholderAtOffset(i, _)
|
||||
or
|
||||
// If we cannot determine the string value of mc.getArgument(0), then we
|
||||
// conservatively consider all arguments as sinks.
|
||||
not exists(mc.getConstantUrl().getStringValue())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,14 +73,14 @@ class MetricCallable extends Callable {
|
||||
// so there should be a branching point for each non-default switch
|
||||
// case (ignoring those that just fall through to the next case).
|
||||
private predicate branchingSwitchCase(ConstCase sc) {
|
||||
not sc.(ControlFlowNode).getASuccessor() instanceof SwitchCase and
|
||||
not sc.getControlFlowNode().getASuccessor().asStmt() instanceof SwitchCase and
|
||||
not defaultFallThrough(sc)
|
||||
}
|
||||
|
||||
private predicate defaultFallThrough(ConstCase sc) {
|
||||
exists(DefaultCase default | default.(ControlFlowNode).getASuccessor() = sc)
|
||||
exists(DefaultCase default | default.getControlFlowNode().getASuccessor().asStmt() = sc)
|
||||
or
|
||||
defaultFallThrough(sc.(ControlFlowNode).getAPredecessor())
|
||||
defaultFallThrough(sc.getControlFlowNode().getAPredecessor().asStmt())
|
||||
}
|
||||
|
||||
/** Holds if `stmt` is a branching statement used for the computation of cyclomatic complexity. */
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
deprecated import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
deprecated import semmle.code.java.dataflow.TaintTracking3
|
||||
import semmle.code.java.security.AndroidIntentRedirection
|
||||
|
||||
/** A taint tracking configuration for tainted Intents being used to start Android components. */
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
deprecated import semmle.code.java.dataflow.DataFlow3
|
||||
import semmle.code.java.security.CleartextStorageQuery
|
||||
private import semmle.code.java.dataflow.FlowSinks
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
@@ -223,10 +223,7 @@ string getAnInsecureHashAlgorithmName() {
|
||||
}
|
||||
|
||||
private string rankedInsecureAlgorithm(int i) {
|
||||
// In this case we know these are being used for encryption, so we want to match
|
||||
// weak hash algorithms too.
|
||||
result =
|
||||
rank[i](string s | s = getAnInsecureAlgorithmName() or s = getAnInsecureHashAlgorithmName())
|
||||
result = rank[i](string s | s = getAnInsecureAlgorithmName())
|
||||
}
|
||||
|
||||
private string insecureAlgorithmString(int i) {
|
||||
@@ -249,8 +246,8 @@ string getInsecureAlgorithmRegex() {
|
||||
string getASecureAlgorithmName() {
|
||||
result =
|
||||
[
|
||||
"RSA", "SHA-?256", "SHA-?512", "CCM", "GCM", "AES(?)",
|
||||
"Blowfish", "ECIES"
|
||||
"RSA", "SHA-?(256|384|512)", "CCM", "GCM", "AES(?)",
|
||||
"Blowfish", "ECIES", "SHA3-(256|384|512)"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
deprecated import semmle.code.java.dataflow.DataFlow2
|
||||
private import semmle.code.java.dataflow.DataFlow2
|
||||
import HardcodedCredentials
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,24 +26,10 @@ class NoState extends PendingIntentState, TNoState {
|
||||
}
|
||||
|
||||
/** A source for an implicit `PendingIntent` flow. */
|
||||
abstract class ImplicitPendingIntentSource extends ApiSourceNode {
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
*
|
||||
* Holds if this source has the specified `state`.
|
||||
*/
|
||||
deprecated predicate hasState(DataFlow::FlowState state) { state = "" }
|
||||
}
|
||||
abstract class ImplicitPendingIntentSource extends ApiSourceNode { }
|
||||
|
||||
/** A sink that sends an implicit and mutable `PendingIntent` to a third party. */
|
||||
abstract class ImplicitPendingIntentSink extends DataFlow::Node {
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
*
|
||||
* Holds if this sink has the specified `state`.
|
||||
*/
|
||||
deprecated predicate hasState(DataFlow::FlowState state) { state = "" }
|
||||
}
|
||||
abstract class ImplicitPendingIntentSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
@@ -62,21 +48,6 @@ class ImplicitPendingIntentAdditionalTaintStep extends Unit {
|
||||
* Holds if the step from `node1` to `node2` creates a mutable `PendingIntent`.
|
||||
*/
|
||||
predicate mutablePendingIntentCreation(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
* Use `mutablePendingIntentCreation` instead.
|
||||
*
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for flows related to the use of implicit `PendingIntent`s. This step is only applicable
|
||||
* in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
deprecated predicate step(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class IntentCreationSource extends ImplicitPendingIntentSource {
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* Provides a default taint sanitizer identifying comparisons against lists of
|
||||
* compile-time constants.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import codeql.typeflow.UniversalFlow as UniversalFlow
|
||||
private import semmle.code.java.Collections
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
|
||||
private class FlowNode = FlowStepsInput::FlowNode;
|
||||
|
||||
/**
|
||||
* Holds if `n2` is an unmodifiable collection constructed from input `n1`,
|
||||
* which is either another collection or a number of elements.
|
||||
*/
|
||||
private predicate unmodifiableCollectionStep(FlowNode n1, FlowNode n2) {
|
||||
exists(Call c, Callable tgt |
|
||||
n2.asExpr() = c and
|
||||
n1.asExpr() = c.getAnArgument() and
|
||||
c.getCallee().getSourceDeclaration() = tgt
|
||||
|
|
||||
tgt.hasQualifiedName("java.util", "Collections",
|
||||
["unmodifiableCollection", "unmodifiableList", "unmodifiableSet"])
|
||||
or
|
||||
tgt.hasQualifiedName("java.util", ["List", "Set"], ["copyOf", "of"])
|
||||
or
|
||||
tgt.hasQualifiedName("com.google.common.collect", ["ImmutableList", "ImmutableSet"],
|
||||
["copyOf", "of"])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n2` is a collection or array constructed from input `n1`, which is
|
||||
* either a collection, an array, or a number of elements.
|
||||
*/
|
||||
private predicate collectionStep(FlowNode n1, FlowNode n2) {
|
||||
n2.asExpr().(ArrayInit).getAnInit() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(ArrayCreationExpr).getInit() = n1.asExpr()
|
||||
or
|
||||
unmodifiableCollectionStep(n1, n2)
|
||||
or
|
||||
exists(Call c, Callable tgt |
|
||||
n2.asExpr() = c and
|
||||
n1.asExpr() = c.getAnArgument() and
|
||||
c.getCallee().getSourceDeclaration() = tgt
|
||||
|
|
||||
tgt.hasQualifiedName("java.util", "Arrays", "asList")
|
||||
or
|
||||
tgt.isStatic() and
|
||||
tgt.hasName(["copyOf", "of"]) and
|
||||
tgt.getDeclaringType().getASourceSupertype+().hasQualifiedName("java.util", "Collection")
|
||||
or
|
||||
tgt instanceof Constructor and
|
||||
tgt.getNumberOfParameters() = 1 and
|
||||
tgt.getParameterType(0) instanceof CollectionType and
|
||||
tgt.getDeclaringType() instanceof CollectionType
|
||||
)
|
||||
}
|
||||
|
||||
private module BaseUniversalFlow = UniversalFlow::Make<Location, FlowStepsInput>;
|
||||
|
||||
private module UnmodifiableProp implements BaseUniversalFlow::NullaryPropertySig {
|
||||
predicate hasPropertyBase(FlowNode n) { unmodifiableCollectionStep(_, n) }
|
||||
}
|
||||
|
||||
/** Holds if the given node is an unmodifiable collection. */
|
||||
private predicate unmodifiableCollection =
|
||||
BaseUniversalFlow::FlowNullary<UnmodifiableProp>::hasProperty/1;
|
||||
|
||||
/**
|
||||
* Holds if `v` is a collection or array with an access, `coll`, at which the
|
||||
* element `e` gets added.
|
||||
*/
|
||||
private predicate collectionAddition(Variable v, VarAccess coll, Expr e) {
|
||||
exists(MethodCall mc, Method m, int arg |
|
||||
mc.getMethod().getSourceDeclaration().overridesOrInstantiates*(m) and
|
||||
mc.getQualifier() = coll and
|
||||
v.getAnAccess() = coll and
|
||||
mc.getArgument(arg) = e
|
||||
|
|
||||
m.hasQualifiedName("java.util", "Collection", ["add", "addAll"]) and
|
||||
m.getNumberOfParameters() = 1 and
|
||||
arg = 0
|
||||
or
|
||||
m.hasQualifiedName("java.util", "List", ["add", "addAll"]) and
|
||||
m.getNumberOfParameters() = 2 and
|
||||
arg = 1
|
||||
or
|
||||
m.hasQualifiedName("java.util", "SequencedCollection", ["addFirst", "addLast"]) and
|
||||
m.getNumberOfParameters() = 1 and
|
||||
arg = 0
|
||||
)
|
||||
or
|
||||
v.getAnAccess() = coll and
|
||||
exists(Assignment assign | assign.getSource() = e |
|
||||
coll = assign.getDest().(ArrayAccess).getArray()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` represents a definition of `v` and `v` is a collection or
|
||||
* array that has additions occurring as side-effects after its definition.
|
||||
*/
|
||||
private predicate nodeWithAddition(FlowNode n, Variable v) {
|
||||
collectionAddition(v, _, _) and
|
||||
(
|
||||
n.asField() = v
|
||||
or
|
||||
n.asSsa().getSourceVariable().getVariable() = v and
|
||||
(n.asSsa() instanceof BaseSsaUpdate or n.asSsa().(BaseSsaImplicitInit).isParameterDefinition(_))
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `c` does not add elements to the given collection. */
|
||||
private predicate safeCallable(Callable c) {
|
||||
c instanceof CollectionQueryMethod
|
||||
or
|
||||
c instanceof CollectionMethod and
|
||||
c.hasName(["clear", "remove", "removeAll", "stream", "iterator", "toArray"])
|
||||
or
|
||||
c.hasQualifiedName("org.apache.commons.lang3", "StringUtils", "join")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` might be mutated in ways that adds elements that are not
|
||||
* tracked by the `collectionAddition` predicate.
|
||||
*/
|
||||
private predicate collectionWithPossibleMutation(FlowNode n) {
|
||||
not unmodifiableCollection(n) and
|
||||
(
|
||||
exists(Expr e |
|
||||
n.asExpr() = e and
|
||||
(e.getType() instanceof CollectionType or e.getType() instanceof Array) and
|
||||
not collectionAddition(_, e, _) and
|
||||
not collectionStep(n, _)
|
||||
|
|
||||
exists(ArrayAccess aa | e = aa.getArray())
|
||||
or
|
||||
exists(Call c, Callable tgt | c.getAnArgument() = e or c.getQualifier() = e |
|
||||
tgt = c.getCallee().getSourceDeclaration() and
|
||||
not safeCallable(tgt)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(FlowNode mid |
|
||||
FlowStepsInput::step(n, mid) and
|
||||
collectionWithPossibleMutation(mid)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection constructor that constructs an empty mutable collection.
|
||||
*/
|
||||
private class EmptyCollectionConstructor extends Constructor {
|
||||
EmptyCollectionConstructor() {
|
||||
this.getDeclaringType() instanceof CollectionType and
|
||||
forall(Type t | t = this.getAParamType() | t instanceof PrimitiveType)
|
||||
}
|
||||
}
|
||||
|
||||
private module CollectionFlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
import FlowStepsInput
|
||||
|
||||
/**
|
||||
* Holds if `n2` is a collection/array/constant whose value(s) are
|
||||
* determined completely from the range of `n1` nodes.
|
||||
*/
|
||||
predicate step(FlowNode n1, FlowNode n2) {
|
||||
// Exclude the regular input constraints for those nodes that are covered
|
||||
// completely by `collectionStep`.
|
||||
FlowStepsInput::step(n1, n2) and
|
||||
not collectionStep(_, n2)
|
||||
or
|
||||
// For collections with side-effects in the form of additions, we add the
|
||||
// sources of those additions as additional input that need to originate
|
||||
// from constants.
|
||||
exists(Variable v |
|
||||
nodeWithAddition(n2, v) and
|
||||
collectionAddition(v, _, n1.asExpr())
|
||||
)
|
||||
or
|
||||
// Include various forms of collection transformation.
|
||||
collectionStep(n1, n2)
|
||||
}
|
||||
|
||||
predicate isExcludedFromNullAnalysis = FlowStepsInput::isExcludedFromNullAnalysis/1;
|
||||
}
|
||||
|
||||
private module CollectionUniversalFlow = UniversalFlow::Make<Location, CollectionFlowStepsInput>;
|
||||
|
||||
private module ConstantCollectionProp implements CollectionUniversalFlow::NullaryPropertySig {
|
||||
/**
|
||||
* Holds if `n` forms the base case for finding collections of constants.
|
||||
* These are individual constants and empty collections.
|
||||
*/
|
||||
predicate hasPropertyBase(FlowNode n) {
|
||||
n.asExpr().isCompileTimeConstant() or
|
||||
n.asExpr().(ConstructorCall).getConstructor() instanceof EmptyCollectionConstructor
|
||||
}
|
||||
|
||||
predicate barrier = collectionWithPossibleMutation/1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given node is either a constant or a collection/array of
|
||||
* constants.
|
||||
*/
|
||||
private predicate constantCollection =
|
||||
CollectionUniversalFlow::FlowNullary<ConstantCollectionProp>::hasProperty/1;
|
||||
|
||||
/** Gets the result of a case normalization call of `arg`. */
|
||||
private MethodCall normalizeCaseCall(Expr arg) {
|
||||
exists(Method changecase | result.getMethod() = changecase |
|
||||
changecase.hasName(["toUpperCase", "toLowerCase"]) and
|
||||
changecase.getDeclaringType() instanceof TypeString and
|
||||
arg = result.getQualifier()
|
||||
or
|
||||
changecase
|
||||
.hasQualifiedName(["org.apache.commons.lang", "org.apache.commons.lang3"], "StringUtils",
|
||||
["lowerCase", "upperCase"]) and
|
||||
arg = result.getArgument(0)
|
||||
or
|
||||
changecase
|
||||
.hasQualifiedName("org.apache.hadoop.util", "StringUtils", ["toLowerCase", "toUpperCase"]) and
|
||||
arg = result.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` ensures that the expression `e` is one of a set of
|
||||
* known constants upon evaluating to `branch`.
|
||||
*/
|
||||
private predicate constantCollectionContainsCheck(Guard g, Expr e, boolean branch) {
|
||||
exists(MethodCall mc, Method m, FlowNode n, Expr checked |
|
||||
g = mc and
|
||||
mc.getMethod().getSourceDeclaration().overridesOrInstantiates*(m) and
|
||||
m.hasQualifiedName("java.util", "Collection", "contains") and
|
||||
n.asExpr() = mc.getQualifier() and
|
||||
constantCollection(n) and
|
||||
checked = mc.getAnArgument() and
|
||||
branch = true
|
||||
|
|
||||
checked = e or
|
||||
checked = normalizeCaseCall(e)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparison against a list of compile-time constants, sanitizing taint by
|
||||
* restricting to a set of known values.
|
||||
*/
|
||||
private class ListOfConstantsComparisonSanitizerGuard extends TaintTracking::DefaultTaintSanitizer {
|
||||
ListOfConstantsComparisonSanitizerGuard() {
|
||||
this = DataFlow::BarrierGuard<constantCollectionContainsCheck/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,11 @@ class InsecureAlgoLiteral extends InsecureAlgorithm, ShortStringLiteral {
|
||||
s.length() > 1 and
|
||||
not s.regexpMatch(getSecureAlgorithmRegex()) and
|
||||
// Exclude results covered by another query.
|
||||
not s.regexpMatch(getInsecureAlgorithmRegex())
|
||||
not s.regexpMatch(getInsecureAlgorithmRegex()) and
|
||||
// Exclude results covered by `InsecureAlgoProperty`.
|
||||
// This removes duplicates when a string literal is a default value for the property,
|
||||
// such as "MD5" in the following: `props.getProperty("hashAlg2", "MD5")`.
|
||||
not exists(InsecureAlgoProperty insecAlgoProp | this = insecAlgoProp.getAnArgument())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,15 +29,19 @@ private module ValidationMethod<DataFlow::guardChecksSig/3 validationGuard> {
|
||||
*/
|
||||
private predicate validationMethod(Method m, int arg) {
|
||||
exists(
|
||||
Guard g, SsaImplicitInit var, ControlFlowNode exit, ControlFlowNode normexit, boolean branch
|
||||
Guard g, SsaImplicitInit var, ControlFlow::ExitNode exit, ControlFlowNode normexit,
|
||||
boolean branch
|
||||
|
|
||||
validationGuard(g, var.getAUse(), branch) and
|
||||
var.isParameterDefinition(m.getParameter(arg)) and
|
||||
exit = m and
|
||||
exit.getEnclosingCallable() = m and
|
||||
normexit.getANormalSuccessor() = exit and
|
||||
1 = strictcount(ControlFlowNode n | n.getANormalSuccessor() = exit)
|
||||
|
|
||||
g.(ConditionNode).getABranchSuccessor(branch) = exit or
|
||||
exists(ConditionNode conditionNode |
|
||||
g = conditionNode.getCondition() and conditionNode.getABranchSuccessor(branch) = exit
|
||||
)
|
||||
or
|
||||
g.controls(normexit.getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
@@ -333,3 +337,18 @@ private Method getSourceMethod(Method m) {
|
||||
not exists(Method src | m = src.getKotlinParameterDefaultsProxy()) and
|
||||
result = m
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer that protects against path injection vulnerabilities
|
||||
* by extracting the final component of the user provided path.
|
||||
*
|
||||
* TODO: convert this class to models-as-data if sanitizer support is added
|
||||
*/
|
||||
private class FileGetNameSanitizer extends PathInjectionSanitizer {
|
||||
FileGetNameSanitizer() {
|
||||
exists(MethodCall mc |
|
||||
mc.getMethod().hasQualifiedName("java.io", "File", "getName") and
|
||||
this.asExpr() = mc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,14 +63,17 @@ abstract class RequestForgerySanitizer extends DataFlow::Node { }
|
||||
|
||||
private class PrimitiveSanitizer extends RequestForgerySanitizer instanceof SimpleTypeSanitizer { }
|
||||
|
||||
private class HostnameSanitizingPrefix extends InterestingPrefix {
|
||||
/**
|
||||
* A string constant that contains a prefix which looks like when it is prepended to untrusted
|
||||
* input, it will restrict the host or entity addressed.
|
||||
*
|
||||
* For example, anything containing `?` or `#`, or a slash that doesn't appear to be a protocol
|
||||
* specifier (e.g. `http://` is not sanitizing), or specifically the string "/".
|
||||
*/
|
||||
class HostnameSanitizingPrefix extends InterestingPrefix {
|
||||
int offset;
|
||||
|
||||
HostnameSanitizingPrefix() {
|
||||
// Matches strings that look like when prepended to untrusted input, they will restrict
|
||||
// the host or entity addressed: for example, anything containing `?` or `#`, or a slash that
|
||||
// doesn't appear to be a protocol specifier (e.g. `http://` is not sanitizing), or specifically
|
||||
// the string "/".
|
||||
exists(this.getStringValue().regexpFind("([?#]|[^?#:/\\\\][/\\\\])|^/$", 0, offset))
|
||||
}
|
||||
|
||||
@@ -81,8 +84,10 @@ private class HostnameSanitizingPrefix extends InterestingPrefix {
|
||||
* A value that is the result of prepending a string that prevents any value from controlling the
|
||||
* host of a URL.
|
||||
*/
|
||||
private class HostnameSantizer extends RequestForgerySanitizer {
|
||||
HostnameSantizer() { this.asExpr() = any(HostnameSanitizingPrefix hsp).getAnAppendedExpression() }
|
||||
private class HostnameSanitizer extends RequestForgerySanitizer {
|
||||
HostnameSanitizer() {
|
||||
this.asExpr() = any(HostnameSanitizingPrefix hsp).getAnAppendedExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,30 +9,12 @@ private import semmle.code.java.security.Sanitizers
|
||||
/**
|
||||
* A source for server-side template injection (SST) vulnerabilities.
|
||||
*/
|
||||
abstract class TemplateInjectionSource extends DataFlow::Node {
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
*
|
||||
* Holds if this source has the specified `state`.
|
||||
*/
|
||||
deprecated predicate hasState(DataFlow::FlowState state) {
|
||||
state instanceof DataFlow::FlowStateEmpty
|
||||
}
|
||||
}
|
||||
abstract class TemplateInjectionSource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sink for server-side template injection (SST) vulnerabilities.
|
||||
*/
|
||||
abstract class TemplateInjectionSink extends DataFlow::Node {
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
*
|
||||
* Holds if this sink has the specified `state`.
|
||||
*/
|
||||
deprecated predicate hasState(DataFlow::FlowState state) {
|
||||
state instanceof DataFlow::FlowStateEmpty
|
||||
}
|
||||
}
|
||||
abstract class TemplateInjectionSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
@@ -46,20 +28,6 @@ class TemplateInjectionAdditionalTaintStep extends Unit {
|
||||
* step for flows related to server-side template injection (SST) vulnerabilities.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
*
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for flows related toserver-side template injection (SST) vulnerabilities.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
deprecated predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,22 +35,6 @@ class TemplateInjectionAdditionalTaintStep extends Unit {
|
||||
*/
|
||||
abstract class TemplateInjectionSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
*
|
||||
* A sanitizer for server-side template injection (SST) vulnerabilities.
|
||||
* This sanitizer is only applicable when `TemplateInjectionSanitizerWithState::hasState`
|
||||
* holds for the flow state.
|
||||
*/
|
||||
abstract deprecated class TemplateInjectionSanitizerWithState extends DataFlow::Node {
|
||||
/**
|
||||
* DEPRECATED: Open-ended flow state is not intended to be part of the extension points.
|
||||
*
|
||||
* Holds if this sanitizer has the specified `state`.
|
||||
*/
|
||||
abstract deprecated predicate hasState(DataFlow::FlowState state);
|
||||
}
|
||||
|
||||
private class DefaultTemplateInjectionSource extends TemplateInjectionSource instanceof ActiveThreatModelSource
|
||||
{ }
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.FlowSinks
|
||||
private import semmle.code.java.dataflow.TaintTracking2
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
private import semmle.code.java.frameworks.Kryo
|
||||
private import semmle.code.java.frameworks.XStream
|
||||
|
||||
@@ -50,7 +50,7 @@ private predicate validatedAccess(VarAccess va) {
|
||||
bb.getNode(i + 1) = node.getANormalSuccessor()
|
||||
|
|
||||
bb.bbStrictlyDominates(va.getBasicBlock()) or
|
||||
bb.getNode(any(int j | j > i)) = va
|
||||
bb.getNode(any(int j | j > i)).asExpr() = va
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
deprecated import semmle.code.java.dataflow.DataFlow3
|
||||
private import semmle.code.java.dataflow.DataFlow3
|
||||
private import semmle.code.java.dataflow.RangeUtils
|
||||
|
||||
private module Frameworks {
|
||||
|
||||
Reference in New Issue
Block a user