mirror of
https://github.com/github/codeql.git
synced 2026-03-30 20:28:15 +02:00
Merge branch 'main' of https://github.com/github/codeql into oscarsj/merge-back-rc-3.21
This commit is contained in:
16
java/ql/lib/change-notes/2026-02-18-cfg.md
Normal file
16
java/ql/lib/change-notes/2026-02-18-cfg.md
Normal 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.
|
||||
4
java/ql/lib/change-notes/2026-03-04-binary-assignment.md
Normal file
4
java/ql/lib/change-notes/2026-03-04-binary-assignment.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The class `Assignment` now extends `BinaryExpr`. Uses of `BinaryExpr` may in some cases need slight adjustment.
|
||||
@@ -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.
|
||||
@@ -815,6 +815,7 @@ when_branch_else(unique int id: @whenbranch ref);
|
||||
| @geexpr
|
||||
| @eqexpr
|
||||
| @neexpr
|
||||
| @assignment
|
||||
| @valueeqexpr
|
||||
| @valueneexpr;
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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 }
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(...)" }
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -0,0 +1,2 @@
|
||||
description: Expand @binaryexpr union to include @assignment
|
||||
compatibility: full
|
||||
28
java/ql/lib/utils/test/AstCfg.qll
Normal file
28
java/ql/lib/utils/test/AstCfg.qll
Normal 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) }
|
||||
40
java/ql/lib/utils/test/BasicBlock.qll
Normal file
40
java/ql/lib/utils/test/BasicBlock.qll
Normal 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
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user