Checkpoint from VS Code for cloud agent session

This commit is contained in:
Owen Mansel-Chan
2026-03-30 15:50:01 +01:00
parent 2a22399150
commit 7bf289ed1e
8 changed files with 3590 additions and 724 deletions

View File

@@ -5,66 +5,27 @@ overlay[local]
module;
import go
private import ControlFlowGraphImpl
private import codeql.controlflow.BasicBlock as BB
private import codeql.controlflow.SuccessorType
private import ControlFlowGraphShared
private module Input implements BB::InputSig<Location> {
/** A delineated part of the AST with its own CFG. */
class CfgScope = ControlFlow::Root;
/** A basic block in the control-flow graph. */
class BasicBlock = GoCfg::Cfg::BasicBlock;
/** The class of control flow nodes. */
class Node = ControlFlowNode;
/** Gets the CFG scope in which this node occurs. */
CfgScope nodeGetCfgScope(Node node) { node.getRoot() = result }
/** Gets an immediate successor of this node. */
Node nodeGetASuccessor(Node node, SuccessorType t) {
result = node.getASuccessor() and
(
not result instanceof ControlFlow::ConditionGuardNode and t instanceof DirectSuccessor
or
t.(BooleanSuccessor).getValue() = result.(ControlFlow::ConditionGuardNode).getOutcome()
)
}
/**
* Holds if `node` represents an entry node to be used when calculating
* dominance.
*/
predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode }
/**
* Holds if `node` represents an exit node to be used when calculating
* post dominance.
*/
predicate nodeIsPostDominanceExit(Node node) { node instanceof ExitNode }
}
private module BbImpl = BB::Make<Location, Input>;
class BasicBlock = BbImpl::BasicBlock;
class EntryBasicBlock = BbImpl::EntryBasicBlock;
cached
private predicate reachableBB(BasicBlock bb) {
bb instanceof EntryBasicBlock
or
exists(BasicBlock predBB | predBB.getASuccessor(_) = bb | reachableBB(predBB))
}
/** An entry basic block. */
class EntryBasicBlock = GoCfg::Cfg::EntryBasicBlock;
/**
* A basic block that is reachable from an entry basic block.
*
* Since the shared CFG library only creates nodes for reachable code,
* all basic blocks are reachable by construction.
*/
class ReachableBasicBlock extends BasicBlock {
ReachableBasicBlock() { reachableBB(this) }
ReachableBasicBlock() { any() }
}
/**
* A reachable basic block with more than one predecessor.
*/
class ReachableJoinBlock extends ReachableBasicBlock {
ReachableJoinBlock() { this.getFirstNode().isJoin() }
ReachableJoinBlock() { this.getFirstNode().(ControlFlow::Node).isJoin() }
}

View File

@@ -5,47 +5,35 @@ overlay[local]
module;
import go
private import ControlFlowGraphImpl
private import ControlFlowGraphShared
/** Provides helper predicates for mapping btween CFG nodes and the AST. */
/** Provides helper predicates for mapping between CFG nodes and the AST. */
module ControlFlow {
/** A file or function with which a CFG is associated. */
/** A function with which a CFG is associated. */
class Root extends AstNode {
Root() { exists(this.(File).getADecl()) or exists(this.(FuncDef).getBody()) }
Root() { exists(this.(FuncDef).getBody()) }
/** Holds if `nd` belongs to this file or function. */
predicate isRootOf(AstNode nd) {
this = nd.getEnclosingFunction()
or
not exists(nd.getEnclosingFunction()) and
this = nd.getFile()
}
/** Holds if `nd` belongs to this function. */
predicate isRootOf(AstNode nd) { this = nd.getEnclosingFunction() }
/** Gets the synthetic entry node of the CFG for this file or function. */
/** Gets the synthetic entry node of the CFG for this function. */
EntryNode getEntryNode() { result = ControlFlow::entryNode(this) }
/** Gets the synthetic exit node of the CFG for this file or function. */
/** Gets the synthetic exit node of the CFG for this function. */
ExitNode getExitNode() { result = ControlFlow::exitNode(this) }
}
/**
* A node in the intra-procedural control-flow graph of a Go function or file.
* A node in the intra-procedural control-flow graph of a Go function.
*
* Nodes correspond to expressions and statements that compute a value or perform
* an operation (as opposed to providing syntactic structure or type information).
*
* There are also synthetic entry and exit nodes for each Go function and file
* There are also synthetic entry and exit nodes for each Go function
* that mark the beginning and the end, respectively, of the execution of the
* function and the loading of the file.
* function.
*/
class Node extends TControlFlowNode {
/** Gets a node that directly follows this one in the control-flow graph. */
Node getASuccessor() { result = CFG::succ(this) }
/** Gets a node that directly precedes this one in the control-flow graph. */
Node getAPredecessor() { this = result.getASuccessor() }
class Node extends GoCfg::ControlFlowNode {
/** Holds if this is a node with more than one successor. */
predicate isBranch() { strictcount(this.getASuccessor()) > 1 }
@@ -53,22 +41,23 @@ module ControlFlow {
predicate isJoin() { strictcount(this.getAPredecessor()) > 1 }
/** Holds if this is the first control-flow node in `subtree`. */
predicate isFirstNodeOf(AstNode subtree) { CFG::firstNode(subtree, this) }
predicate isFirstNodeOf(AstNode subtree) {
this.isBefore(subtree)
or
this.injects(subtree)
}
/** Holds if this node is the (unique) entry node of a function or file. */
predicate isEntryNode() { this instanceof MkEntryNode }
/** Holds if this node is the (unique) entry node of a function. */
predicate isEntryNode() { this instanceof GoCfg::ControlFlow::EntryNode }
/** Holds if this node is the (unique) exit node of a function or file. */
predicate isExitNode() { this instanceof MkExitNode }
/** Gets the basic block to which this node belongs. */
BasicBlock getBasicBlock() { result.getANode() = this }
/** Holds if this node is the (unique) exit node of a function. */
predicate isExitNode() { this instanceof GoCfg::ControlFlow::ExitNode }
/** Holds if this node dominates `dominee` in the control-flow graph. */
overlay[caller?]
pragma[inline]
predicate dominatesNode(ControlFlow::Node dominee) {
exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j |
exists(GoCfg::Cfg::BasicBlock thisbb, GoCfg::Cfg::BasicBlock dbb, int i, int j |
this = thisbb.getNode(i) and dominee = dbb.getNode(j)
|
thisbb.strictlyDominates(dbb)
@@ -77,20 +66,12 @@ module ControlFlow {
)
}
/** Gets the innermost function or file to which this node belongs. */
Root getRoot() { none() }
/** Gets the innermost function to which this node belongs. */
Root getRoot() { result = this.getEnclosingCallable() }
/** Gets the file to which this node belongs. */
File getFile() { result = this.getLocation().getFile() }
/**
* Gets a textual representation of this control flow node.
*/
string toString() { result = "control-flow node" }
/** Gets the source location for this element. */
Location getLocation() { none() }
/**
* DEPRECATED: Use `getLocation()` instead.
*
@@ -114,6 +95,12 @@ module ControlFlow {
}
}
/** A synthetic entry node for a function. */
class EntryNode extends Node instanceof GoCfg::ControlFlow::EntryNode { }
/** A synthetic exit node for a function. */
class ExitNode extends Node instanceof GoCfg::ControlFlow::ExitNode { }
/**
* A control-flow node that initializes or updates the value of a constant, a variable,
* a field, or an (array, slice, or map) element.
@@ -173,7 +160,7 @@ module ControlFlow {
exists(IR::FieldTarget trg | trg = super.getLhs() |
(
trg.getBase() = base or
trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr())
trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr())
) and
trg.getField() = f and
super.getRhs() = rhs
@@ -221,7 +208,7 @@ module ControlFlow {
exists(IR::ElementTarget trg | trg = super.getLhs() |
(
trg.getBase() = base or
trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr())
trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr())
) and
trg.getIndex() = index and
super.getRhs() = rhs
@@ -251,11 +238,15 @@ module ControlFlow {
* A control-flow node recording the fact that a certain expression has a known
* Boolean value at this point in the program.
*/
class ConditionGuardNode extends IR::Instruction, MkConditionGuardNode {
class ConditionGuardNode extends Node {
Expr cond;
boolean outcome;
ConditionGuardNode() { this = MkConditionGuardNode(cond, outcome) }
ConditionGuardNode() {
this.isAfterTrue(cond) and outcome = true
or
this.isAfterFalse(cond) and outcome = false
}
private predicate ensuresAux(Expr expr, boolean b) {
expr = cond and b = outcome
@@ -321,21 +312,17 @@ module ControlFlow {
boolean getOutcome() { result = outcome }
override Root getRoot() { result.isRootOf(cond) }
override string toString() { result = cond + " is " + outcome }
override Location getLocation() { result = cond.getLocation() }
}
/**
* Gets the entry node of function or file `root`.
* Gets the entry node of function `root`.
*/
Node entryNode(Root root) { result = MkEntryNode(root) }
EntryNode entryNode(Root root) { result.getEnclosingCallable() = root }
/**
* Gets the exit node of function or file `root`.
* Gets the exit node of function `root`.
*/
Node exitNode(Root root) { result = MkExitNode(root) }
ExitNode exitNode(Root root) { result.getEnclosingCallable() = root }
/**
* Holds if the function `f` may return without panicking, exiting the process, or looping forever.
@@ -343,7 +330,9 @@ module ControlFlow {
* This is defined conservatively, and so may also hold of a function that in fact
* cannot return normally, but never fails to hold of a function that can return normally.
*/
predicate mayReturnNormally(FuncDecl f) { CFG::mayReturnNormally(f.getBody()) }
predicate mayReturnNormally(FuncDecl f) {
exists(GoCfg::ControlFlow::NormalExitNode exit | exit.getEnclosingCallable() = f)
}
/**
* Holds if `pred` is the node for the case `testExpr` in an expression
@@ -353,20 +342,16 @@ module ControlFlow {
predicate isSwitchCaseTestPassingEdge(
ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr
) {
CFG::isSwitchCaseTestPassingEdge(pred, succ, switchExpr, testExpr)
exists(ExpressionSwitchStmt ess, CaseClause cc, int i |
ess.getExpr() = switchExpr and
cc = ess.getACase() and
testExpr = cc.getExpr(i) and
pred.isAfter(testExpr) and
succ.isFirstNodeOf(cc.getStmt(0))
)
}
}
class ControlFlowNode = ControlFlow::Node;
class Write = ControlFlow::WriteNode;
/**
* Provides the shared CFG library types for Go.
*
* These types are generated by the shared `codeql.controlflow.ControlFlowGraph`
* library and coexist with the existing Go CFG types during the transition.
* Use `SharedCfg::ControlFlowNode` to access the shared library's node type,
* `SharedCfg::ControlFlow::EntryNode` for entry nodes, etc.
*/
module SharedCfg = GoCfg;

View File

@@ -25,6 +25,11 @@ module GoCfg {
private import Cfg2
import Public
/** Holds if `e` has an implicit field selection at `index` for `implicitField`. */
predicate implicitFieldSelection(Go::AstNode e, int index, Go::Field implicitField) {
Input::implicitFieldSelection(e, index, implicitField)
}
/** Provides an implementation of the AST signature for Go. */
private module Ast implements CfgLib::AstSig<Go::Location> {
class AstNode = Go::AstNode;
@@ -63,7 +68,11 @@ module GoCfg {
AstNode callableGetBody(Callable c) { result = c.(Go::FuncDef).getBody() }
Callable getEnclosingCallable(AstNode node) { result = node.getEnclosingFunction() }
Callable getEnclosingCallable(AstNode node) {
result = node and node instanceof Callable
or
not node instanceof Callable and result = node.getEnclosingFunction()
}
class Stmt = Go::Stmt;
@@ -515,7 +524,7 @@ module GoCfg {
private predicate notBlankIdent(Go::Expr e) { not e instanceof Go::BlankIdent }
/** Helper: implicit field selection for promoted selectors */
private predicate implicitFieldSelection(Ast::AstNode e, int index, Go::Field implicitField) {
predicate implicitFieldSelection(Ast::AstNode e, int index, Go::Field implicitField) {
exists(Go::StructType baseType, Go::PromotedField child, int implicitFieldDepth |
baseType = e.(Go::PromotedSelector).getSelectedStructType() and
(

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -89,7 +89,7 @@ private module Internal {
/** Holds if the `i`th node of `bb` in function `f` is an entry node. */
private predicate entryNode(FuncDef f, ReachableBasicBlock bb, int i) {
f = bb.getScope() and
bb.getNode(i).isEntryNode()
bb.getNode(i).(ControlFlow::Node).isEntryNode()
}
/**

View File

@@ -154,7 +154,7 @@ module Revel {
private IR::EvalInstruction skipImplicitFieldReads(IR::Instruction insn) {
result = insn or
result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBase())
result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBaseInstruction())
}
/** A call to `Controller.Render`. */