Merge remote-tracking branch 'origin/main' into js/move-cors-query-from-experimental

This commit is contained in:
Napalys Klicius
2025-09-04 15:24:44 +02:00
1762 changed files with 97409 additions and 32772 deletions

View File

@@ -1,3 +1,15 @@
## 0.0.4
No user-facing changes.
## 0.0.3
No user-facing changes.
## 0.0.2
No user-facing changes.
## 0.0.1
### Minor Analysis Improvements

View File

@@ -0,0 +1,3 @@
## 0.0.2
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.3
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.4
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.1
lastReleaseVersion: 0.0.4

View File

@@ -46,27 +46,7 @@ module ConceptsMake<LocationSig Location, DF::InputSig<Location> DataFlowLang> {
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CryptographicOperation::Range` instead.
*/
class CryptographicOperation extends DataFlowNode instanceof CryptographicOperation::Range {
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
/** Gets the data flow node where the cryptographic algorithm used in this operation is configured. */
DataFlowNode getInitialization() { result = super.getInitialization() }
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
DataFlowNode getAnInput() { result = super.getAnInput() }
/**
* Gets the block mode used to perform this cryptographic operation.
*
* This predicate is only expected to have a result if two conditions hold:
* 1. The operation is an encryption operation, i.e. the algorithm used is an `EncryptionAlgorithm`, and
* 2. The algorithm used is a block cipher (not a stream cipher).
*
* If either of these conditions do not hold, then this predicate should have no result.
*/
BlockMode getBlockMode() { result = super.getBlockMode() }
}
final class CryptographicOperation = CryptographicOperation::Range;
/** Provides classes for modeling new applications of a cryptographic algorithms. */
module CryptographicOperation {
@@ -133,29 +113,7 @@ module ConceptsMake<LocationSig Location, DF::InputSig<Location> DataFlowLang> {
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `Http::Client::Request::Range` instead.
*/
class Request extends DataFlowNode instanceof Request::Range {
/**
* Gets a data flow node that contributes to the URL of the request.
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
*/
DataFlowNode getAUrlPart() { result = super.getAUrlPart() }
/** Gets a string that identifies the framework used for this request. */
string getFramework() { result = super.getFramework() }
/**
* Holds if this request is made using a mode that disables SSL/TLS
* certificate validation, where `disablingNode` represents the point at
* which the validation was disabled, and `argumentOrigin` represents the origin
* of the argument that disabled the validation (which could be the same node as
* `disablingNode`).
*/
predicate disablesCertificateValidation(
DataFlowNode disablingNode, DataFlowNode argumentOrigin
) {
super.disablesCertificateValidation(disablingNode, argumentOrigin)
}
}
final class Request = Request::Range;
/** Provides a class for modeling new HTTP requests. */
module Request {

View File

@@ -1,5 +1,5 @@
name: codeql/concepts
version: 0.0.2-dev
version: 0.0.5-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 2.0.14
No user-facing changes.
## 2.0.13
No user-facing changes.
## 2.0.12
No user-facing changes.
## 2.0.11
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.12
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.13
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.14
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 2.0.11
lastReleaseVersion: 2.0.14

View File

@@ -9,13 +9,12 @@ overlay[local?]
module;
private import codeql.util.Location
private import SuccessorType
/** Provides the language-specific input specification. */
signature module InputSig<LocationSig Location> {
class SuccessorType;
/** Hold if `t` represents a conditional successor type. */
predicate successorTypeIsCondition(SuccessorType t);
default predicate successorTypeIsCondition(SuccessorType t) { t instanceof ConditionalSuccessor }
/** A delineated part of the AST with its own CFG. */
class CfgScope;
@@ -47,12 +46,129 @@ signature module InputSig<LocationSig Location> {
predicate nodeIsPostDominanceExit(Node node);
}
signature module CfgSig<LocationSig Location> {
/** A control flow node. */
class ControlFlowNode {
/** Gets a textual representation of this control flow node. */
string toString();
/** Gets the location of this control flow node. */
Location getLocation();
}
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock {
/** Gets a textual representation of this basic block. */
string toString();
/** Gets the location of this basic block. */
Location getLocation();
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
ControlFlowNode getNode(int pos);
/** Gets the last control flow node in this basic block. */
ControlFlowNode getLastNode();
/** Gets the length of this basic block. */
int length();
/** Gets an immediate successor of this basic block, if any. */
BasicBlock getASuccessor();
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessor(SuccessorType t);
/**
* 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);
/**
* 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);
/**
* Holds if `df` is in the dominance frontier of this basic block. That is,
* this basic block dominates a predecessor of `df`, but does not dominate
* `df` itself. I.e., it is equivaluent to:
* ```
* this.dominates(df.getAPredecessor()) and not this.strictlyDominates(df)
* ```
*/
predicate inDominanceFrontier(BasicBlock df);
/**
* Gets the basic block that immediately dominates this basic block, if any.
*
* That is, the result is the unique basic block satisfying:
* 1. The result strictly dominates this basic block.
* 2. There exists no other basic block that is strictly dominated by the
* result and which strictly dominates this basic block.
*
* All basic blocks, except entry basic blocks, have a unique immediate
* dominator.
*/
BasicBlock getImmediateDominator();
/**
* Holds if this basic block strictly post-dominates basic block `bb`.
*
* That is, all paths reaching a normal exit point basic block from basic
* block `bb` must go through this basic block and this basic block is
* different from `bb`.
*/
predicate strictlyPostDominates(BasicBlock bb);
/**
* Holds if this basic block post-dominates basic block `bb`.
*
* That is, all paths reaching a normal exit point basic block from basic
* block `bb` must go through this basic block.
*/
predicate postDominates(BasicBlock bb);
}
/**
* An entry basic block, that is, a basic block whose first node is
* an entry node.
*/
class EntryBasicBlock extends BasicBlock;
/**
* Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1`
* and `bb2` is a dominating edge.
*
* An edge `(bb1, bb2)` is dominating if there exists a basic block that can
* only be reached from the entry block by going through `(bb1, bb2)`. This
* implies that `(bb1, bb2)` dominates its endpoint `bb2`. I.e., `bb2` can
* only be reached from the entry block by going via `(bb1, bb2)`.
*
* This is a necessary and sufficient condition for an edge to dominate some
* block, and therefore `dominatingEdge(bb1, bb2) and bb2.dominates(bb3)`
* means that the edge `(bb1, bb2)` dominates `bb3`.
*/
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2);
}
/**
* Provides a basic block construction on top of a control flow graph.
*/
module Make<LocationSig Location, InputSig<Location> Input> {
module Make<LocationSig Location, InputSig<Location> Input> implements CfgSig<Location> {
private import Input
class ControlFlowNode = Input::Node;
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
@@ -239,6 +355,10 @@ module Make<LocationSig Location, InputSig<Location> Input> {
string toString() { result = this.getFirstNode().toString() }
}
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { nodeIsDominanceEntry(this.getFirstNode()) }
}
/**
* Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1`
* and `bb2` is a dominating edge.
@@ -343,14 +463,14 @@ module Make<LocationSig Location, InputSig<Location> Input> {
cached
Node getNode(BasicBlock bb, int pos) { bbIndex(bb.getFirstNode(), result, pos) }
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { nodeIsDominanceEntry(bb.getFirstNode()) }
cached
predicate bbSuccessor(BasicBlock bb1, BasicBlock bb2, SuccessorType t) {
bb2.getFirstNode() = nodeGetASuccessor(bb1.getLastNode(), t)
}
/** Holds if `bb` is an entry basic block. */
private predicate entryBlock(EntryBasicBlock bb) { any() }
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `pred`.
@@ -360,7 +480,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
idominance(entryBlock/1, succBB/2)(_, dom, bb)
/** Holds if `pred` is a basic block predecessor of `succ`. */
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }

View File

@@ -8,6 +8,7 @@ module;
private import codeql.util.Location
private import codeql.util.FileSystem
private import codeql.util.Void
private import SuccessorType
/** Provides the language-specific input specification. */
signature module InputSig<LocationSig Location> {
@@ -59,26 +60,11 @@ signature module InputSig<LocationSig Location> {
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
predicate scopeLast(CfgScope scope, AstNode last, Completion c);
/** A type of a control flow successor. */
class SuccessorType {
/** Gets a textual representation of this successor type. */
string toString();
}
/** Gets a successor type that matches completion `c`. */
SuccessorType getAMatchingSuccessorType(Completion c);
/**
* Hold if `t` represents simple (normal) evaluation of a statement or an
* expression.
*/
predicate successorTypeIsSimple(SuccessorType t);
/** Hold if `t` represents a conditional successor type. */
predicate successorTypeIsCondition(SuccessorType t);
/** Holds if `t` is an abnormal exit type out of a CFG scope. */
predicate isAbnormalExitType(SuccessorType t);
default predicate successorTypeIsCondition(SuccessorType t) { t instanceof ConditionalSuccessor }
/**
* Gets an `id` of `node`. This is used to order the predecessors of a join
@@ -261,32 +247,44 @@ module MakeWithSplitting<
/** Gets the `i`th child element, in order of evaluation. */
abstract AstNode getChildNode(int i);
private AstNode getChildNodeRanked(int i) {
result = rank[i + 1](AstNode child, int j | child = this.getChildNode(j) | child order by j)
private ControlFlowTree getChildTreeRanked(int i) {
result =
rank[i + 1](ControlFlowTree child, int j | child = this.getChildNode(j) | child order by j)
}
/** Gets the first child node of this element. */
final AstNode getFirstChildNode() { result = this.getChildNodeRanked(0) }
deprecated final AstNode getFirstChildNode() { result = this.getChildTreeRanked(0) }
/** Gets the first child node of this element. */
final ControlFlowTree getFirstChildTree() { result = this.getChildTreeRanked(0) }
/** Gets the last child node of this node. */
final AstNode getLastChildElement() {
deprecated final AstNode getLastChildElement() {
exists(int last |
result = this.getChildNodeRanked(last) and
not exists(this.getChildNodeRanked(last + 1))
result = this.getChildTreeRanked(last) and
not exists(this.getChildTreeRanked(last + 1))
)
}
/** Gets the last child node of this node. */
final ControlFlowTree getLastChildTree() {
exists(int last |
result = this.getChildTreeRanked(last) and
not exists(this.getChildTreeRanked(last + 1))
)
}
/** Holds if this element has no children. */
predicate isLeafElement() { not exists(this.getFirstChildNode()) }
predicate isLeafElement() { not exists(this.getFirstChildTree()) }
override predicate propagatesAbnormal(AstNode child) { child = this.getChildNode(_) }
pragma[nomagic]
override predicate succ(AstNode pred, AstNode succ, Completion c) {
exists(int i |
last(this.getChildNodeRanked(i), pred, c) and
last(this.getChildTreeRanked(i), pred, c) and
completionIsNormal(c) and
first(this.getChildNodeRanked(i + 1), succ)
first(this.getChildTreeRanked(i + 1), succ)
)
}
}
@@ -294,7 +292,7 @@ module MakeWithSplitting<
/** A standard element that is executed in pre-order. */
abstract class StandardPreOrderTree extends StandardTree, PreOrderTree {
override predicate last(AstNode last, Completion c) {
last(this.getLastChildElement(), last, c)
last(this.getLastChildTree(), last, c)
or
this.isLeafElement() and
completionIsValidFor(c, this) and
@@ -305,7 +303,7 @@ module MakeWithSplitting<
StandardTree.super.succ(pred, succ, c)
or
pred = this and
first(this.getFirstChildNode(), succ) and
first(this.getFirstChildTree(), succ) and
completionIsSimple(c)
}
}
@@ -313,16 +311,16 @@ module MakeWithSplitting<
/** A standard element that is executed in post-order. */
abstract class StandardPostOrderTree extends StandardTree, PostOrderTree {
override predicate first(AstNode first) {
first(this.getFirstChildNode(), first)
first(this.getFirstChildTree(), first)
or
not exists(this.getFirstChildNode()) and
not exists(this.getFirstChildTree()) and
first = this
}
override predicate succ(AstNode pred, AstNode succ, Completion c) {
StandardTree.super.succ(pred, succ, c)
or
last(this.getLastChildElement(), pred, c) and
last(this.getLastChildTree(), pred, c) and
succ = this and
completionIsNormal(c)
}
@@ -510,7 +508,7 @@ module MakeWithSplitting<
private predicate succEntrySplits(CfgScope pred, AstNode succ, Splits succSplits, SuccessorType t) {
exists(int rnk |
scopeFirst(pred, succ) and
successorTypeIsSimple(t) and
t instanceof DirectSuccessor and
succEntrySplitsFromRank(pred, succ, succSplits, rnk)
|
rnk = 0 and
@@ -947,6 +945,12 @@ module MakeWithSplitting<
)
}
/** Holds if `t` is an abnormal exit type out of a CFG scope. */
private predicate isAbnormalExitType(SuccessorType t) {
t instanceof ExceptionSuccessor or
t instanceof ExitSuccessor
}
/**
* Internal representation of control flow nodes in the control flow graph.
* The control flow graph is pruned for unreachable nodes.
@@ -1004,7 +1008,7 @@ module MakeWithSplitting<
exists(CfgScope scope |
pred = TAnnotatedExitNode(scope, _) and
result = TExitNode(scope) and
successorTypeIsSimple(t)
t instanceof DirectSuccessor
)
}
@@ -1308,7 +1312,7 @@ module MakeWithSplitting<
label =
strictconcat(SuccessorType t, string s |
succ = getASuccessor(pred, t) and
if successorTypeIsSimple(t) then s = "" else s = t.toString()
if t instanceof DirectSuccessor then s = "" else s = t.toString()
|
s, ", " order by s
)
@@ -1523,6 +1527,8 @@ module MakeWithSplitting<
query predicate multipleSuccessors(Node node, SuccessorType t, Node successor) {
strictcount(getASuccessor(node, t)) > 1 and
successor = getASuccessor(node, t) and
// allow for multiple exception successors
not t instanceof ExceptionSuccessor and
// allow for functions with multiple bodies
not (t instanceof SimpleSuccessorType and node instanceof EntryNode)
}
@@ -1578,8 +1584,6 @@ module MakeWithSplitting<
private class NodeAlias = Node;
private module BasicBlockInputSig implements BB::InputSig<Location> {
class SuccessorType = Input::SuccessorType;
predicate successorTypeIsCondition = Input::successorTypeIsCondition/1;
class CfgScope = CfgScopeAlias;
@@ -1597,7 +1601,7 @@ module MakeWithSplitting<
private module BasicBlockImpl = BB::Make<Location, BasicBlockInputSig>;
final class BasicBlock = BasicBlockImpl::BasicBlock;
class BasicBlock = BasicBlockImpl::BasicBlock;
predicate dominatingEdge = BasicBlockImpl::dominatingEdge/2;

View File

@@ -50,76 +50,17 @@
overlay[local?]
module;
private import codeql.controlflow.BasicBlock as BB
private import codeql.controlflow.SuccessorType
private import codeql.util.Boolean
private import codeql.util.Location
private import codeql.util.Unit
signature module InputSig<LocationSig Location> {
class SuccessorType {
/** Gets a textual representation of this successor type. */
string toString();
}
signature class TypSig;
class ExceptionSuccessor extends SuccessorType;
class ConditionalSuccessor extends SuccessorType {
/** Gets the Boolean value of this successor. */
boolean getValue();
}
class BooleanSuccessor extends ConditionalSuccessor;
class NullnessSuccessor extends ConditionalSuccessor;
/** A control flow node. */
class ControlFlowNode {
/** Gets a textual representation of this control flow node. */
string toString();
/** Gets the location of this control flow node. */
Location getLocation();
}
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock {
/** Gets a textual representation of this basic block. */
string toString();
/** Gets the `i`th node in this basic block. */
ControlFlowNode getNode(int i);
/** Gets the last control flow node in this basic block. */
ControlFlowNode getLastNode();
/** Gets the length of this basic block. */
int length();
/** Gets the location of this basic block. */
Location getLocation();
BasicBlock getASuccessor(SuccessorType t);
predicate dominates(BasicBlock bb);
predicate strictlyDominates(BasicBlock bb);
}
/**
* Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1`
* and `bb2` is a dominating edge.
*
* An edge `(bb1, bb2)` is dominating if there exists a basic block that can
* only be reached from the entry block by going through `(bb1, bb2)`. This
* implies that `(bb1, bb2)` dominates its endpoint `bb2`. I.e., `bb2` can
* only be reached from the entry block by going via `(bb1, bb2)`.
*
* This is a necessary and sufficient condition for an edge to dominate some
* block, and therefore `dominatingEdge(bb1, bb2) and bb2.dominates(bb3)`
* means that the edge `(bb1, bb2)` dominates `bb3`.
*/
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2);
signature module InputSig<LocationSig Location, TypSig ControlFlowNode, TypSig BasicBlock> {
/** A control flow node indicating normal termination of a callable. */
class NormalExitNode extends ControlFlowNode;
class AstNode {
/** Gets a textual representation of this AST node. */
@@ -207,10 +148,56 @@ signature module InputSig<LocationSig Location> {
/** Gets the false branch of this expression. */
Expr getElse();
}
class Parameter {
/** Gets a textual representation of this parameter. */
string toString();
/** Gets the location of this parameter. */
Location getLocation();
}
class ParameterPosition {
/** Gets a textual representation of this element. */
bindingset[this]
string toString();
}
class ArgumentPosition {
/** Gets a textual representation of this element. */
bindingset[this]
string toString();
}
/**
* Holds if the parameter position `ppos` matches the argument position
* `apos`.
*/
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos);
/** A non-overridable method. */
class NonOverridableMethod {
Parameter getParameter(ParameterPosition ppos);
/** Gets an expression being returned by this method. */
Expr getAReturnExpr();
}
class NonOverridableMethodCall extends Expr {
NonOverridableMethod getMethod();
Expr getArgument(ArgumentPosition apos);
}
}
/** Provides guards-related predicates and classes. */
module Make<LocationSig Location, InputSig<Location> Input> {
module Make<
LocationSig Location, BB::CfgSig<Location> Cfg,
InputSig<Location, Cfg::ControlFlowNode, Cfg::BasicBlock> Input>
{
private module Cfg_ = Cfg;
private import Cfg_
private import Input
private newtype TAbstractSingleValue =
@@ -480,6 +467,8 @@ module Make<LocationSig Location, InputSig<Location> Input> {
)
}
private predicate normalExitBlock(BasicBlock bb) { bb.getNode(_) instanceof NormalExitNode }
signature module LogicInputSig {
class SsaDefinition {
/** Gets the basic block to which this SSA definition belongs. */
@@ -503,6 +492,8 @@ module Make<LocationSig Location, InputSig<Location> Input> {
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb);
}
predicate parameterDefinition(Parameter p, SsaDefinition def);
/**
* Holds if `guard` evaluating to `val` ensures that:
* `e <= k` when `upper = true`
@@ -525,8 +516,6 @@ module Make<LocationSig Location, InputSig<Location> Input> {
* Holds if the assumption that `g1` has been evaluated to `v1` implies that
* `g2` has been evaluated to `v2`, that is, the evaluation of `g2` to `v2`
* dominates the evaluation of `g1` to `v1`.
*
* This predicate can be instantiated with `CustomGuard<..>::additionalImpliesStep`.
*/
default predicate additionalImpliesStep(PreGuard g1, GuardValue v1, PreGuard g2, GuardValue v2) {
none()
@@ -827,83 +816,85 @@ module Make<LocationSig Location, InputSig<Location> Input> {
private signature predicate baseGuardValueSig(Guard guard, GuardValue v);
/**
* Calculates the transitive closure of all the guard implication steps
* starting from a given set of base cases.
*/
cached
private module ImpliesTC<baseGuardValueSig/2 baseGuardValue> {
private module Cached {
/**
* Holds if `tgtGuard` evaluating to `tgtVal` implies that `guard`
* evaluates to `v`.
* Calculates the transitive closure of all the guard implication steps
* starting from a given set of base cases.
*/
pragma[nomagic]
cached
predicate guardControls(Guard guard, GuardValue v, Guard tgtGuard, GuardValue tgtVal) {
baseGuardValue(tgtGuard, tgtVal) and
guard = tgtGuard and
v = tgtVal
or
exists(Guard g0, GuardValue v0 |
guardControls(g0, v0, tgtGuard, tgtVal) and
impliesStep2(g0, v0, guard, v)
)
or
exists(Guard g0, GuardValue v0 |
guardControls(g0, v0, tgtGuard, tgtVal) and
unboundImpliesStep(g0, v0, guard, v)
)
or
exists(SsaDefinition def0, GuardValue v0 |
ssaControls(def0, v0, tgtGuard, tgtVal) and
impliesStepSsaGuard(def0, v0, guard, v)
)
or
exists(Guard g0, GuardValue v0 |
guardControls(g0, v0, tgtGuard, tgtVal) and
additionalImpliesStep(g0, v0, guard, v)
)
module ImpliesTC<baseGuardValueSig/2 baseGuardValue> {
/**
* Holds if `tgtGuard` evaluating to `tgtVal` implies that `guard`
* evaluates to `v`.
*/
pragma[nomagic]
cached
predicate guardControls(Guard guard, GuardValue v, Guard tgtGuard, GuardValue tgtVal) {
baseGuardValue(tgtGuard, tgtVal) and
guard = tgtGuard and
v = tgtVal
or
exists(Guard g0, GuardValue v0 |
guardControls(g0, v0, tgtGuard, tgtVal) and
impliesStep2(g0, v0, guard, v)
)
or
exists(Guard g0, GuardValue v0 |
guardControls(g0, v0, tgtGuard, tgtVal) and
unboundImpliesStep(g0, v0, guard, v)
)
or
exists(SsaDefinition def0, GuardValue v0 |
ssaControls(def0, v0, tgtGuard, tgtVal) and
impliesStepSsaGuard(def0, v0, guard, v)
)
or
exists(Guard g0, GuardValue v0 |
guardControls(g0, v0, tgtGuard, tgtVal) and
WrapperGuard::wrapperImpliesStep(g0, v0, guard, v)
)
or
exists(Guard g0, GuardValue v0 |
guardControls(g0, v0, tgtGuard, tgtVal) and
additionalImpliesStep(g0, v0, guard, v)
)
}
/**
* Holds if `tgtGuard` evaluating to `tgtVal` implies that `def`
* evaluates to `v`.
*/
pragma[nomagic]
cached
predicate ssaControls(SsaDefinition def, GuardValue v, Guard tgtGuard, GuardValue tgtVal) {
exists(Guard g0 |
guardControls(g0, v, tgtGuard, tgtVal) and
guardReadsSsaVar(g0, def)
)
or
exists(SsaDefinition def0 |
ssaControls(def0, v, tgtGuard, tgtVal) and
impliesStepSsa(def0, v, def)
)
}
}
/**
* Holds if `tgtGuard` evaluating to `tgtVal` implies that `def`
* evaluates to `v`.
* Holds if `guard` evaluating to `v` implies that `e` is guaranteed to be
* null if `isNull` is true, and non-null if `isNull` is false.
*/
pragma[nomagic]
cached
predicate ssaControls(SsaDefinition def, GuardValue v, Guard tgtGuard, GuardValue tgtVal) {
exists(Guard g0 |
guardControls(g0, v, tgtGuard, tgtVal) and
guardReadsSsaVar(g0, def)
)
or
exists(SsaDefinition def0 |
ssaControls(def0, v, tgtGuard, tgtVal) and
impliesStepSsa(def0, v, def)
)
predicate nullGuard(Guard guard, GuardValue v, Expr e, boolean isNull) {
impliesStep2(guard, v, e, any(GuardValue gv | gv.isNullness(isNull))) or
WrapperGuard::wrapperImpliesStep(guard, v, e, any(GuardValue gv | gv.isNullness(isNull))) or
additionalImpliesStep(guard, v, e, any(GuardValue gv | gv.isNullness(isNull)))
}
}
private predicate booleanGuard(Guard guard, GuardValue val) {
exists(guard) and exists(val.asBooleanValue())
}
private import Cached
private module BooleanImplies = ImpliesTC<booleanGuard/2>;
/** INTERNAL: Don't use. */
predicate boolImplies(Guard g1, GuardValue v1, Guard g2, GuardValue v2) {
BooleanImplies::guardControls(g2, v2, g1, v1) and
g2 != g1
}
/**
* Holds if `guard` evaluating to `v` implies that `e` is guaranteed to be
* null if `isNull` is true, and non-null if `isNull` is false.
*/
predicate nullGuard(Guard guard, GuardValue v, Expr e, boolean isNull) {
impliesStep2(guard, v, e, any(GuardValue gv | gv.isNullness(isNull))) or
additionalImpliesStep(guard, v, e, any(GuardValue gv | gv.isNullness(isNull)))
}
predicate nullGuard = Cached::nullGuard/4;
private predicate hasAValueBranchEdge(Guard guard, GuardValue v) {
guard.hasValueBranchEdge(_, _, v)
@@ -944,61 +935,45 @@ module Make<LocationSig Location, InputSig<Location> Input> {
)
}
signature module CustomGuardInputSig {
class ParameterPosition {
/** Gets a textual representation of this element. */
bindingset[this]
string toString();
}
class ArgumentPosition {
/** Gets a textual representation of this element. */
bindingset[this]
string toString();
}
/**
* Holds if the parameter position `ppos` matches the argument position
* `apos`.
*/
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos);
/** A non-overridable method with a boolean return value. */
class BooleanMethod {
SsaDefinition getParameter(ParameterPosition ppos);
Expr getAReturnExpr();
}
class BooleanMethodCall extends Expr {
BooleanMethod getMethod();
Expr getArgument(ArgumentPosition apos);
}
}
/**
* Provides an implementation of guard implication logic for custom
* wrappers. This can be used to instantiate the `additionalImpliesStep`
* predicate.
* Provides an implementation of guard implication logic for guard
* wrappers.
*/
module CustomGuard<CustomGuardInputSig CustomGuardInput> {
private import CustomGuardInput
private module WrapperGuard {
final private class FinalExpr = Expr;
private class ReturnExpr extends FinalExpr {
ReturnExpr() { any(BooleanMethod m).getAReturnExpr() = this }
class ReturnExpr extends FinalExpr {
ReturnExpr() { any(NonOverridableMethod m).getAReturnExpr() = this }
NonOverridableMethod getMethod() { result.getAReturnExpr() = this }
pragma[nomagic]
BasicBlock getBasicBlock() { result = super.getBasicBlock() }
}
private predicate booleanReturnGuard(Guard guard, GuardValue val) {
guard instanceof ReturnExpr and exists(val.asBooleanValue())
private predicate relevantCallValue(NonOverridableMethodCall call, GuardValue val) {
BranchImplies::guardControls(call, val, _, _) or
ReturnImplies::guardControls(call, val, _, _)
}
private module ReturnImplies = ImpliesTC<booleanReturnGuard/2>;
predicate relevantReturnValue(NonOverridableMethod m, GuardValue val) {
exists(NonOverridableMethodCall call |
relevantCallValue(call, val) and
call.getMethod() = m and
not val instanceof TException
)
}
private predicate returnGuard(Guard guard, GuardValue val) {
relevantReturnValue(guard.(ReturnExpr).getMethod(), val)
}
module ReturnImplies = ImpliesTC<returnGuard/2>;
pragma[nomagic]
private predicate directlyControlsReturn(Guard guard, GuardValue val, ReturnExpr ret) {
guard.directlyValueControls(ret.getBasicBlock(), val)
}
/**
* Holds if `ret` is a return expression in a non-overridable method that
@@ -1006,35 +981,53 @@ module Make<LocationSig Location, InputSig<Location> Input> {
* parameter has the value `val`.
*/
private predicate validReturnInCustomGuard(
ReturnExpr ret, ParameterPosition ppos, boolean retval, GuardValue val
ReturnExpr ret, ParameterPosition ppos, GuardValue retval, GuardValue val
) {
exists(BooleanMethod m, SsaDefinition param |
exists(NonOverridableMethod m, SsaDefinition param |
m.getAReturnExpr() = ret and
m.getParameter(ppos) = param
parameterDefinition(m.getParameter(ppos), param)
|
exists(Guard g0, GuardValue v0 |
g0.directlyValueControls(ret.getBasicBlock(), v0) and
directlyControlsReturn(g0, v0, ret) and
BranchImplies::ssaControls(param, val, g0, v0) and
retval = [true, false]
relevantReturnValue(m, retval)
)
or
ReturnImplies::ssaControls(param, val, ret,
any(GuardValue r | r.asBooleanValue() = retval))
ReturnImplies::ssaControls(param, val, ret, retval)
)
}
private predicate guardDirectlyControlsExit(Guard guard, GuardValue val) {
exists(BasicBlock bb |
guard.directlyValueControls(bb, val) and
normalExitBlock(bb)
)
}
/**
* Gets a non-overridable method with a boolean return value that performs a check
* on the `ppos`th parameter. A return value equal to `retval` allows us to conclude
* Gets a non-overridable method that performs a check on the `ppos`th
* parameter. A return value equal to `retval` allows us to conclude
* that the argument has the value `val`.
*/
private BooleanMethod customGuard(ParameterPosition ppos, boolean retval, GuardValue val) {
private NonOverridableMethod wrapperGuard(
ParameterPosition ppos, GuardValue retval, GuardValue val
) {
forex(ReturnExpr ret |
result.getAReturnExpr() = ret and
not ret.(ConstantExpr).asBooleanValue() = retval.booleanNot()
not exists(GuardValue notRetval |
exprHasValue(ret, notRetval) and
disjointValues(notRetval, retval)
)
|
validReturnInCustomGuard(ret, ppos, retval, val)
)
or
exists(SsaDefinition param, Guard g0, GuardValue v0 |
parameterDefinition(result.getParameter(ppos), param) and
guardDirectlyControlsExit(g0, v0) and
retval = TException(false) and
BranchImplies::ssaControls(param, val, g0, v0)
)
}
/**
@@ -1043,16 +1036,124 @@ module Make<LocationSig Location, InputSig<Location> Input> {
* dominates the evaluation of `g1` to `v1`.
*
* This predicate covers the implication steps that arise from calls to
* custom guard wrappers.
* guard wrappers.
*/
predicate additionalImpliesStep(PreGuard g1, GuardValue v1, PreGuard g2, GuardValue v2) {
exists(BooleanMethodCall call, ParameterPosition ppos, ArgumentPosition apos |
predicate wrapperImpliesStep(PreGuard g1, GuardValue v1, PreGuard g2, GuardValue v2) {
exists(NonOverridableMethodCall call, ParameterPosition ppos, ArgumentPosition apos |
g1 = call and
call.getMethod() = customGuard(ppos, v1.asBooleanValue(), v2) and
call.getMethod() = wrapperGuard(ppos, v1, v2) and
call.getArgument(apos) = g2 and
parameterMatch(pragma[only_bind_out](ppos), pragma[only_bind_out](apos)) and
not exprHasValue(g2, v2) // disregard trivial guard
)
}
}
signature predicate guardChecksSig(Guard g, Expr e, boolean branch);
bindingset[this]
signature class StateSig;
private module WithState<StateSig State> {
signature predicate guardChecksSig(Guard g, Expr e, boolean branch, State state);
}
/**
* Extends a `BarrierGuard` input predicate with wrapped invocations.
*/
module ValidationWrapper<guardChecksSig/3 guardChecks0> {
private predicate guardChecksWithState(Guard g, Expr e, boolean branch, Unit state) {
guardChecks0(g, e, branch) and exists(state)
}
private module StatefulWrapper = ValidationWrapperWithState<Unit, guardChecksWithState/4>;
/**
* Holds if the guard `g` validates the SSA definition `def` upon evaluating to `val`.
*/
predicate guardChecksDef(Guard g, SsaDefinition def, GuardValue val) {
StatefulWrapper::guardChecksDef(g, def, val, _)
}
}
/**
* Extends a `BarrierGuard` input predicate with wrapped invocations.
*/
module ValidationWrapperWithState<
StateSig State, WithState<State>::guardChecksSig/4 guardChecks0>
{
private import WrapperGuard
/**
* Holds if `ret` is a return expression in a non-overridable method that
* on a return value of `retval` allows the conclusion that the `ppos`th
* parameter has been validated by the given guard.
*/
private predicate validReturnInValidationWrapper(
ReturnExpr ret, ParameterPosition ppos, GuardValue retval, State state
) {
exists(NonOverridableMethod m, SsaDefinition param, Guard guard, GuardValue val |
m.getAReturnExpr() = ret and
parameterDefinition(m.getParameter(ppos), param) and
guardChecksDef(guard, param, val, state)
|
guard.valueControls(ret.getBasicBlock(), val) and
relevantReturnValue(m, retval)
or
ReturnImplies::guardControls(guard, val, ret, retval)
)
}
/**
* Gets a non-overridable method that performs a check on the `ppos`th
* parameter. A return value equal to `retval` allows us to conclude
* that the argument has been validated by the given guard.
*/
private NonOverridableMethod validationWrapper(
ParameterPosition ppos, GuardValue retval, State state
) {
forex(ReturnExpr ret |
result.getAReturnExpr() = ret and
not exists(GuardValue notRetval |
exprHasValue(ret, notRetval) and
disjointValues(notRetval, retval)
)
|
validReturnInValidationWrapper(ret, ppos, retval, state)
)
or
exists(SsaDefinition param, BasicBlock bb, Guard guard, GuardValue val |
parameterDefinition(result.getParameter(ppos), param) and
guardChecksDef(guard, param, val, state) and
guard.valueControls(bb, val) and
normalExitBlock(bb) and
retval = TException(false)
)
}
/**
* Holds if the guard `g` validates the expression `e` upon evaluating to `val`.
*/
private predicate guardChecks(Guard g, Expr e, GuardValue val, State state) {
guardChecks0(g, e, val.asBooleanValue(), state)
or
exists(NonOverridableMethodCall call, ParameterPosition ppos, ArgumentPosition apos |
g = call and
call.getMethod() = validationWrapper(ppos, val, state) and
call.getArgument(apos) = e and
parameterMatch(pragma[only_bind_out](ppos), pragma[only_bind_out](apos))
)
}
/**
* Holds if the guard `g` validates the SSA definition `def` upon evaluating to `val`.
*/
predicate guardChecksDef(Guard g, SsaDefinition def, GuardValue val, State state) {
exists(Expr e |
guardChecks(g, e, val, state) and
guardReadsSsaVar(e, def)
)
}
}
/**

View File

@@ -0,0 +1,343 @@
/**
* Provides different types of control flow successor types. These are used as
* edge labels in the control flow graph.
*
* ```text
* SuccessorType
* |- NormalSuccessor
* | |- DirectSuccessor
* | \- ConditionalSuccessor
* | |- BooleanSuccessor
* | |- NullnessSuccessor
* | |- MatchingSuccessor
* | \- EmptinessSuccessor
* \- AbruptSuccessor
* |- ExceptionSuccessor
* |- ReturnSuccessor
* |- ExitSuccessor (program termination)
* \- JumpSuccessor
* |- BreakSuccessor
* |- ContinueSuccessor
* |- GotoSuccessor
* |- RedoSuccessor // rare, used in Ruby
* \- RetrySuccessor // rare, used in Ruby
* ```
*/
overlay[local]
module;
private import codeql.util.Boolean
private newtype TSuccessorType =
TDirectSuccessor() or
TBooleanSuccessor(Boolean branch) or
TNullnessSuccessor(Boolean isNull) or
TMatchingSuccessor(Boolean isMatch) or
TEmptinessSuccessor(Boolean isEmpty) or
TExceptionSuccessor() or
TReturnSuccessor() or
TExitSuccessor() or
TBreakSuccessor() or
TContinueSuccessor() or
TGotoSuccessor() or
TRedoSuccessor() or
TRetrySuccessor()
/**
* The type of a control flow successor.
*
* A successor is either normal, which covers direct and conditional
* successors, or abrupt, which covers all other types of successors including
* for example exceptions, returns, and other jumps.
*/
private class SuccessorTypeImpl extends TSuccessorType {
/** Gets a textual representation of this successor type. */
abstract string toString();
}
final class SuccessorType = SuccessorTypeImpl;
private class TNormalSuccessor = TDirectSuccessor or TConditionalSuccessor;
/**
* A normal control flow successor. This is either a direct or a conditional
* successor.
*/
abstract private class NormalSuccessorImpl extends SuccessorTypeImpl, TNormalSuccessor { }
final class NormalSuccessor = NormalSuccessorImpl;
/** A direct control flow successor. */
class DirectSuccessor extends NormalSuccessorImpl, TDirectSuccessor {
override string toString() { result = "successor" }
}
private class TConditionalSuccessor =
TBooleanSuccessor or TMatchingSuccessor or TNullnessSuccessor or TEmptinessSuccessor;
/**
* A conditional control flow successor. Either a Boolean successor (`BooleanSuccessor`),
* a nullness successor (`NullnessSuccessor`), a matching successor (`MatchingSuccessor`),
* or an emptiness successor (`EmptinessSuccessor`).
*/
abstract private class ConditionalSuccessorImpl extends NormalSuccessorImpl, TConditionalSuccessor {
/** Gets the Boolean value of this successor. */
abstract boolean getValue();
}
final class ConditionalSuccessor = ConditionalSuccessorImpl;
/**
* A Boolean control flow successor.
*
* For example, this program fragment:
*
* ```csharp
* if (x < 0)
* return 0;
* else
* return 1;
* ```
*
* has a control flow graph containing Boolean successors:
*
* ```text
* if
* |
* x < 0
* / \
* / \
* / \
* true false
* | \
* return 0 return 1
* ```
*/
class BooleanSuccessor extends ConditionalSuccessorImpl, TBooleanSuccessor {
override boolean getValue() { this = TBooleanSuccessor(result) }
override string toString() { result = this.getValue().toString() }
}
/**
* A nullness control flow successor.
*
* For example, this program fragment:
*
* ```csharp
* int? M(string s) => s?.Length;
* ```
*
* has a control flow graph containing nullness successors:
*
* ```text
* enter M
* |
* s
* / \
* / \
* / \
* null non-null
* \ |
* \ Length
* \ /
* \ /
* exit M
* ```
*/
class NullnessSuccessor extends ConditionalSuccessorImpl, TNullnessSuccessor {
/** Holds if this is a `null` successor. */
predicate isNull() { this = TNullnessSuccessor(true) }
override boolean getValue() { this = TNullnessSuccessor(result) }
override string toString() { if this.isNull() then result = "null" else result = "non-null" }
}
/**
* A matching control flow successor.
*
* For example, this program fragment:
*
* ```csharp
* switch (x) {
* case 0 :
* return 0;
* default :
* return 1;
* }
* ```
*
* has a control flow graph containing matching successors:
*
* ```text
* switch
* |
* x
* |
* case 0
* / \
* / \
* / \
* match no-match
* | \
* return 0 default
* |
* return 1
* ```
*/
class MatchingSuccessor extends ConditionalSuccessorImpl, TMatchingSuccessor {
/** Holds if this is a match successor. */
predicate isMatch() { this = TMatchingSuccessor(true) }
override boolean getValue() { this = TMatchingSuccessor(result) }
override string toString() { if this.isMatch() then result = "match" else result = "no-match" }
}
/**
* An emptiness control flow successor.
*
* For example, this program fragment:
*
* ```csharp
* foreach (var arg in args)
* {
* yield return arg;
* }
* yield return "";
* ```
*
* has a control flow graph containing emptiness successors:
*
* ```text
* args
* |
* loop-header------<-----
* / \ \
* / \ |
* / \ |
* / \ |
* empty non-empty |
* | \ |
* yield return "" \ |
* var arg |
* | |
* yield return arg |
* \_________/
* ```
*/
class EmptinessSuccessor extends ConditionalSuccessorImpl, TEmptinessSuccessor {
/** Holds if this is an empty successor. */
predicate isEmpty() { this = TEmptinessSuccessor(true) }
override boolean getValue() { this = TEmptinessSuccessor(result) }
override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
}
private class TAbruptSuccessor =
TExceptionSuccessor or TReturnSuccessor or TExitSuccessor or TJumpSuccessor;
/** An abrupt control flow successor. */
abstract private class AbruptSuccessorImpl extends SuccessorTypeImpl, TAbruptSuccessor { }
final class AbruptSuccessor = AbruptSuccessorImpl;
/**
* An exceptional control flow successor.
*
* Example:
*
* ```csharp
* int M(string s)
* {
* if (s == null)
* throw new ArgumentNullException(nameof(s));
* return s.Length;
* }
* ```
*
* The callable exit node of `M` is an exceptional successor of the node
* `throw new ArgumentNullException(nameof(s));`.
*/
class ExceptionSuccessor extends AbruptSuccessorImpl, TExceptionSuccessor {
override string toString() { result = "exception" }
}
/**
* A `return` control flow successor.
*
* Example:
*
* ```csharp
* void M()
* {
* return;
* }
* ```
*
* The callable exit node of `M` is a `return` successor of the `return;`
* statement.
*/
class ReturnSuccessor extends AbruptSuccessorImpl, TReturnSuccessor {
override string toString() { result = "return" }
}
/**
* An exit control flow successor.
*
* Example:
*
* ```csharp
* int M(string s)
* {
* if (s == null)
* System.Environment.Exit(0);
* return s.Length;
* }
* ```
*
* The callable exit node of `M` is an exit successor of the node on line 4.
*/
class ExitSuccessor extends AbruptSuccessorImpl, TExitSuccessor {
override string toString() { result = "exit" }
}
private class TJumpSuccessor =
TBreakSuccessor or TContinueSuccessor or TGotoSuccessor or TRedoSuccessor or TRetrySuccessor;
/**
* A jump control flow successor.
*
* This covers non-exceptional, non-local control flow, such as `break`,
* `continue`, and `goto`.
*/
abstract private class JumpSuccessorImpl extends AbruptSuccessorImpl, TJumpSuccessor { }
final class JumpSuccessor = JumpSuccessorImpl;
/** A `break` control flow successor. */
class BreakSuccessor extends JumpSuccessorImpl, TBreakSuccessor {
override string toString() { result = "break" }
}
/** A `continue` control flow successor. */
class ContinueSuccessor extends JumpSuccessorImpl, TContinueSuccessor {
override string toString() { result = "continue" }
}
/** A `goto` control flow successor. */
class GotoSuccessor extends JumpSuccessorImpl, TGotoSuccessor {
override string toString() { result = "goto" }
}
/** A `redo` control flow successor (rare, used in Ruby). */
class RedoSuccessor extends JumpSuccessorImpl, TRedoSuccessor {
override string toString() { result = "redo" }
}
/** A `retry` control flow successor (rare, used in Ruby). */
class RetrySuccessor extends JumpSuccessorImpl, TRetrySuccessor {
override string toString() { result = "retry" }
}

View File

@@ -1,5 +1,5 @@
name: codeql/controlflow
version: 2.0.12-dev
version: 2.0.15-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 2.0.14
No user-facing changes.
## 2.0.13
No user-facing changes.
## 2.0.12
No user-facing changes.
## 2.0.11
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.12
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.13
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.14
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 2.0.11
lastReleaseVersion: 2.0.14

View File

@@ -8,66 +8,14 @@ module;
private import codeql.util.Boolean
private import codeql.util.Unit
private import codeql.util.Location
private import codeql.controlflow.BasicBlock as BB
private import codeql.ssa.Ssa as Ssa
signature module InputSig<LocationSig Location> {
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock {
/** Gets a textual representation of this basic block. */
string toString();
signature class BasicBlockSig;
/** Gets the `i`th node in this basic block. */
ControlFlowNode getNode(int i);
/** Gets the length of this basic block. */
int length();
/** Gets the enclosing callable. */
Callable getEnclosingCallable();
/** Gets the location of this basic block. */
Location getLocation();
}
/** A control flow node. */
class ControlFlowNode {
/** Gets a textual representation of this control flow node. */
string toString();
/** Gets the location of this control flow node. */
Location getLocation();
}
/**
* Gets the basic block that immediately dominates basic block `bb`, if any.
*
* That is, all paths reaching `bb` from some entry point basic block must go
* through the result.
*
* Example:
*
* ```csharp
* int M(string s) {
* if (s == null)
* throw new ArgumentNullException(nameof(s));
* return s.Length;
* }
* ```
*
* The basic block starting on line 2 is an immediate dominator of
* the basic block on line 4 (all paths from the entry point of `M`
* to `return s.Length;` must go through the null check.
*/
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb);
/** Gets an immediate successor of basic block `bb`, if any. */
BasicBlock getABasicBlockSuccessor(BasicBlock bb);
/** Holds if `bb` is a control-flow entry point. */
default predicate entryBlock(BasicBlock bb) { not exists(getImmediateBasicBlockDominator(bb)) }
signature module InputSig<LocationSig Location, BasicBlockSig BasicBlock> {
/** Gets the enclosing callable of the basic block. */
Callable basicBlockGetEnclosingCallable(BasicBlock bb);
/** A variable that is captured in a closure. */
class CapturedVariable {
@@ -153,7 +101,9 @@ signature module InputSig<LocationSig Location> {
}
}
signature module OutputSig<LocationSig Location, InputSig<Location> I> {
signature module OutputSig<
LocationSig Location, BasicBlockSig BasicBlock, InputSig<Location, BasicBlock> I>
{
/**
* A data flow node that we need to reference in the step relations for
* captured variables.
@@ -255,9 +205,18 @@ signature module OutputSig<LocationSig Location, InputSig<Location> I> {
* Constructs the type `ClosureNode` and associated step relations, which are
* intended to be included in the data-flow node and step relations.
*/
module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig<Location, Input> {
module Flow<
LocationSig Location, BB::CfgSig<Location> Cfg, InputSig<Location, Cfg::BasicBlock> Input>
implements OutputSig<Location, Cfg::BasicBlock, Input>
{
private import Input
final private class CfgBb = Cfg::BasicBlock;
private class BasicBlock extends CfgBb {
Callable getEnclosingCallable() { result = basicBlockGetEnclosingCallable(this) }
}
additional module ConsistencyChecks {
final private class FinalExpr = Expr;
@@ -332,17 +291,17 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
query predicate uniqueDominator(RelevantBasicBlock bb, string msg) {
msg = "BasicBlock has multiple immediate dominators" and
2 <= strictcount(getImmediateBasicBlockDominator(bb))
2 <= strictcount(bb.getImmediateDominator())
}
query predicate localDominator(RelevantBasicBlock bb, string msg) {
msg = "BasicBlock has non-local dominator" and
bb.getEnclosingCallable() != getImmediateBasicBlockDominator(bb).getEnclosingCallable()
bb.getEnclosingCallable() != bb.getImmediateDominator().(BasicBlock).getEnclosingCallable()
}
query predicate localSuccessor(RelevantBasicBlock bb, string msg) {
msg = "BasicBlock has non-local successor" and
bb.getEnclosingCallable() != getABasicBlockSuccessor(bb).getEnclosingCallable()
bb.getEnclosingCallable() != bb.getASuccessor().(BasicBlock).getEnclosingCallable()
}
query predicate uniqueDefiningScope(CapturedVariable v, string msg) {
@@ -669,7 +628,7 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
/** Holds if `cc` needs a definition at the entry of its callable scope. */
private predicate entryDef(CaptureContainer cc, BasicBlock bb, int i) {
exists(Callable c |
entryBlock(bb) and
bb instanceof Cfg::EntryBasicBlock and
pragma[only_bind_out](bb.getEnclosingCallable()) = c and
i =
min(int j |
@@ -685,22 +644,10 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
)
}
private module CaptureSsaInput implements Ssa::InputSig<Location> {
final class BasicBlock = Input::BasicBlock;
final class ControlFlowNode = Input::ControlFlowNode;
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
result = Input::getImmediateBasicBlockDominator(bb)
}
BasicBlock getABasicBlockSuccessor(BasicBlock bb) {
result = Input::getABasicBlockSuccessor(bb)
}
private module CaptureSsaInput implements Ssa::InputSig<Location, Cfg::BasicBlock> {
class SourceVariable = CaptureContainer;
predicate variableWrite(BasicBlock bb, int i, SourceVariable cc, boolean certain) {
predicate variableWrite(Cfg::BasicBlock bb, int i, SourceVariable cc, boolean certain) {
Cached::ref() and
(
exists(CapturedVariable v | cc = TVariable(v) and captureWrite(v, bb, i, true, _))
@@ -710,9 +657,9 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
certain = true
}
predicate variableRead(BasicBlock bb, int i, SourceVariable cc, boolean certain) {
predicate variableRead(Cfg::BasicBlock bb, int i, SourceVariable cc, boolean certain) {
(
synthThisQualifier(bb, i) and cc = TThis(bb.getEnclosingCallable())
synthThisQualifier(bb, i) and cc = TThis(bb.(BasicBlock).getEnclosingCallable())
or
exists(CapturedVariable v | cc = TVariable(v) |
captureRead(v, bb, i, true, _) or synthRead(v, bb, i, true, _)
@@ -722,24 +669,30 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
}
}
private module CaptureSsa = Ssa::Make<Location, CaptureSsaInput>;
private module CaptureSsa = Ssa::Make<Location, Cfg, CaptureSsaInput>;
private module DataFlowIntegrationInput implements CaptureSsa::DataFlowIntegrationInputSig {
private import codeql.util.Void
class Expr instanceof Input::ControlFlowNode {
class Expr instanceof Cfg::ControlFlowNode {
string toString() { result = super.toString() }
predicate hasCfgNode(BasicBlock bb, int i) { bb.getNode(i) = this }
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { bb.getNode(i) = this }
}
class GuardValue = Void;
class Guard extends Void {
predicate hasBranchEdge(BasicBlock bb1, BasicBlock bb2, boolean branch) { none() }
predicate hasValueBranchEdge(Cfg::BasicBlock bb1, Cfg::BasicBlock bb2, GuardValue val) {
none()
}
predicate controlsBranchEdge(BasicBlock bb1, BasicBlock bb2, boolean branch) { none() }
predicate valueControlsBranchEdge(Cfg::BasicBlock bb1, Cfg::BasicBlock bb2, GuardValue val) {
none()
}
}
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, boolean branch) { none() }
predicate guardDirectlyControlsBlock(Guard guard, Cfg::BasicBlock bb, GuardValue val) { none() }
predicate includeWriteDefsInFlowStep() { none() }

View File

@@ -1,8 +1,9 @@
name: codeql/dataflow
version: 2.0.12-dev
version: 2.0.15-dev
groups: shared
library: true
dependencies:
codeql/controlflow: ${workspace}
codeql/ssa: ${workspace}
codeql/typetracking: ${workspace}
codeql/util: ${workspace}

View File

@@ -1,3 +1,15 @@
## 1.0.30
No user-facing changes.
## 1.0.29
No user-facing changes.
## 1.0.28
No user-facing changes.
## 1.0.27
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.28
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.27
lastReleaseVersion: 1.0.30

View File

@@ -1,5 +1,5 @@
name: codeql/mad
version: 1.0.28-dev
version: 1.0.31-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 0.0.8
No user-facing changes.
## 0.0.7
No user-facing changes.
## 0.0.6
No user-facing changes.
## 0.0.5
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.6
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.7
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.8
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.5
lastReleaseVersion: 0.0.8

View File

@@ -365,7 +365,7 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
*/
abstract class ArtifactConsumer extends ConsumerElement {
/**
* Use `getAKnownArtifactSource() instead. The behaviour of these two predicates is equivalent.
* Use `getAKnownArtifactSource() instead. The behavior of these two predicates is equivalent.
*/
final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() }
@@ -1841,9 +1841,7 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
* An SCRYPT key derivation algorithm node.
*/
class ScryptAlgorithmNode extends KeyDerivationAlgorithmNode {
ScryptAlgorithmInstance scryptInstance;
ScryptAlgorithmNode() { scryptInstance = instance.asAlg() }
ScryptAlgorithmNode() { instance.asAlg() instanceof ScryptAlgorithmInstance }
/**
* Gets the iteration count (`N`) argument

View File

@@ -1,5 +1,5 @@
name: codeql/quantum
version: 0.0.6-dev
version: 0.0.9-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 1.0.30
No user-facing changes.
## 1.0.29
No user-facing changes.
## 1.0.28
No user-facing changes.
## 1.0.27
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.28
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.27
lastReleaseVersion: 1.0.30

View File

@@ -1,5 +1,5 @@
name: codeql/rangeanalysis
version: 1.0.28-dev
version: 1.0.31-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 1.0.30
No user-facing changes.
## 1.0.29
No user-facing changes.
## 1.0.28
No user-facing changes.
## 1.0.27
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.28
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.27
lastReleaseVersion: 1.0.30

View File

@@ -1,5 +1,5 @@
name: codeql/regex
version: 1.0.28-dev
version: 1.0.31-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 2.0.6
No user-facing changes.
## 2.0.5
No user-facing changes.
## 2.0.4
No user-facing changes.
## 2.0.3
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.4
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.5
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.6
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 2.0.3
lastReleaseVersion: 2.0.6

View File

@@ -5,63 +5,14 @@
overlay[local?]
module;
private import codeql.controlflow.BasicBlock as BB
private import codeql.util.Location
private import codeql.util.Unit
signature class BasicBlockSig;
/** Provides the input specification of the SSA implementation. */
signature module InputSig<LocationSig Location> {
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock {
/** Gets a textual representation of this basic block. */
string toString();
/** Gets the `i`th node in this basic block. */
ControlFlowNode getNode(int i);
/** Gets the length of this basic block. */
int length();
/** Gets the location of this basic block. */
Location getLocation();
}
/** A control flow node. */
class ControlFlowNode {
/** Gets a textual representation of this control flow node. */
string toString();
/** Gets the location of this control flow node. */
Location getLocation();
}
/**
* Gets the basic block that immediately dominates basic block `bb`, if any.
*
* That is, all paths reaching `bb` from some entry point basic block must go
* through the result.
*
* Example:
*
* ```csharp
* int M(string s) {
* if (s == null)
* throw new ArgumentNullException(nameof(s));
* return s.Length;
* }
* ```
*
* The basic block starting on line 2 is an immediate dominator of
* the basic block on line 4 (all paths from the entry point of `M`
* to `return s.Length;` must go through the null check.
*/
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb);
/** Gets an immediate successor of basic block `bb`, if any. */
BasicBlock getABasicBlockSuccessor(BasicBlock bb);
signature module InputSig<LocationSig Location, BasicBlockSig BasicBlock> {
/** A variable that can be SSA converted. */
class SourceVariable {
/** Gets a textual representation of this variable. */
@@ -108,12 +59,13 @@ signature module InputSig<LocationSig Location> {
* NB: If this predicate is exposed, it should be cached.
* ```
*/
module Make<LocationSig Location, InputSig<Location> Input> {
module Make<
LocationSig Location, BB::CfgSig<Location> Cfg, InputSig<Location, Cfg::BasicBlock> Input>
{
private import Cfg
private import Input
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) {
getABasicBlockSuccessor(result) = bb
}
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { result.getASuccessor() = bb }
/**
* A classification of variable references into reads and
@@ -237,9 +189,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/**
* Holds if source variable `v` is live at the end of basic block `bb`.
*/
predicate liveAtExit(BasicBlock bb, SourceVariable v) {
liveAtEntry(getABasicBlockSuccessor(bb), v)
}
predicate liveAtExit(BasicBlock bb, SourceVariable v) { liveAtEntry(bb.getASuccessor(), v) }
/**
* Holds if variable `v` is live in basic block `bb` at rank `rnk`.
@@ -270,25 +220,6 @@ module Make<LocationSig Location, InputSig<Location> Input> {
private import Liveness
/**
* Holds if `df` is in the dominance frontier of `bb`.
*
* This is equivalent to:
*
* ```ql
* bb = getImmediateBasicBlockDominator*(getABasicBlockPredecessor(df)) and
* not bb = getImmediateBasicBlockDominator+(df)
* ```
*/
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
bb = getABasicBlockPredecessor(df) and not bb = getImmediateBasicBlockDominator(df)
or
exists(BasicBlock prev | inDominanceFrontier(prev, df) |
bb = getImmediateBasicBlockDominator(prev) and
not bb = getImmediateBasicBlockDominator(df)
)
}
/**
* Holds if `bb` is in the dominance frontier of a block containing a
* definition of `v`.
@@ -297,7 +228,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
exists(BasicBlock defbb, Definition def |
def.definesAt(v, defbb, _) and
inDominanceFrontier(defbb, bb)
defbb.inDominanceFrontier(bb)
)
}
@@ -307,7 +238,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
*/
pragma[nomagic]
private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) {
exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) |
exists(BasicBlock readbb | readbb.inDominanceFrontier(bb) |
ssaDefReachesRead(v, _, readbb, _) and
variableRead(readbb, _, v, true) and
not variableWrite(readbb, _, v, _)
@@ -389,7 +320,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
*/
pragma[nomagic]
private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) {
idom = getImmediateBasicBlockDominator(bb) and
idom = bb.getImmediateDominator() and
liveAtExit(bb, v) and
not any(Definition def).definesAt(v, bb, _)
}
@@ -439,7 +370,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
ssaDefReachesReadWithinBlock(v, def, bb, i)
or
ssaRef(bb, i, v, Read()) and
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, v) and
ssaDefReachesEndOfBlock(bb.getImmediateDominator(), def, v) and
not ssaDefReachesReadWithinBlock(v, _, bb, i)
}
@@ -483,7 +414,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
*/
pragma[nomagic]
private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) {
idom = getImmediateBasicBlockDominator(bb) and
idom = bb.getImmediateDominator() and
liveAtExit(bb, v) and
not ssaRef(bb, _, v, _)
}
@@ -517,7 +448,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
bb1 = bb2 and
refRank(bb1, i1, v, _) + 1 = refRank(bb2, i2, v, Read())
or
refReachesEndOfBlock(bb1, i1, getImmediateBasicBlockDominator(bb2), v) and
refReachesEndOfBlock(bb1, i1, bb2.getImmediateDominator(), v) and
1 = refRank(bb2, i2, v, Read())
}
@@ -808,12 +739,12 @@ module Make<LocationSig Location, InputSig<Location> Input> {
DefinitionExt def, SourceVariable v, BasicBlock bb1, BasicBlock bb2
) {
defOccursInBlock(def, bb1, v, _) and
bb2 = getABasicBlockSuccessor(bb1)
bb2 = bb1.getASuccessor()
or
exists(BasicBlock mid |
varBlockReachesExt(def, v, bb1, mid) and
ssaDefReachesThroughBlock(def, mid) and
bb2 = getABasicBlockSuccessor(mid)
bb2 = mid.getASuccessor()
)
}
@@ -943,7 +874,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
ssaDefReachesEndOfBlockExt0(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
ssaDefReachesEndOfBlockExt0(bb.getImmediateDominator(), def, pragma[only_bind_into](v)) and
liveThroughExt(bb, pragma[only_bind_into](v))
}
@@ -1150,7 +1081,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
predicate uncertainWriteDefinitionInput = SsaDefReachesNew::uncertainWriteDefinitionInput/2;
/** Holds if `bb` is a control-flow exit point. */
private predicate exitBlock(BasicBlock bb) { not exists(getABasicBlockSuccessor(bb)) }
private predicate exitBlock(BasicBlock bb) { not exists(bb.getASuccessor()) }
/**
* NB: If this predicate is exposed, it should be cached.
@@ -1418,7 +1349,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
or
ssaDefReachesRead(v, def, bb, i) and
not SsaDefReachesNew::ssaDefReachesReadWithinBlock(v, def, bb, i) and
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
not def.definesAt(v, bb.getImmediateDominator*(), _)
)
}
@@ -1566,23 +1497,29 @@ module Make<LocationSig Location, InputSig<Location> Input> {
*/
default predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) { none() }
/** An abstract value that a `Guard` may evaluate to. */
class GuardValue {
/** Gets a textual representation of this value. */
string toString();
}
/** A (potential) guard. */
class Guard {
/** Gets a textual representation of this guard. */
string toString();
/**
* Holds if the evaluation of this guard to `branch` corresponds to the edge
* Holds if the evaluation of this guard to `val` corresponds to the edge
* from `bb1` to `bb2`.
*/
predicate hasBranchEdge(BasicBlock bb1, BasicBlock bb2, boolean branch);
predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val);
/**
* Holds if this guard evaluating to `branch` controls the control-flow
* Holds if this guard evaluating to `val` controls the control-flow
* branch edge from `bb1` to `bb2`. That is, following the edge from
* `bb1` to `bb2` implies that this guard evaluated to `branch`.
* `bb1` to `bb2` implies that this guard evaluated to `val`.
*
* This predicate differs from `hasBranchEdge` in that it also covers
* This predicate differs from `hasValueBranchEdge` in that it also covers
* indirect guards, such as:
* ```
* b = guard;
@@ -1590,15 +1527,15 @@ module Make<LocationSig Location, InputSig<Location> Input> {
* if (b) { ... }
* ```
*/
predicate controlsBranchEdge(BasicBlock bb1, BasicBlock bb2, boolean branch);
predicate valueControlsBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val);
}
/** Holds if `guard` directly controls block `bb` upon evaluating to `branch`. */
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, boolean branch);
/** Holds if `guard` directly controls block `bb` upon evaluating to `val`. */
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, GuardValue val);
/** Holds if `guard` controls block `bb` upon evaluating to `branch`. */
default predicate guardControlsBlock(Guard guard, BasicBlock bb, boolean branch) {
guardDirectlyControlsBlock(guard, bb, branch)
/** Holds if `guard` controls block `bb` upon evaluating to `val`. */
default predicate guardControlsBlock(Guard guard, BasicBlock bb, GuardValue val) {
guardDirectlyControlsBlock(guard, bb, val)
}
/**
@@ -1661,7 +1598,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
DfInput::keepAllPhiInputBackEdges() and
exists(getAPhiInputDef(phi, input)) and
phi.getBasicBlock() = bbPhi and
getImmediateBasicBlockDominator+(input) = bbPhi
input.getImmediateDominator+() = bbPhi
)
}
@@ -1683,7 +1620,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
(
// The input node is relevant either if it sits directly on a branch
// edge for a guard,
exists(DfInput::Guard g | g.hasBranchEdge(input, phi.getBasicBlock(), _))
exists(DfInput::Guard g | g.hasValueBranchEdge(input, phi.getBasicBlock(), _))
or
// or if the unique predecessor is not an equivalent substitute in
// terms of being controlled by the same guards.
@@ -1702,9 +1639,9 @@ module Make<LocationSig Location, InputSig<Location> Input> {
AdjacentSsaRefs::adjacentRefPhi(prev, _, input, phi.getBasicBlock(),
phi.getSourceVariable()) and
prev != input and
exists(DfInput::Guard g, boolean branch |
DfInput::guardDirectlyControlsBlock(g, input, branch) and
not DfInput::guardDirectlyControlsBlock(g, prev, branch)
exists(DfInput::Guard g, DfInput::GuardValue val |
DfInput::guardDirectlyControlsBlock(g, input, val) and
not DfInput::guardDirectlyControlsBlock(g, prev, val)
)
)
)
@@ -2118,13 +2055,13 @@ module Make<LocationSig Location, InputSig<Location> Input> {
}
/**
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
* Holds if the guard `g` validates the expression `e` upon evaluating to `val`.
*
* The expression `e` is expected to be a syntactic part of the guard `g`.
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
* the argument `x`.
*/
signature predicate guardChecksSig(DfInput::Guard g, DfInput::Expr e, boolean branch);
signature predicate guardChecksSig(DfInput::Guard g, DfInput::Expr e, DfInput::GuardValue val);
pragma[nomagic]
private Definition getAPhiInputDef(SsaInputNodeImpl n) {
@@ -2139,7 +2076,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
private module WithState<StateSig State> {
/**
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`, blocking
* Holds if the guard `g` validates the expression `e` upon evaluating to `val`, blocking
* flow in the given `state`.
*
* The expression `e` is expected to be a syntactic part of the guard `g`.
@@ -2147,15 +2084,15 @@ module Make<LocationSig Location, InputSig<Location> Input> {
* the argument `x`.
*/
signature predicate guardChecksSig(
DfInput::Guard g, DfInput::Expr e, boolean branch, State state
DfInput::Guard g, DfInput::Expr e, DfInput::GuardValue val, State state
);
/**
* Holds if the guard `g` validates the SSA definition `def` upon
* evaluating to `branch`, blocking flow in the given `state`.
* evaluating to `val`, blocking flow in the given `state`.
*/
signature predicate guardChecksDefSig(
DfInput::Guard g, Definition def, boolean branch, State state
DfInput::Guard g, Definition def, DfInput::GuardValue val, State state
);
}
@@ -2167,9 +2104,9 @@ module Make<LocationSig Location, InputSig<Location> Input> {
*/
module BarrierGuard<guardChecksSig/3 guardChecks> {
private predicate guardChecksWithState(
DfInput::Guard g, DfInput::Expr e, boolean branch, Unit state
DfInput::Guard g, DfInput::Expr e, DfInput::GuardValue val, Unit state
) {
guardChecks(g, e, branch) and exists(state)
guardChecks(g, e, val) and exists(state)
}
private module StatefulBarrier = BarrierGuardWithState<Unit, guardChecksWithState/4>;
@@ -2188,9 +2125,9 @@ module Make<LocationSig Location, InputSig<Location> Input> {
module BarrierGuardWithState<StateSig State, WithState<State>::guardChecksSig/4 guardChecks> {
pragma[nomagic]
private predicate guardChecksSsaDef(
DfInput::Guard g, Definition def, boolean branch, State state
DfInput::Guard g, Definition def, DfInput::GuardValue val, State state
) {
guardChecks(g, DfInput::getARead(def), branch, state)
guardChecks(g, DfInput::getARead(def), val, state)
}
private module Barrier = BarrierGuardDefWithState<State, guardChecksSsaDef/4>;
@@ -2210,14 +2147,14 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Gets a node that is safely guarded by the given guard check. */
pragma[nomagic]
Node getABarrierNode(State state) {
exists(DfInput::Guard g, boolean branch, Definition def, BasicBlock bb |
guardChecksSsaDef(g, def, branch, state)
exists(DfInput::Guard g, DfInput::GuardValue val, Definition def, BasicBlock bb |
guardChecksSsaDef(g, def, val, state)
|
// guard controls a read
exists(DfInput::Expr e |
e = DfInput::getARead(def) and
e.hasCfgNode(bb, _) and
DfInput::guardControlsBlock(g, bb, branch) and
DfInput::guardControlsBlock(g, bb, val) and
result.(ExprNode).getExpr() = e
)
or
@@ -2226,9 +2163,9 @@ module Make<LocationSig Location, InputSig<Location> Input> {
def = getAPhiInputDef(result) and
result.(SsaInputNodeImpl).isInputInto(phi, bb)
|
DfInput::guardControlsBlock(g, bb, branch)
DfInput::guardControlsBlock(g, bb, val)
or
g.controlsBranchEdge(bb, phi.getBasicBlock(), branch)
g.valueControlsBranchEdge(bb, phi.getBasicBlock(), val)
)
)
}

View File

@@ -1,7 +1,8 @@
name: codeql/ssa
version: 2.0.4-dev
version: 2.0.7-dev
groups: shared
library: true
dependencies:
codeql/controlflow: ${workspace}
codeql/util: ${workspace}
warnOnImplicitThis: true

View File

@@ -1,3 +1,15 @@
## 1.0.30
No user-facing changes.
## 1.0.29
No user-facing changes.
## 1.0.28
No user-facing changes.
## 1.0.27
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.28
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.27
lastReleaseVersion: 1.0.30

View File

@@ -1,5 +1,5 @@
name: codeql/threat-models
version: 1.0.28-dev
version: 1.0.31-dev
library: true
groups: shared
dataExtensions:

View File

@@ -24,4 +24,4 @@ zstd = "0.13.3"
[dev-dependencies]
tree-sitter-ql = "0.23.1"
tree-sitter-json = "0.24.8"
rand = "0.9.1"
rand = "0.9.2"

View File

@@ -57,23 +57,23 @@ impl Autobuilder {
let verbosity = env::var("CODEQL_VERBOSITY");
if let Ok(verbosity) = verbosity {
cmd.arg(format!("--verbosity={}", verbosity));
cmd.arg(format!("--verbosity={verbosity}"));
}
for ext in &self.include_extensions {
cmd.arg(format!("--include-extension={}", ext));
cmd.arg(format!("--include-extension={ext}"));
}
for glob in &self.include_globs {
cmd.arg(format!("--include={}", glob));
cmd.arg(format!("--include={glob}"));
}
for glob in &self.exclude_globs {
cmd.arg(format!("--exclude={}", glob));
cmd.arg(format!("--exclude={glob}"));
}
if let Some(limit) = &self.size_limit {
cmd.arg(format!("--size-limit={}", limit));
cmd.arg(format!("--size-limit={limit}"));
}
cmd.arg(format!("--language={}", &self.language));

View File

@@ -194,7 +194,7 @@ impl DiagnosticLoggers {
path: self
.root
.as_ref()
.map(|root| root.to_owned().join(format!("extractor_{}.jsonl", n))),
.map(|root| root.to_owned().join(format!("extractor_{n}.jsonl"))),
})
}
}

View File

@@ -230,7 +230,7 @@ pub fn extract(
parser.set_language(language).unwrap();
parser.set_included_ranges(ranges).unwrap();
let tree = parser.parse(source, None).expect("Failed to parse file");
trap_writer.comment(format!("Auto-generated TRAP file for {}", path_str));
trap_writer.comment(format!("Auto-generated TRAP file for {path_str}"));
let file_label = populate_file(trap_writer, path, transformer);
let mut visitor = Visitor::new(
source,
@@ -298,9 +298,9 @@ impl<'a> Visitor<'a> {
source,
diagnostics_writer,
trap_writer,
ast_node_location_table_name: format!("{}_ast_node_location", language_prefix),
ast_node_parent_table_name: format!("{}_ast_node_parent", language_prefix),
tokeninfo_table_name: format!("{}_tokeninfo", language_prefix),
ast_node_location_table_name: format!("{language_prefix}_ast_node_location"),
ast_node_parent_table_name: format!("{language_prefix}_ast_node_parent"),
tokeninfo_table_name: format!("{language_prefix}_tokeninfo"),
schema,
stack: Vec::new(),
}

View File

@@ -80,7 +80,7 @@ impl Extractor {
.iter()
.map(|file_list| {
File::open(file_list)
.unwrap_or_else(|_| panic!("Unable to open file list at {:?}", file_list))
.unwrap_or_else(|_| panic!("Unable to open file list at {file_list:?}"))
})
.collect();

View File

@@ -53,7 +53,7 @@ impl fmt::Display for Case<'_> {
writeln!(f, "case @{}.{} of", &self.name, &self.column)?;
let mut sep = " ";
for (c, tp) in &self.branches {
writeln!(f, "{} {} = @{}", sep, c, tp)?;
writeln!(f, "{sep} {c} = @{tp}")?;
sep = "|";
}
writeln!(f, ";")
@@ -68,7 +68,7 @@ impl fmt::Display for Table<'_> {
if key_index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", key)?;
write!(f, "{key}")?;
}
writeln!(f, "]")?;
}
@@ -112,7 +112,7 @@ impl fmt::Display for Union<'_> {
} else {
write!(f, " | ")?;
}
write!(f, "@{}", member)?;
write!(f, "@{member}")?;
}
Ok(())
}
@@ -122,9 +122,9 @@ impl fmt::Display for Union<'_> {
pub fn write(file: &mut dyn std::io::Write, entries: &[Entry]) -> std::io::Result<()> {
for entry in entries {
match entry {
Entry::Case(case) => write!(file, "{}\n\n", case)?,
Entry::Table(table) => write!(file, "{}\n\n", table)?,
Entry::Union(union) => write!(file, "{}\n\n", union)?,
Entry::Case(case) => write!(file, "{case}\n\n")?,
Entry::Table(table) => write!(file, "{table}\n\n")?,
Entry::Union(union) => write!(file, "{union}\n\n")?,
}
}

View File

@@ -259,8 +259,8 @@ fn add_field_for_column_storage<'a>(
/// values are their integer representations.
fn convert_nodes(
nodes: &node_types::NodeTypeMap,
) -> (Vec<dbscheme::Entry>, Set<&str>, Map<&str, usize>) {
let mut entries: Vec<dbscheme::Entry> = Vec::new();
) -> (Vec<dbscheme::Entry<'_>>, Set<&str>, Map<&str, usize>) {
let mut entries = Vec::new();
let mut ast_node_members: Set<&str> = Set::new();
let token_kinds: Map<&str, usize> = nodes
.iter()

View File

@@ -12,10 +12,10 @@ pub enum TopLevel<'a> {
impl fmt::Display for TopLevel<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TopLevel::Import(imp) => write!(f, "{}", imp),
TopLevel::Class(cls) => write!(f, "{}", cls),
TopLevel::Module(m) => write!(f, "{}", m),
TopLevel::Predicate(pred) => write!(f, "{}", pred),
TopLevel::Import(imp) => write!(f, "{imp}"),
TopLevel::Class(cls) => write!(f, "{cls}"),
TopLevel::Module(m) => write!(f, "{m}"),
TopLevel::Predicate(pred) => write!(f, "{pred}"),
}
}
}
@@ -30,7 +30,7 @@ impl fmt::Display for Import<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "import {}", &self.module)?;
if let Some(name) = &self.alias {
write!(f, " as {}", name)?;
write!(f, " as {name}")?;
}
Ok(())
}
@@ -48,7 +48,7 @@ pub struct Class<'a> {
impl fmt::Display for Class<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(qldoc) = &self.qldoc {
write!(f, "/** {} */", qldoc)?;
write!(f, "/** {qldoc} */")?;
}
if self.is_abstract {
write!(f, "abstract ")?;
@@ -58,7 +58,7 @@ impl fmt::Display for Class<'_> {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", supertype)?;
write!(f, "{supertype}")?;
}
writeln!(f, " {{ ")?;
@@ -81,7 +81,7 @@ impl fmt::Display for Class<'_> {
}
for predicate in &self.predicates {
writeln!(f, " {}", predicate)?;
writeln!(f, " {predicate}")?;
}
write!(f, "}}")?;
@@ -101,7 +101,7 @@ pub struct Module<'a> {
impl fmt::Display for Module<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(qldoc) = &self.qldoc {
write!(f, "/** {} */", qldoc)?;
write!(f, "/** {qldoc} */")?;
}
if let Some(overlay_annotation) = &self.overlay {
write!(f, "overlay[")?;
@@ -113,7 +113,7 @@ impl fmt::Display for Module<'_> {
}
writeln!(f, "module {} {{ ", self.name)?;
for decl in &self.body {
writeln!(f, " {}", decl)?;
writeln!(f, " {decl}")?;
}
write!(f, "}}")?;
Ok(())
@@ -140,8 +140,8 @@ impl fmt::Display for Type<'_> {
match self {
Type::Int => write!(f, "int"),
Type::String => write!(f, "string"),
Type::Normal(name) => write!(f, "{}", name),
Type::At(name) => write!(f, "@{}", name),
Type::Normal(name) => write!(f, "{name}"),
Type::At(name) => write!(f, "@{name}"),
}
}
}
@@ -169,16 +169,16 @@ pub enum Expression<'a> {
impl fmt::Display for Expression<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expression::Var(x) => write!(f, "{}", x),
Expression::String(s) => write!(f, "\"{}\"", s),
Expression::Integer(n) => write!(f, "{}", n),
Expression::Var(x) => write!(f, "{x}"),
Expression::String(s) => write!(f, "\"{s}\""),
Expression::Integer(n) => write!(f, "{n}"),
Expression::Pred(n, args) => {
write!(f, "{}(", n)?;
write!(f, "{n}(")?;
for (index, arg) in args.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
write!(f, "{arg}")?;
}
write!(f, ")")
}
@@ -190,7 +190,7 @@ impl fmt::Display for Expression<'_> {
if index > 0 {
write!(f, " and ")?;
}
write!(f, "({})", conjunct)?;
write!(f, "({conjunct})")?;
}
Ok(())
}
@@ -203,19 +203,19 @@ impl fmt::Display for Expression<'_> {
if index > 0 {
write!(f, " or ")?;
}
write!(f, "({})", disjunct)?;
write!(f, "({disjunct})")?;
}
Ok(())
}
}
Expression::Equals(a, b) => write!(f, "{} = {}", a, b),
Expression::Equals(a, b) => write!(f, "{a} = {b}"),
Expression::Dot(x, member_pred, args) => {
write!(f, "{}.{}(", x, member_pred)?;
write!(f, "{x}.{member_pred}(")?;
for (index, arg) in args.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
write!(f, "{arg}")?;
}
write!(f, ")")
}
@@ -226,26 +226,26 @@ impl fmt::Display for Expression<'_> {
expr,
second_expr,
} => {
write!(f, "{}(", name)?;
write!(f, "{name}(")?;
if !vars.is_empty() {
for (index, var) in vars.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", var)?;
write!(f, "{var}")?;
}
write!(f, " | ")?;
}
if let Some(range) = range {
write!(f, "{} | ", range)?;
write!(f, "{range} | ")?;
}
write!(f, "{}", expr)?;
write!(f, "{expr}")?;
if let Some(second_expr) = second_expr {
write!(f, ", {}", second_expr)?;
write!(f, ", {second_expr}")?;
}
write!(f, ")")
}
Expression::Negation(e) => write!(f, "not ({})", e),
Expression::Negation(e) => write!(f, "not ({e})"),
}
}
}
@@ -272,7 +272,7 @@ pub struct Predicate<'a> {
impl fmt::Display for Predicate<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(qldoc) = &self.qldoc {
write!(f, "/** {} */", qldoc)?;
write!(f, "/** {qldoc} */")?;
}
if let Some(overlay_annotation) = &self.overlay {
write!(f, "overlay[")?;
@@ -293,14 +293,14 @@ impl fmt::Display for Predicate<'_> {
}
match &self.return_type {
None => write!(f, "predicate ")?,
Some(return_type) => write!(f, "{} ", return_type)?,
Some(return_type) => write!(f, "{return_type} ")?,
}
write!(f, "{}(", self.name)?;
for (index, param) in self.formal_parameters.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", param)?;
write!(f, "{param}")?;
}
write!(f, ") {{ {} }}", self.body)?;

View File

@@ -200,7 +200,7 @@ pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Cl
}
// Creates the `ReservedWord` class.
pub fn create_reserved_word_class(db_name: &str) -> ql::Class {
pub fn create_reserved_word_class(db_name: &str) -> ql::Class<'_> {
let class_name = "ReservedWord";
let get_a_primary_ql_class = create_get_a_primary_ql_class(class_name, true);
ql::Class {
@@ -237,7 +237,7 @@ fn create_none_predicate<'a>(
/// Creates an overridden `getAPrimaryQlClass` predicate that returns the given
/// name.
fn create_get_a_primary_ql_class(class_name: &str, is_final: bool) -> ql::Predicate {
fn create_get_a_primary_ql_class(class_name: &str, is_final: bool) -> ql::Predicate<'_> {
ql::Predicate {
qldoc: Some(String::from(
"Gets the name of the primary QL class for this element.",
@@ -318,7 +318,7 @@ pub fn create_get_node_file_predicate<'a>(
}
}
pub fn create_discardable_ast_node_predicate(ast_node_name: &str) -> ql::Predicate {
pub fn create_discardable_ast_node_predicate(ast_node_name: &str) -> ql::Predicate<'_> {
ql::Predicate {
name: "discardableAstNode",
qldoc: Some(String::from(
@@ -352,7 +352,7 @@ pub fn create_discardable_ast_node_predicate(ast_node_name: &str) -> ql::Predica
}
}
pub fn create_discard_ast_node_predicate(ast_node_name: &str) -> ql::Predicate {
pub fn create_discard_ast_node_predicate(ast_node_name: &str) -> ql::Predicate<'_> {
ql::Predicate {
name: "discardAstNode",
qldoc: Some(String::from(
@@ -666,7 +666,7 @@ fn create_field_getters<'a>(
}
};
let qldoc = match &field.name {
Some(name) => format!("Gets the node corresponding to the field `{}`.", name),
Some(name) => format!("Gets the node corresponding to the field `{name}`."),
None => {
if formal_parameters.is_empty() {
"Gets the child of this node.".to_owned()
@@ -692,8 +692,8 @@ fn create_field_getters<'a>(
}
/// Converts the given node types into CodeQL classes wrapping the dbscheme.
pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel> {
let mut classes: Vec<ql::TopLevel> = Vec::new();
pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
let mut classes = Vec::new();
let mut token_kinds = BTreeSet::new();
for (type_name, node) in nodes {
if let node_types::EntryKind::Token { .. } = &node.kind {

View File

@@ -326,7 +326,7 @@ fn node_type_name(kind: &str, named: bool) -> String {
if named {
kind.to_string()
} else {
format!("{}_unnamed", kind)
format!("{kind}_unnamed")
}
}

View File

@@ -112,7 +112,7 @@ impl Writer {
fn write_trap_entries<W: Write>(&self, file: &mut W) -> std::io::Result<()> {
for trap_entry in &self.trap_output {
writeln!(file, "{}", trap_entry)?;
writeln!(file, "{trap_entry}")?;
}
Ok(())
}
@@ -131,21 +131,21 @@ pub enum Entry {
impl fmt::Display for Entry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Entry::FreshId(label) => write!(f, "{}=*", label),
Entry::FreshId(label) => write!(f, "{label}=*"),
Entry::MapLabelToKey(label, key) => {
write!(f, "{}=@\"{}\"", label, key.replace('"', "\"\""))
}
Entry::GenericTuple(name, args) => {
write!(f, "{}(", name)?;
write!(f, "{name}(")?;
for (index, arg) in args.iter().enumerate() {
if index > 0 {
write!(f, ",")?;
}
write!(f, "{}", arg)?;
write!(f, "{arg}")?;
}
write!(f, ")")
}
Entry::Comment(line) => write!(f, "// {}", line),
Entry::Comment(line) => write!(f, "// {line}"),
}
}
}
@@ -179,8 +179,8 @@ const MAX_STRLEN: usize = 1048576;
impl fmt::Display for Arg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Arg::Label(x) => write!(f, "{}", x),
Arg::Int(x) => write!(f, "{}", x),
Arg::Label(x) => write!(f, "{x}"),
Arg::Int(x) => write!(f, "{x}"),
Arg::String(x) => write!(
f,
"\"{}\"",
@@ -220,9 +220,9 @@ impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut text = String::new();
for trap_entry in &self.0 {
text.push_str(&format!("{}\n", trap_entry));
text.push_str(&format!("{trap_entry}\n"));
}
write!(f, "{}", text)
write!(f, "{text}")
}
}

View File

@@ -1,3 +1,15 @@
## 1.0.30
No user-facing changes.
## 1.0.29
No user-facing changes.
## 1.0.28
No user-facing changes.
## 1.0.27
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.28
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.27
lastReleaseVersion: 1.0.30

View File

@@ -1,7 +1,7 @@
name: codeql/tutorial
description: Library for the CodeQL detective tutorials, helping new users learn to
write CodeQL queries.
version: 1.0.28-dev
version: 1.0.31-dev
groups: shared
library: true
warnOnImplicitThis: true

View File

@@ -1,3 +1,15 @@
## 1.0.30
No user-facing changes.
## 1.0.29
No user-facing changes.
## 1.0.28
No user-facing changes.
## 1.0.27
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.28
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.27
lastReleaseVersion: 1.0.30

View File

@@ -1,5 +1,5 @@
name: codeql/typeflow
version: 1.0.28-dev
version: 1.0.31-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 0.0.11
No user-facing changes.
## 0.0.10
No user-facing changes.
## 0.0.9
No user-facing changes.
## 0.0.8
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.10
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.11
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.0.9
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.8
lastReleaseVersion: 0.0.11

View File

@@ -334,7 +334,19 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
/** Holds if this path starts with `tp`, followed by `suffix`. */
bindingset[this]
predicate isCons(TypeParameter tp, TypePath suffix) {
suffix = this.stripPrefix(TypePath::singleton(tp))
exists(string regexp | regexp = "([0-9]+)\\.(.*)" |
tp = TypeParameter::decode(this.regexpCapture(regexp, 1)) and
suffix = this.regexpCapture(regexp, 2)
)
}
/** Holds if this path starts with `prefix`, followed by `tp`. */
bindingset[this]
predicate isSnoc(TypePath prefix, TypeParameter tp) {
exists(string regexp | regexp = "(|.+\\.)([0-9]+)\\." |
prefix = this.regexpCapture(regexp, 1) and
tp = TypeParameter::decode(this.regexpCapture(regexp, 2))
)
}
/** Gets the head of this path, if any. */
@@ -719,20 +731,24 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
IsInstantiationOfInputSig<TypeMentionTypeTree>
{
pragma[nomagic]
private predicate typeCondition(Type type, TypeAbstraction abs, TypeMentionTypeTree lhs) {
conditionSatisfiesConstraint(abs, lhs, _) and type = resolveTypeMentionRoot(lhs)
private predicate typeCondition(
Type type, TypeAbstraction abs, TypeMentionTypeTree condition
) {
conditionSatisfiesConstraint(abs, condition, _) and
type = resolveTypeMentionRoot(condition)
}
pragma[nomagic]
private predicate typeConstraint(Type type, TypeMentionTypeTree rhs) {
conditionSatisfiesConstraint(_, _, rhs) and type = resolveTypeMentionRoot(rhs)
private predicate typeConstraint(Type type, TypeMentionTypeTree constraint) {
conditionSatisfiesConstraint(_, _, constraint) and
type = resolveTypeMentionRoot(constraint)
}
predicate potentialInstantiationOf(
TypeMentionTypeTree condition, TypeAbstraction abs, TypeMention constraint
TypeMentionTypeTree constraint, TypeAbstraction abs, TypeMention condition
) {
exists(Type type |
typeConstraint(type, condition) and typeCondition(type, abs, constraint)
typeConstraint(type, constraint) and typeCondition(type, abs, condition)
)
}
}
@@ -749,20 +765,20 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
constraint.resolveTypeAt(path) = t
or
// recursive case
exists(TypeAbstraction midAbs, TypeMention midSup, TypeMention midSub |
conditionSatisfiesConstraint(abs, condition, midSup) and
// NOTE: `midAbs` describe the free type variables in `midSub`, hence
exists(TypeAbstraction midAbs, TypeMention midConstraint, TypeMention midCondition |
conditionSatisfiesConstraint(abs, condition, midConstraint) and
// NOTE: `midAbs` describe the free type variables in `midCondition`, hence
// we use that for instantiation check.
IsInstantiationOf<TypeMentionTypeTree, IsInstantiationOfInput>::isInstantiationOf(midSup,
midAbs, midSub)
IsInstantiationOf<TypeMentionTypeTree, IsInstantiationOfInput>::isInstantiationOf(midConstraint,
midAbs, midCondition)
|
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, path, t) and
conditionSatisfiesConstraintTypeAt(midAbs, midCondition, constraint, path, t) and
not t = midAbs.getATypeParameter()
or
exists(TypePath prefix, TypePath suffix, TypeParameter tp |
tp = midAbs.getATypeParameter() and
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, prefix, tp) and
instantiatesWith(midSup, midSub, tp, suffix, t) and
conditionSatisfiesConstraintTypeAt(midAbs, midCondition, constraint, prefix, tp) and
instantiatesWith(midConstraint, midCondition, tp, suffix, t) and
path = prefix.append(suffix)
)
)
@@ -937,23 +953,24 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*/
pragma[nomagic]
private predicate hasConstraintMention(
HasTypeTree tt, TypeAbstraction abs, TypeMention sub, Type constraint,
HasTypeTree tt, TypeAbstraction abs, TypeMention condition, Type constraint,
TypeMention constraintMention
) {
exists(Type type | hasTypeConstraint(tt, type, constraint) |
not exists(countConstraintImplementations(type, constraint)) and
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, _, _) and
resolveTypeMentionRoot(sub) = abs.getATypeParameter() and
conditionSatisfiesConstraintTypeAt(abs, condition, constraintMention, _, _) and
resolveTypeMentionRoot(condition) = abs.getATypeParameter() and
constraint = resolveTypeMentionRoot(constraintMention)
or
countConstraintImplementations(type, constraint) > 0 and
rootTypesSatisfaction(type, constraint, abs, sub, constraintMention) and
rootTypesSatisfaction(type, constraint, abs, condition, constraintMention) and
// When there are multiple ways the type could implement the
// constraint we need to find the right implementation, which is the
// one where the type instantiates the precondition.
if multipleConstraintImplementations(type, constraint)
then
IsInstantiationOf<HasTypeTree, IsInstantiationOfInput>::isInstantiationOf(tt, abs, sub)
IsInstantiationOf<HasTypeTree, IsInstantiationOfInput>::isInstantiationOf(tt, abs,
condition)
else any()
)
}

View File

@@ -1,5 +1,5 @@
name: codeql/typeinference
version: 0.0.9-dev
version: 0.0.12-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 2.0.14
No user-facing changes.
## 2.0.13
No user-facing changes.
## 2.0.12
No user-facing changes.
## 2.0.11
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.12
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.13
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 2.0.14
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 2.0.11
lastReleaseVersion: 2.0.14

View File

@@ -1,5 +1,5 @@
name: codeql/typetracking
version: 2.0.12-dev
version: 2.0.15-dev
groups: shared
library: true
dependencies:

View File

@@ -1,3 +1,15 @@
## 1.0.30
No user-facing changes.
## 1.0.29
No user-facing changes.
## 1.0.28
No user-facing changes.
## 1.0.27
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.28
No user-facing changes.

Some files were not shown because too many files have changed in this diff Show More