Merge branch 'main' of https://github.com/github/codeql into oscarsj/merge-back-rc-3.21

This commit is contained in:
Óscar San José
2026-03-06 16:20:36 +01:00
846 changed files with 87321 additions and 58588 deletions

View File

@@ -0,0 +1,16 @@
---
category: breaking
---
* The Java control flow graph (CFG) implementation has been completely
rewritten. The CFG now includes additional nodes to more accurately represent
certain constructs. This also means that any existing code that implicitly
relies on very specific details about the CFG may need to be updated.
The CFG now only includes the nodes that are reachable from the entry point.
Additionally, the following breaking changes have been made:
- `ControlFlowNode.asCall` has been removed - use `Call.getControlFlowNode` instead.
- `ControlFlowNode.getEnclosingStmt` has been removed.
- `ControlFlow::ExprNode` has been removed.
- `ControlFlow::StmtNode` has been removed.
- `ControlFlow::Node` has been removed - this was merely an alias of
`ControlFlowNode`, which is still available.
- Previously deprecated predicates on `BasicBlock` have been removed.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The class `Assignment` now extends `BinaryExpr`. Uses of `BinaryExpr` may in some cases need slight adjustment.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.

View File

@@ -815,6 +815,7 @@ when_branch_else(unique int id: @whenbranch ref);
| @geexpr
| @eqexpr
| @neexpr
| @assignment
| @valueeqexpr
| @valueneexpr;

View File

@@ -57,7 +57,7 @@ extensions:
- ["java.net", "InetSocketAddress", True, "getPort", "()", "", "Argument[this].SyntheticField[java.net.InetSocketAddress.port]", "ReturnValue", "taint", "manual"]
- ["java.net", "URI", False, "resolve", "(URI)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", "Argument[5]", "Argument[this].SyntheticField[java.net.URI.query]", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String,String,String)", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.net", "URI", False, "create", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.net", "URI", False, "getPath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-manual"]

View File

@@ -4,7 +4,7 @@ extensions:
extensible: summaryModel
data:
- ["java.util.zip", "GZIPInputStream", False, "GZIPInputStream", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.util.zip", "ZipEntry", True, "ZipEntry", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.util.zip", "ZipEntry", True, "ZipEntry", "(String)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
- ["java.util.zip", "ZipFile", True, "ZipFile", "(File)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.util.zip", "ZipFile", True, "ZipFile", "(File,Charset)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.util.zip", "ZipFile", True, "ZipFile", "(File,int)", "", "Argument[0]", "Argument[this]", "taint", "manual"]

View File

@@ -3,8 +3,8 @@ extensions:
pack: codeql/java-all
extensible: summaryModel
data:
- ["org.apache.hc.core5.http.io.entity", "BasicHttpEntity", True, "BasicHttpEntity", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "BufferedHttpEntity", True, "BufferedHttpEntity", "(HttpEntity)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "BasicHttpEntity", True, "BasicHttpEntity", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "BufferedHttpEntity", True, "BufferedHttpEntity", "(HttpEntity)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "ByteArrayEntity", True, "ByteArrayEntity", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "EntityUtils", True, "parse", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "EntityUtils", True, "toByteArray", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
@@ -14,6 +14,6 @@ extensions:
- ["org.apache.hc.core5.http.io.entity", "HttpEntities", True, "createUrlEncoded", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "HttpEntities", True, "gzip", "(HttpEntity)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "HttpEntities", True, "withTrailers", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "HttpEntityWrapper", True, "HttpEntityWrapper", "(HttpEntity)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "InputStreamEntity", True, "InputStreamEntity", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "HttpEntityWrapper", True, "HttpEntityWrapper", "(HttpEntity)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "InputStreamEntity", True, "InputStreamEntity", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.hc.core5.http.io.entity", "StringEntity", True, "StringEntity", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]

View File

@@ -4,8 +4,8 @@ extensions:
extensible: summaryModel
data:
- ["org.apache.http.entity", "BasicHttpEntity", True, "setContent", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.http.entity", "BufferedHttpEntity", True, "BufferedHttpEntity", "(HttpEntity)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.http.entity", "BufferedHttpEntity", True, "BufferedHttpEntity", "(HttpEntity)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.http.entity", "ByteArrayEntity", True, "ByteArrayEntity", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.http.entity", "HttpEntityWrapper", True, "HttpEntityWrapper", "(HttpEntity)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.http.entity", "InputStreamEntity", True, "InputStreamEntity", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["org.apache.http.entity", "HttpEntityWrapper", True, "HttpEntityWrapper", "(HttpEntity)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.http.entity", "InputStreamEntity", True, "InputStreamEntity", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.apache.http.entity", "StringEntity", True, "StringEntity", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]

View File

@@ -21,7 +21,7 @@ external int selectedSourceColumn();
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
module ViewCfgQueryInput implements ViewCfgQueryInputSig<File> {
module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig<File> {
predicate selectedSourceFile = selectedSourceFileAlias/0;
predicate selectedSourceLine = selectedSourceLineAlias/0;
@@ -42,4 +42,4 @@ module ViewCfgQueryInput implements ViewCfgQueryInputSig<File> {
}
}
import ViewCfgQuery<File, ViewCfgQueryInput>
import ControlFlow::ViewCfgQuery<File, ViewCfgQueryInput>

View File

@@ -1,96 +0,0 @@
/**
* Provides classes and predicates for representing completions.
*/
overlay[local?]
module;
/*
* A completion represents how a statement or expression terminates.
*
* There are five kinds of completions: normal completion,
* `return` completion, `break` completion,
* `continue` completion, and `throw` completion.
*
* Normal completions are further subdivided into boolean completions and all
* other normal completions. A boolean completion adds the information that the
* cfg node terminated with the given boolean value due to a subexpression
* terminating with the other given boolean value. This is only
* relevant for conditional contexts in which the value controls the
* control-flow successor.
*/
import java
/**
* A label of a `LabeledStmt`.
*/
newtype Label = MkLabel(string l) { exists(LabeledStmt lbl | l = lbl.getLabel()) }
/**
* Either a `Label` or nothing.
*/
newtype MaybeLabel =
JustLabel(Label l) or
NoLabel()
/**
* A completion of a statement or an expression.
*/
newtype Completion =
/**
* The statement or expression completes normally and continues to the next statement.
*/
NormalCompletion() or
/**
* The statement or expression completes by returning from the function.
*/
ReturnCompletion() or
/**
* The expression completes with value `outerValue` overall and with the last control
* flow node having value `innerValue`.
*/
BooleanCompletion(boolean outerValue, boolean innerValue) {
(outerValue = true or outerValue = false) and
(innerValue = true or innerValue = false)
} or
/**
* The expression or statement completes via a `break` statement.
*/
BreakCompletion(MaybeLabel l) or
/**
* The expression or statement completes via a `yield` statement.
*/
YieldCompletion(NormalOrBooleanCompletion c) or
/**
* The expression or statement completes via a `continue` statement.
*/
ContinueCompletion(MaybeLabel l) or
/**
* The expression or statement completes by throwing a `ThrowableType`.
*/
ThrowCompletion(ThrowableType tt)
/** A completion that is either a `NormalCompletion` or a `BooleanCompletion`. */
class NormalOrBooleanCompletion extends Completion {
NormalOrBooleanCompletion() {
this instanceof NormalCompletion or this instanceof BooleanCompletion
}
/** Gets a textual representation of this completion. */
string toString() { result = "completion" }
}
/** Gets the completion `ContinueCompletion(NoLabel())`. */
ContinueCompletion anonymousContinueCompletion() { result = ContinueCompletion(NoLabel()) }
/** Gets the completion `ContinueCompletion(JustLabel(l))`. */
ContinueCompletion labelledContinueCompletion(Label l) { result = ContinueCompletion(JustLabel(l)) }
/** Gets the completion `BreakCompletion(NoLabel())`. */
BreakCompletion anonymousBreakCompletion() { result = BreakCompletion(NoLabel()) }
/** Gets the completion `BreakCompletion(JustLabel(l))`. */
BreakCompletion labelledBreakCompletion(Label l) { result = BreakCompletion(JustLabel(l)) }
/** Gets the completion `BooleanCompletion(value, value)`. */
Completion basicBooleanCompletion(boolean value) { result = BooleanCompletion(value, value) }

File diff suppressed because it is too large Load Diff

View File

@@ -392,7 +392,7 @@ class ArrayInit extends Expr, @arrayinit {
* element assignments since there the assignment destination is not directly
* the array variable but instead an `ArrayAccess`.
*/
class Assignment extends Expr, @assignment {
class Assignment extends BinaryExpr, @assignment {
/** Gets the destination (left-hand side) of the assignment. */
Expr getDest() { result.isNthChildOf(this, 0) }
@@ -417,6 +417,8 @@ class Assignment extends Expr, @assignment {
* For example, `x = 23`.
*/
class AssignExpr extends Assignment, @assignexpr {
override string getOp() { result = "=" }
override string getAPrimaryQlClass() { result = "AssignExpr" }
}
@@ -445,7 +447,7 @@ class AssignOp extends Assignment, @assignop {
override Expr getSource() { result.getParent() = this }
/** Gets a string representation of the assignment operator of this compound assignment. */
/*abstract*/ string getOp() { result = "??=" }
/*abstract*/ override string getOp() { result = "??=" }
/** Gets a printable representation of this expression. */
override string toString() { result = "..." + this.getOp() + "..." }
@@ -739,155 +741,155 @@ class BinaryExpr extends Expr, @binaryexpr {
}
/** Gets a printable representation of this expression. */
override string toString() { result = "..." + this.getOp() + "..." }
override string toString() { result = "... " + this.getOp() + " ..." }
/** Gets a string representation of the operator of this binary expression. */
/*abstract*/ string getOp() { result = " ?? " }
/*abstract*/ string getOp() { result = "??" }
}
/** A binary expression using the `*` operator. */
class MulExpr extends BinaryExpr, @mulexpr {
override string getOp() { result = " * " }
override string getOp() { result = "*" }
override string getAPrimaryQlClass() { result = "MulExpr" }
}
/** A binary expression using the `/` operator. */
class DivExpr extends BinaryExpr, @divexpr {
override string getOp() { result = " / " }
override string getOp() { result = "/" }
override string getAPrimaryQlClass() { result = "DivExpr" }
}
/** A binary expression using the `%` operator. */
class RemExpr extends BinaryExpr, @remexpr {
override string getOp() { result = " % " }
override string getOp() { result = "%" }
override string getAPrimaryQlClass() { result = "RemExpr" }
}
/** A binary expression using the `+` operator. */
class AddExpr extends BinaryExpr, @addexpr {
override string getOp() { result = " + " }
override string getOp() { result = "+" }
override string getAPrimaryQlClass() { result = "AddExpr" }
}
/** A binary expression using the `-` operator. */
class SubExpr extends BinaryExpr, @subexpr {
override string getOp() { result = " - " }
override string getOp() { result = "-" }
override string getAPrimaryQlClass() { result = "SubExpr" }
}
/** A binary expression using the `<<` operator. */
class LeftShiftExpr extends BinaryExpr, @lshiftexpr {
override string getOp() { result = " << " }
override string getOp() { result = "<<" }
override string getAPrimaryQlClass() { result = "LeftShiftExpr" }
}
/** A binary expression using the `>>` operator. */
class RightShiftExpr extends BinaryExpr, @rshiftexpr {
override string getOp() { result = " >> " }
override string getOp() { result = ">>" }
override string getAPrimaryQlClass() { result = "RightShiftExpr" }
}
/** A binary expression using the `>>>` operator. */
class UnsignedRightShiftExpr extends BinaryExpr, @urshiftexpr {
override string getOp() { result = " >>> " }
override string getOp() { result = ">>>" }
override string getAPrimaryQlClass() { result = "UnsignedRightShiftExpr" }
}
/** A binary expression using the `&` operator. */
class AndBitwiseExpr extends BinaryExpr, @andbitexpr {
override string getOp() { result = " & " }
override string getOp() { result = "&" }
override string getAPrimaryQlClass() { result = "AndBitwiseExpr" }
}
/** A binary expression using the `|` operator. */
class OrBitwiseExpr extends BinaryExpr, @orbitexpr {
override string getOp() { result = " | " }
override string getOp() { result = "|" }
override string getAPrimaryQlClass() { result = "OrBitwiseExpr" }
}
/** A binary expression using the `^` operator. */
class XorBitwiseExpr extends BinaryExpr, @xorbitexpr {
override string getOp() { result = " ^ " }
override string getOp() { result = "^" }
override string getAPrimaryQlClass() { result = "XorBitwiseExpr" }
}
/** A binary expression using the `&&` operator. */
class AndLogicalExpr extends BinaryExpr, @andlogicalexpr {
override string getOp() { result = " && " }
override string getOp() { result = "&&" }
override string getAPrimaryQlClass() { result = "AndLogicalExpr" }
}
/** A binary expression using the `||` operator. */
class OrLogicalExpr extends BinaryExpr, @orlogicalexpr {
override string getOp() { result = " || " }
override string getOp() { result = "||" }
override string getAPrimaryQlClass() { result = "OrLogicalExpr" }
}
/** A binary expression using the `<` operator. */
class LTExpr extends BinaryExpr, @ltexpr {
override string getOp() { result = " < " }
override string getOp() { result = "<" }
override string getAPrimaryQlClass() { result = "LTExpr" }
}
/** A binary expression using the `>` operator. */
class GTExpr extends BinaryExpr, @gtexpr {
override string getOp() { result = " > " }
override string getOp() { result = ">" }
override string getAPrimaryQlClass() { result = "GTExpr" }
}
/** A binary expression using the `<=` operator. */
class LEExpr extends BinaryExpr, @leexpr {
override string getOp() { result = " <= " }
override string getOp() { result = "<=" }
override string getAPrimaryQlClass() { result = "LEExpr" }
}
/** A binary expression using the `>=` operator. */
class GEExpr extends BinaryExpr, @geexpr {
override string getOp() { result = " >= " }
override string getOp() { result = ">=" }
override string getAPrimaryQlClass() { result = "GEExpr" }
}
/** A binary expression using Java's `==` or Kotlin's `===` operator. */
class EQExpr extends BinaryExpr, @eqexpr {
override string getOp() { result = " == " }
override string getOp() { result = "==" }
override string getAPrimaryQlClass() { result = "EQExpr" }
}
/** A binary expression using the Kotlin `==` operator, semantically equivalent to `Objects.equals`. */
class ValueEQExpr extends BinaryExpr, @valueeqexpr {
override string getOp() { result = " (value equals) " }
override string getOp() { result = "(value equals)" }
override string getAPrimaryQlClass() { result = "ValueEQExpr" }
}
/** A binary expression using Java's `!=` or Kotlin's `!==` operator. */
class NEExpr extends BinaryExpr, @neexpr {
override string getOp() { result = " != " }
override string getOp() { result = "!=" }
override string getAPrimaryQlClass() { result = "NEExpr" }
}
/** A binary expression using the Kotlin `!=` operator, semantically equivalent to `Objects.equals`. */
class ValueNEExpr extends BinaryExpr, @valueneexpr {
override string getOp() { result = " (value not-equals) " }
override string getOp() { result = "(value not-equals)" }
override string getAPrimaryQlClass() { result = "ValueNEExpr" }
}
@@ -1245,6 +1247,9 @@ class ClassInstanceExpr extends Expr, ConstructorCall, @classinstancexpr {
/** Gets the immediately enclosing statement of this class instance creation expression. */
override Stmt getEnclosingStmt() { result = Expr.super.getEnclosingStmt() }
/** Gets the `ControlFlowNode` corresponding to this call. */
override ControlFlowNode getControlFlowNode() { result = Expr.super.getControlFlowNode() }
/** Gets a printable representation of this expression. */
override string toString() {
result = "new " + this.getConstructor().getName() + "(...)"
@@ -2113,6 +2118,9 @@ class MethodCall extends Expr, Call, @methodaccess {
/** Gets the immediately enclosing statement that contains this method access. */
override Stmt getEnclosingStmt() { result = Expr.super.getEnclosingStmt() }
/** Gets the `ControlFlowNode` corresponding to this call. */
override ControlFlowNode getControlFlowNode() { result = Expr.super.getControlFlowNode() }
/** Gets a printable representation of this expression. */
override string toString() {
if exists(this.getMethod())
@@ -2305,6 +2313,9 @@ class Call extends ExprParent, @caller {
/** Gets the enclosing statement of this call. */
/*abstract*/ Stmt getEnclosingStmt() { none() }
/** Gets the `ControlFlowNode` corresponding to this call. */
/*abstract*/ ControlFlowNode getControlFlowNode() { none() }
/** Gets the number of arguments provided in this call. */
int getNumArgument() { count(this.getAnArgument()) = result }

View File

@@ -207,29 +207,12 @@ private class PpArrayInit extends PpAst, ArrayInit {
override PpAst getChild(int i) { exists(int j | result = this.getInit(j) and i = 1 + 2 * j) }
}
private class PpAssignment extends PpAst, Assignment {
override string getPart(int i) {
i = 1 and
this instanceof AssignExpr and
result = " = "
or
i = 1 and
result = " " + this.(AssignOp).getOp() + " "
}
override PpAst getChild(int i) {
i = 0 and result = this.getDest()
or
i = 2 and result = this.getRhs()
}
}
private class PpLiteral extends PpAst, Literal {
override string getPart(int i) { i = 0 and result = this.getLiteral() }
}
private class PpBinaryExpr extends PpAst, BinaryExpr {
override string getPart(int i) { i = 1 and result = this.getOp() }
override string getPart(int i) { i = 1 and result = " " + this.getOp() + " " }
override PpAst getChild(int i) {
i = 0 and result = this.getLeftOperand()

View File

@@ -61,7 +61,7 @@ class Stmt extends StmtParent, ExprParent, @stmt {
}
/** A statement parent is any element that can have a statement as its child. */
class StmtParent extends @stmtparent, Top { }
class StmtParent extends @stmtparent, ExprParent { }
/**
* An error statement.
@@ -960,6 +960,9 @@ class ThisConstructorInvocationStmt extends Stmt, ConstructorCall, @constructori
/** Gets the immediately enclosing statement of this constructor invocation. */
override Stmt getEnclosingStmt() { result = this }
/** Gets the `ControlFlowNode` corresponding to this call. */
override ControlFlowNode getControlFlowNode() { result = Stmt.super.getControlFlowNode() }
override string pp() { result = "this(...)" }
override string toString() { result = "this(...)" }
@@ -1001,6 +1004,9 @@ class SuperConstructorInvocationStmt extends Stmt, ConstructorCall, @superconstr
/** Gets the immediately enclosing statement of this constructor invocation. */
override Stmt getEnclosingStmt() { result = this }
/** Gets the `ControlFlowNode` corresponding to this call. */
override ControlFlowNode getControlFlowNode() { result = Stmt.super.getControlFlowNode() }
override string pp() { result = "super(...)" }
override string toString() { result = "super(...)" }

View File

@@ -93,8 +93,7 @@ class ArithExpr extends Expr {
) and
forall(Expr e |
e = this.(BinaryExpr).getAnOperand() or
e = this.(UnaryAssignExpr).getOperand() or
e = this.(AssignOp).getSource()
e = this.(UnaryAssignExpr).getOperand()
|
e.getType() instanceof NumType
)
@@ -114,21 +113,17 @@ class ArithExpr extends Expr {
*/
Expr getLeftOperand() {
result = this.(BinaryExpr).getLeftOperand() or
result = this.(UnaryAssignExpr).getOperand() or
result = this.(AssignOp).getDest()
result = this.(UnaryAssignExpr).getOperand()
}
/**
* Gets the right-hand operand if this is a binary expression.
*/
Expr getRightOperand() {
result = this.(BinaryExpr).getRightOperand() or result = this.(AssignOp).getRhs()
}
Expr getRightOperand() { result = this.(BinaryExpr).getRightOperand() }
/** Gets an operand of this arithmetic expression. */
Expr getAnOperand() {
result = this.(BinaryExpr).getAnOperand() or
result = this.(UnaryAssignExpr).getOperand() or
result = this.(AssignOp).getSource()
result = this.(UnaryAssignExpr).getOperand()
}
}

View File

@@ -6,151 +6,8 @@ module;
import java
import Dominance
private import codeql.controlflow.BasicBlock as BB
private import codeql.controlflow.SuccessorType
private module Input implements BB::InputSig<Location> {
/** Hold if `t` represents a conditional successor type. */
predicate successorTypeIsCondition(SuccessorType t) { none() }
/** A delineated part of the AST with its own CFG. */
class CfgScope = Callable;
/** The class of control flow nodes. */
class Node = ControlFlowNode;
/** Gets the CFG scope in which this node occurs. */
CfgScope nodeGetCfgScope(Node node) { node.getEnclosingCallable() = result }
/** Gets an immediate successor of this node. */
Node nodeGetASuccessor(Node node, SuccessorType t) { result = node.getASuccessor(t) }
/**
* Holds if `node` represents an entry node to be used when calculating
* dominance.
*/
predicate nodeIsDominanceEntry(Node node) {
exists(Stmt entrystmt | entrystmt = node.asStmt() |
exists(Callable c | entrystmt = c.getBody())
or
// This disjunct is technically superfluous, but safeguards against extractor problems.
entrystmt instanceof BlockStmt and
not exists(entrystmt.getEnclosingCallable()) and
not entrystmt.getParent() instanceof Stmt
)
}
/**
* Holds if `node` represents an exit node to be used when calculating
* post dominance.
*/
predicate nodeIsPostDominanceExit(Node node) { node instanceof ControlFlow::NormalExitNode }
}
private module BbImpl = BB::Make<Location, Input>;
import BbImpl
/** Holds if the dominance relation is calculated for `bb`. */
predicate hasDominanceInformation(BasicBlock bb) {
exists(BasicBlock entry |
Input::nodeIsDominanceEntry(entry.getFirstNode()) and entry.getASuccessor*() = bb
)
}
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock extends BbImpl::BasicBlock {
/** Gets the immediately enclosing callable whose body contains this node. */
Callable getEnclosingCallable() { result = this.getScope() }
/**
* Holds if this basic block dominates basic block `bb`.
*
* That is, all paths reaching `bb` from the entry point basic block must
* go through this basic block.
*/
predicate dominates(BasicBlock bb) { super.dominates(bb) }
/**
* Holds if this basic block strictly dominates basic block `bb`.
*
* That is, all paths reaching `bb` from the entry point basic block must
* go through this basic block and this basic block is different from `bb`.
*/
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor(t) }
BasicBlock getASuccessor() { result = super.getASuccessor() }
BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
/**
* DEPRECATED: Use `getASuccessor` instead.
*
* Gets an immediate successor of this basic block.
*/
deprecated BasicBlock getABBSuccessor() { result = this.getASuccessor() }
/**
* DEPRECATED: Use `getAPredecessor` instead.
*
* Gets an immediate predecessor of this basic block.
*/
deprecated BasicBlock getABBPredecessor() { result.getASuccessor() = this }
/**
* DEPRECATED: Use `strictlyDominates` instead.
*
* Holds if this basic block strictly dominates `node`.
*/
deprecated predicate bbStrictlyDominates(BasicBlock node) { this.strictlyDominates(node) }
/**
* DEPRECATED: Use `dominates` instead.
*
* Holds if this basic block dominates `node`. (This is reflexive.)
*/
deprecated predicate bbDominates(BasicBlock node) { this.dominates(node) }
/**
* DEPRECATED: Use `strictlyPostDominates` instead.
*
* Holds if this basic block strictly post-dominates `node`.
*/
deprecated predicate bbStrictlyPostDominates(BasicBlock node) { this.strictlyPostDominates(node) }
/**
* DEPRECATED: Use `postDominates` instead.
*
* Holds if this basic block post-dominates `node`. (This is reflexive.)
*/
deprecated predicate bbPostDominates(BasicBlock node) { this.postDominates(node) }
}
/** A basic block that ends in an exit node. */
class ExitBlock extends BasicBlock {
ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode }
}
private class BasicBlockAlias = BasicBlock;
module Cfg implements BB::CfgSig<Location> {
class ControlFlowNode = BbImpl::ControlFlowNode;
class BasicBlock = BasicBlockAlias;
class EntryBasicBlock extends BasicBlock instanceof BbImpl::EntryBasicBlock { }
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BbImpl::dominatingEdge(bb1, bb2) }
}

View File

@@ -10,6 +10,7 @@ private import semmle.code.java.controlflow.Dominance
private import semmle.code.java.controlflow.internal.Preconditions
private import semmle.code.java.controlflow.internal.SwitchCases
private import codeql.controlflow.Guards as SharedGuards
private import codeql.controlflow.SuccessorType
/**
* A basic block that terminates in a condition, splitting the subsequent control flow.
@@ -75,70 +76,6 @@ class ConditionBlock extends BasicBlock {
}
}
// Join order engineering -- first determine the switch block and the case indices required, then retrieve them.
bindingset[switch, i]
pragma[inline_late]
private predicate isNthCaseOf(SwitchBlock switch, SwitchCase c, int i) { c.isNthCaseOf(switch, i) }
/**
* Gets a switch case >= pred, up to but not including `pred`'s successor pattern case,
* where `pred` is declared on `switch`.
*/
private SwitchCase getACaseUpToNextPattern(PatternCase pred, SwitchBlock switch) {
// Note we do include `case null, default` (as well as plain old `default`) here.
not result.(ConstCase).getValue(_) instanceof NullLiteral and
exists(int maxCaseIndex |
switch = pred.getParent() and
if exists(getNextPatternCase(pred))
then maxCaseIndex = getNextPatternCase(pred).getCaseIndex() - 1
else maxCaseIndex = lastCaseIndex(switch)
|
isNthCaseOf(switch, result, [pred.getCaseIndex() .. maxCaseIndex])
)
}
/**
* Gets the closest pattern case preceding `case`, including `case` itself, if any.
*/
private PatternCase getClosestPrecedingPatternCase(SwitchCase case) {
case = getACaseUpToNextPattern(result, _)
}
/**
* Holds if `pred` is a control-flow predecessor of switch case `sc` that is not a
* fall-through from a previous case.
*
* For classic switches that means flow from the selector expression; for switches
* involving pattern cases it can also mean flow from a previous pattern case's type
* test or guard failing and proceeding to then consider subsequent cases.
*/
private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pred) {
pred = sc.getControlFlowNode().getAPredecessor() and
(
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
// will test if the selector equals `"SomeConstant"` and if so branch to the same
// case statement. Therefore don't label this a non-fall-through predecessor.
exists(PatternCase previousPatternCase |
previousPatternCase = getClosestPrecedingPatternCase(sc)
|
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.
(
previousPatternCase.isRule() or
not previousPatternCase.getIndex() = sc.getIndex() - 1
)
)
or
// Unambigious: on the test-passing edge there must be at least one intervening
// declaration node, including anonymous `_` declarations.
pred.asStmt() = getClosestPrecedingPatternCase(sc)
)
}
private module GuardsInput implements SharedGuards::InputSig<Location, ControlFlowNode, BasicBlock> {
private import java as J
private import semmle.code.java.dataflow.internal.BaseSSA as Base
@@ -231,39 +168,18 @@ private module GuardsInput implements SharedGuards::InputSig<Location, ControlFl
)
}
private predicate hasPatternCaseMatchEdge(BasicBlock bb1, BasicBlock bb2, boolean isMatch) {
exists(ConditionNode patterncase |
this instanceof PatternCase and
patterncase = super.getControlFlowNode() and
bb1.getLastNode() = patterncase and
bb2.getFirstNode() = patterncase.getABranchSuccessor(isMatch)
)
}
predicate matchEdge(BasicBlock bb1, BasicBlock bb2) {
exists(ControlFlowNode pred |
// Pattern cases are handled as ConditionBlocks.
not this instanceof PatternCase and
bb2.getFirstNode() = super.getControlFlowNode() and
isNonFallThroughPredecessor(this, pred) and
bb1 = pred.getBasicBlock()
)
or
this.hasPatternCaseMatchEdge(bb1, bb2, true)
bb1.getASuccessor(any(MatchingSuccessor s | s.getValue() = true)) = bb2 and
bb1.getLastNode() = super.getControlFlowNode()
}
predicate nonMatchEdge(BasicBlock bb1, BasicBlock bb2) {
this.hasPatternCaseMatchEdge(bb1, bb2, false)
bb1.getASuccessor(any(MatchingSuccessor s | s.getValue() = false)) = bb2 and
bb1.getLastNode() = super.getControlFlowNode()
}
}
abstract private class BinExpr extends Expr {
Expr getAnOperand() {
result = this.(BinaryExpr).getAnOperand() or result = this.(AssignOp).getSource()
}
}
class AndExpr extends BinExpr {
class AndExpr extends BinaryExpr {
AndExpr() {
this instanceof AndBitwiseExpr or
this instanceof AndLogicalExpr or
@@ -271,7 +187,7 @@ private module GuardsInput implements SharedGuards::InputSig<Location, ControlFl
}
}
class OrExpr extends BinExpr {
class OrExpr extends BinaryExpr {
OrExpr() {
this instanceof OrBitwiseExpr or
this instanceof OrLogicalExpr or

View File

@@ -34,7 +34,7 @@ abstract class ActionConfiguration extends string {
private BasicBlock actionBlock(ActionConfiguration conf) {
exists(ControlFlowNode node | result = node.getBasicBlock() |
conf.isAction(node) or
callAlwaysPerformsAction(node.asCall(), conf)
callAlwaysPerformsAction(any(Call call | call.getControlFlowNode() = node), conf)
)
}
@@ -66,10 +66,6 @@ private class JoinBlock extends BasicBlock {
JoinBlock() { 2 <= strictcount(this.getAPredecessor()) }
}
private class ReachableBlock extends BasicBlock {
ReachableBlock() { hasDominanceInformation(this) }
}
/**
* Holds if `bb` is a block that is collectively dominated by a set of one or
* more actions that individually does not dominate the exit.
@@ -78,7 +74,7 @@ private predicate postActionBlock(BasicBlock bb, ActionConfiguration conf) {
bb = nonDominatingActionBlock(conf)
or
if bb instanceof JoinBlock
then forall(ReachableBlock pred | pred = bb.getAPredecessor() | postActionBlock(pred, conf))
then forall(BasicBlock pred | pred = bb.getAPredecessor() | postActionBlock(pred, conf))
else postActionBlock(bb.getAPredecessor(), conf)
}

View File

@@ -367,12 +367,24 @@ module ModelValidation {
)
}
string getIncorrectConstructorSummaryOutput() {
exists(string namespace, string type, string name, string output |
summaryModel(namespace, type, _, name, _, _, _, output, _, _, _)
|
type = name and
output.matches("ReturnValue%") and
result =
"Constructor model for " + namespace + "." + type +
" should use `Argument[this]` in the output, not `ReturnValue`."
)
}
/** Holds if some row in a MaD flow model appears to contain typos. */
query predicate invalidModelRow(string msg) {
msg =
[
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
KindVal::getInvalidModelKind()
getIncorrectConstructorSummaryOutput(), KindVal::getInvalidModelKind()
]
}
}

View File

@@ -229,7 +229,7 @@ class InstanceAccessExt extends TInstanceAccessExt {
/** Gets the control flow node associated with this instance access. */
ControlFlowNode getCfgNode() {
exists(ExprParent e | e = this.getAssociatedExprOrStmt() |
result.asCall() = e
result = e.(Call).getControlFlowNode()
or
e.(InstanceAccess).getControlFlowNode() = result
or

View File

@@ -17,6 +17,7 @@ private import NullGuards
private import semmle.code.java.Collections
private import semmle.code.java.controlflow.internal.Preconditions
private import semmle.code.java.controlflow.ControlFlowReachability
private import codeql.controlflow.SuccessorType
/** Gets an expression that may be `null`. */
Expr nullExpr() { result = nullExpr(_) }
@@ -52,8 +53,6 @@ private predicate unboxed(Expr e) {
assign.getDest().getType() instanceof PrimitiveType and assign.getSource() = e
)
or
exists(AssignOp assign | assign.getSource() = e and assign.getType() instanceof PrimitiveType)
or
exists(EqualityTest eq |
eq.getAnOperand() = e and eq.getAnOperand().getType() instanceof PrimitiveType
)
@@ -61,6 +60,7 @@ private predicate unboxed(Expr e) {
exists(BinaryExpr bin |
bin.getAnOperand() = e and
not bin instanceof EqualityTest and
not bin instanceof AssignExpr and
bin.getType() instanceof PrimitiveType
)
or
@@ -230,14 +230,8 @@ private Expr nonEmptyExpr() {
/** The control flow edge that exits an enhanced for loop if the `Iterable` is empty. */
private predicate enhancedForEarlyExit(EnhancedForStmt for, ControlFlowNode n1, ControlFlowNode n2) {
exists(Expr forExpr |
n1.getANormalSuccessor() = n2 and
for.getExpr() = forExpr and
forExpr.getAChildExpr*() = n1.asExpr() and
not forExpr.getAChildExpr*() = n2.asExpr() and
n1.getANormalSuccessor().asExpr() = for.getVariable() and
not n2.asExpr() = for.getVariable()
)
n1.getASuccessor(any(EmptinessSuccessor t | t.isEmpty())) = n2 and
for.getExpr().getControlFlowNode() = n1
}
/** A control flow edge that cannot be taken. */

View File

@@ -86,23 +86,7 @@ module Sem implements Semantic<Location> {
class ConstantIntegerExpr = RU::ConstantIntegerExpr;
abstract class BinaryExpr extends Expr {
Expr getLeftOperand() {
result = this.(J::BinaryExpr).getLeftOperand() or result = this.(J::AssignOp).getDest()
}
Expr getRightOperand() {
result = this.(J::BinaryExpr).getRightOperand() or result = this.(J::AssignOp).getRhs()
}
final Expr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() }
final predicate hasOperands(Expr e1, Expr e2) {
this.getLeftOperand() = e1 and this.getRightOperand() = e2
or
this.getLeftOperand() = e2 and this.getRightOperand() = e1
}
}
class BinaryExpr = J::BinaryExpr;
class AddExpr extends BinaryExpr {
AddExpr() { this instanceof J::AddExpr or this instanceof J::AssignAddExpr }
@@ -220,6 +204,8 @@ module Sem implements Semantic<Location> {
int getBlockId1(BasicBlock bb) { idOf(bb, result) }
string getBlockId2(BasicBlock bb) { bb.getFirstNode().getIdTag() = result }
class Guard extends G::Guards_v2::Guard {
Expr asExpr() { result = this }
}

View File

@@ -326,20 +326,45 @@ private module Input implements TypeFlowInput<Location> {
)
}
/**
* Holds if `ioe` checks `v`, its true-successor is `bb`, and `bb` has multiple
* predecessors.
*/
private predicate instanceofDisjunct(InstanceOfExpr ioe, BasicBlock bb, Base::SsaDefinition v) {
/** Holds if `ioe` checks `v` and its true-successor is `bb`. */
private predicate instanceofTrueSuccessor(InstanceOfExpr ioe, BasicBlock bb, Base::SsaDefinition v) {
ioe.getExpr() = v.getARead() and
strictcount(bb.getAPredecessor()) > 1 and
exists(ConditionBlock cb | cb.getCondition() = ioe and cb.getTestSuccessor(true) = bb)
}
/** Holds if `bb` is disjunctively guarded by multiple `instanceof` tests on `v`. */
private predicate instanceofDisjunction(BasicBlock bb, Base::SsaDefinition v) {
strictcount(InstanceOfExpr ioe | instanceofDisjunct(ioe, bb, v)) =
strictcount(bb.getAPredecessor())
/**
* Holds if `bb` is disjunctively guarded by one (`trivial = true`) or more
* (`trivial = false`) `instanceof` tests on `v`.
*/
private predicate instanceofDisjunction(BasicBlock bb, Base::SsaDefinition v, boolean trivial) {
exists(int preds |
strictcount(bb.getAPredecessor()) = preds and
strictcount(InstanceOfExpr ioe | instanceofTrueSuccessor(ioe, bb, v)) = preds and
if preds > 1 then trivial = false else trivial = true
)
or
strictcount(bb.getAPredecessor()) = 2 and
exists(BasicBlock pred1, BasicBlock pred2 |
pred1 != pred2 and
pred1 = bb.getAPredecessor() and
pred2 = bb.getAPredecessor() and
instanceofDisjunction(pred1, v, _) and
instanceofDisjunction(pred2, v, _) and
trivial = false
)
}
/**
* Holds if `bb` is disjunctively guarded by one or more `instanceof` tests
* on `v`, and `ioe` is one of those tests.
*/
private predicate instanceofDisjunct(InstanceOfExpr ioe, BasicBlock bb, Base::SsaDefinition v) {
instanceofDisjunction(bb, v, _) and
(
instanceofTrueSuccessor(ioe, bb, v)
or
exists(BasicBlock pred | pred = bb.getAPredecessor() and instanceofDisjunct(ioe, pred, v))
)
}
/**
@@ -348,7 +373,7 @@ private module Input implements TypeFlowInput<Location> {
*/
predicate instanceofDisjunctionGuarded(TypeFlowNode n, RefType t) {
exists(BasicBlock bb, InstanceOfExpr ioe, Base::SsaDefinition v, VarAccess va |
instanceofDisjunction(bb, v) and
instanceofDisjunction(bb, v, false) and
bb.dominates(va.getBasicBlock()) and
va = v.getARead() and
instanceofDisjunct(ioe, bb, v) and

View File

@@ -93,8 +93,7 @@ private module BaseSsaImpl {
/** Holds if `n` updates the local variable `v`. */
predicate variableUpdate(BaseSsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) {
exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and
b.getNode(i) = n and
hasDominanceInformation(b)
b.getNode(i) = n
}
/** Gets the definition point of a nested class in the parent scope. */
@@ -132,7 +131,7 @@ private module BaseSsaImpl {
inner != outer and
inner.getDeclaringType() = innerclass and
result = parentDef(desugaredGetEnclosingType*(innerclass)) and
result.getEnclosingStmt().getEnclosingCallable() = outer and
result.getEnclosingCallable() = outer and
capturedvar = TLocalVar(outer, v) and
closurevar = TLocalVar(inner, v)
)
@@ -178,15 +177,12 @@ private module SsaImplInput implements SsaImplCommon::InputSig<Location, BasicBl
* Holds if the `i`th of basic block `bb` reads source variable `v`.
*/
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
hasDominanceInformation(bb) and
(
exists(VarRead use |
v.getAnAccess() = use and bb.getNode(i) = use.getControlFlowNode() and certain = true
)
or
variableCapture(v, _, bb, i) and
certain = false
exists(VarRead use |
v.getAnAccess() = use and bb.getNode(i) = use.getControlFlowNode() and certain = true
)
or
variableCapture(v, _, bb, i) and
certain = false
}
}

View File

@@ -115,7 +115,7 @@ private ControlFlowNode captureNode(TrackedVar capturedvar, TrackedVar closureva
inner != outer and
inner.getDeclaringType() = innerclass and
result = parentDef(desugaredGetEnclosingType*(innerclass)) and
result.getEnclosingStmt().getEnclosingCallable() = outer and
result.getEnclosingCallable() = outer and
capturedvar = TLocalVar(outer, v) and
closurevar = TLocalVar(inner, v)
)
@@ -130,8 +130,7 @@ private predicate variableCapture(TrackedVar capturedvar, TrackedVar closurevar,
pragma[nomagic]
private predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and
b.getNode(i) = n and
hasDominanceInformation(b)
b.getNode(i) = n
or
certainVariableUpdate(v.getQualifier(), n, b, i)
}
@@ -153,9 +152,8 @@ private predicate hasEntryDef(TrackedVar v, BasicBlock b) {
overlay[global]
pragma[nomagic]
private predicate uncertainVariableUpdateImpl(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
b.getNode(i) = n and
hasDominanceInformation(b)
exists(Call c | c.getControlFlowNode() = n | updatesNamedField(c, v, _)) and
b.getNode(i) = n
or
uncertainVariableUpdateImpl(v.getQualifier(), n, b, i)
}
@@ -191,18 +189,15 @@ private module SsaImplInput implements SsaImplCommon::InputSig<Location, BasicBl
* This includes implicit reads via calls.
*/
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
hasDominanceInformation(bb) and
(
exists(VarRead use |
v instanceof TrackedVar and
v.getAnAccess() = use and
bb.getNode(i) = use.getControlFlowNode() and
certain = true
)
or
variableCapture(v, _, bb, i) and
certain = false
exists(VarRead use |
v instanceof TrackedVar and
v.getAnAccess() = use and
bb.getNode(i) = use.getControlFlowNode() and
certain = true
)
or
variableCapture(v, _, bb, i) and
certain = false
}
}
@@ -525,8 +520,11 @@ private module Cached {
overlay[global]
cached
predicate defUpdatesNamedField(SsaImplicitWrite calldef, TrackedField f, Callable setter) {
f = calldef.getSourceVariable() and
updatesNamedField0(calldef.getControlFlowNode().asCall(), f, setter)
exists(Call call |
f = calldef.getSourceVariable() and
call.getControlFlowNode() = calldef.getControlFlowNode() and
updatesNamedField0(call, f, setter)
)
}
/** Holds if `init` is a closure variable that captures the value of `capturedvar`. */

View File

@@ -161,13 +161,9 @@ module Private {
this instanceof J::AssignUnsignedRightShiftExpr and result = TUnsignedRightShiftOp()
}
Expr getLeftOperand() {
result = this.(J::BinaryExpr).getLeftOperand() or result = this.(J::AssignOp).getDest()
}
Expr getLeftOperand() { result = this.(J::BinaryExpr).getLeftOperand() }
Expr getRightOperand() {
result = this.(J::BinaryExpr).getRightOperand() or result = this.(J::AssignOp).getRhs()
}
Expr getRightOperand() { result = this.(J::BinaryExpr).getRightOperand() }
}
predicate ssaRead = RU::ssaRead/2;

View File

@@ -23,7 +23,9 @@ private predicate idOfAst(BB::ExprParent x, int y) = equivalenceRelation(id/2)(x
private predicate idOf(BasicBlock x, int y) { idOfAst(x.getFirstNode().getAstNode(), y) }
private int getId(BasicBlock bb) { idOf(bb, result) }
private int getId1(BasicBlock bb) { idOf(bb, result) }
private string getId2(BasicBlock bb) { bb.getFirstNode().getIdTag() = result }
/**
* Declarations to be exposed to users of SsaReadPositionCommon
@@ -39,7 +41,7 @@ module Public {
rank[r](SsaReadPositionPhiInputEdge e |
e.phiInput(phi, _)
|
e order by getId(e.getOrigBlock())
e order by getId1(e.getOrigBlock()), getId2(e.getOrigBlock())
)
}
}

View File

@@ -71,18 +71,28 @@ class MetricCallable extends Callable {
}
}
private predicate fallthroughSwitchCase(SwitchCase sc1, SwitchCase sc2) {
exists(SwitchStmt switch, int n | switch.getStmt(n) = sc1 and switch.getStmt(n + 1) = sc2)
or
exists(SwitchExpr switch, int n | switch.getStmt(n) = sc1 and switch.getStmt(n + 1) = sc2)
}
// Branching points in the sense of cyclomatic complexity are binary,
// 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.getControlFlowNode().getASuccessor().asStmt() instanceof SwitchCase and
not defaultFallThrough(sc)
if sc.isRule()
then any()
else (
not fallthroughSwitchCase(sc, _) and
not defaultFallThrough(sc)
)
}
private predicate defaultFallThrough(ConstCase sc) {
exists(DefaultCase default | default.getControlFlowNode().getASuccessor().asStmt() = sc)
exists(DefaultCase default | fallthroughSwitchCase(default, sc))
or
defaultFallThrough(sc.getControlFlowNode().getAPredecessor().asStmt())
exists(ConstCase mid | defaultFallThrough(mid) and fallthroughSwitchCase(mid, sc))
}
/** Holds if `stmt` is a branching statement used for the computation of cyclomatic complexity. */

View File

@@ -73,7 +73,8 @@ module InsecureRandomnessConfig implements DataFlow::ConfigSig {
predicate isBarrierOut(DataFlow::Node n) { isSink(n) }
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
n1.asExpr() = n2.asExpr().(BinaryExpr).getAnOperand()
n1.asExpr() = n2.asExpr().(BinaryExpr).getAnOperand() and
not n2.asExpr() instanceof AssignExpr
or
n1.asExpr() = n2.asExpr().(UnaryExpr).getOperand()
or

View File

@@ -31,10 +31,7 @@ class RightShiftOp extends Expr {
this instanceof AssignUnsignedRightShiftExpr
}
private Expr getLhs() {
this.(BinaryExpr).getLeftOperand() = result or
this.(Assignment).getDest() = result
}
private Expr getLhs() { this.(BinaryExpr).getLeftOperand() = result }
/**
* Gets the variable that is shifted.

View File

@@ -54,9 +54,8 @@ private module PredictableSeedFlowConfig implements DataFlow::ConfigSig {
private module PredictableSeedFlow = DataFlow::Global<PredictableSeedFlowConfig>;
private predicate predictableCalcStep(Expr e1, Expr e2) {
e2.(BinaryExpr).hasOperands(e1, any(PredictableSeedExpr p))
or
exists(AssignOp a | a = e2 | e1 = a.getDest() and a.getRhs() instanceof PredictableSeedExpr)
e2.(BinaryExpr).hasOperands(e1, any(PredictableSeedExpr p)) and
not e2 instanceof AssignExpr
or
exists(ConstructorCall cc, TypeNumber t | cc = e2 |
cc.getArgument(0) = e1 and

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Expand @binaryexpr union to include @assignment
compatibility: full

View File

@@ -0,0 +1,28 @@
/**
* Provides utilities for getting an AST-based control flow graph in tests.
*/
overlay[local?]
module;
import java
private predicate isAstNode(ControlFlowNode n) {
n.injects(_) or
n instanceof ControlFlow::EntryNode or
n instanceof ControlFlow::AnnotatedExitNode or
n instanceof ControlFlow::ExitNode
}
private predicate succToAst(ControlFlowNode n1, ControlFlowNode n2) {
n2 = n1.getASuccessor() and
isAstNode(n2)
or
exists(ControlFlowNode mid |
mid = n1.getASuccessor() and
not isAstNode(mid) and
succToAst(mid, n2)
)
}
/** Gets a control flow successor of `n` that skips over non-AST nodes. */
ControlFlowNode getAnAstSuccessor(ControlFlowNode n) { isAstNode(n) and succToAst(n, result) }

View File

@@ -0,0 +1,40 @@
/**
* Provides utilities for working with basic blocks in tests.
*/
overlay[local?]
module;
import java
import codeql.util.Boolean
private predicate entryOrExit(ControlFlowNode n) {
n instanceof ControlFlow::EntryNode or
n instanceof ControlFlow::AnnotatedExitNode or
n instanceof ControlFlow::ExitNode
}
/** Gets the first AST node in the basic block `bb`, if any. */
ControlFlowNode getFirstAstNode(BasicBlock bb) { result = getFirstAstNode(bb, false) }
/**
* Gets the first AST node in the basic block `bb`, if any. Otherwise, gets
* the first synthetic node.
*/
ControlFlowNode getFirstAstNodeOrSynth(BasicBlock bb) { result = getFirstAstNode(bb, true) }
private ControlFlowNode getFirstAstNode(BasicBlock bb, Boolean allowSynthetic) {
result =
min(ControlFlowNode n, int i, int astOrder |
bb.getNode(i) = n and
if n.injects(_)
then astOrder = 0
else
if entryOrExit(n)
then astOrder = 1
else (
allowSynthetic = true and astOrder = 2
)
|
n order by astOrder, i
)
}