mirror of
https://github.com/github/codeql.git
synced 2026-05-25 00:27:09 +02:00
PS: Port the Ruby framework for lifting parent/child relations at the AST level to parent/child relations at the CFG level.
This commit is contained in:
@@ -60,16 +60,144 @@ class StmtCfgNode extends AstCfgNode {
|
||||
Stmt getStmt() { result = s }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
|
||||
*/
|
||||
abstract private class ChildMapping extends Ast {
|
||||
/**
|
||||
* Holds if `child` is a (possibly nested) child of this expression
|
||||
* for which we would like to find a matching CFG child.
|
||||
*/
|
||||
abstract predicate relevantChild(Ast child);
|
||||
|
||||
pragma[nomagic]
|
||||
abstract predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb);
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
|
||||
* is a control-flow node for this expression, and `cfnChild` is a control-flow
|
||||
* node for `child`.
|
||||
*
|
||||
* The path never escapes the syntactic scope of this expression.
|
||||
*/
|
||||
cached
|
||||
predicate hasCfgChild(Ast child, CfgNode cfn, CfgNode cfnChild) {
|
||||
this.reachesBasicBlock(child, cfn, cfnChild.getBasicBlock()) and
|
||||
cfnChild.getAstNode() = child
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
|
||||
*/
|
||||
abstract private class ExprChildMapping extends Expr, ChildMapping {
|
||||
pragma[nomagic]
|
||||
override predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb) {
|
||||
this.relevantChild(child) and
|
||||
cfn.getAstNode() = this and
|
||||
bb.getANode() = cfn
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
this.reachesBasicBlock(child, cfn, mid) and
|
||||
bb = mid.getAPredecessor() and
|
||||
not mid.getANode().getAstNode() = child
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
|
||||
*/
|
||||
abstract private class NonExprChildMapping extends ChildMapping {
|
||||
NonExprChildMapping() { not this instanceof Expr }
|
||||
|
||||
pragma[nomagic]
|
||||
override predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb) {
|
||||
this.relevantChild(child) and
|
||||
cfn.getAstNode() = this and
|
||||
bb.getANode() = cfn
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
this.reachesBasicBlock(child, cfn, mid) and
|
||||
bb = mid.getASuccessor() and
|
||||
not mid.getANode().getAstNode() = child
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides classes for control-flow nodes that wrap AST expressions. */
|
||||
module ExprNodes { }
|
||||
module ExprNodes {
|
||||
private class VarAccessChildMapping extends ExprChildMapping, VarAccess {
|
||||
override predicate relevantChild(Ast n) { none() }
|
||||
}
|
||||
|
||||
class VarAccessCfgNode extends ExprCfgNode {
|
||||
override string getAPrimaryQlClass() { result = "VarAccessCfgNode" }
|
||||
|
||||
override VarAccessChildMapping e;
|
||||
|
||||
override VarAccess getExpr() { result = super.getExpr() }
|
||||
}
|
||||
|
||||
private class VarReadAccessChildMapping extends VarAccessChildMapping, VarReadAccess { }
|
||||
|
||||
class VarReadAccessCfgNode extends VarAccessCfgNode {
|
||||
override string getAPrimaryQlClass() { result = "VarReadAccessCfgNode" }
|
||||
|
||||
override VarReadAccessChildMapping e;
|
||||
|
||||
override VarReadAccess getExpr() { result = super.getExpr() }
|
||||
}
|
||||
|
||||
private class VarWriteAccessChildMapping extends VarAccessChildMapping, VarWriteAccess { }
|
||||
|
||||
class VariableWriteAccessCfgNode extends VarAccessCfgNode {
|
||||
override string getAPrimaryQlClass() { result = "VarWriteAccessCfgNode" }
|
||||
|
||||
override VarWriteAccessChildMapping e;
|
||||
|
||||
override VarWriteAccess getExpr() { result = super.getExpr() }
|
||||
|
||||
Variable getVariable() { result = e.getVariable() }
|
||||
|
||||
predicate isExplicitWrite(StmtNodes::AssignStmtCfgNode assignment) {
|
||||
this = assignment.getLeftHandSide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module StmtNodes {
|
||||
/** A control-flow node that wraps a `Cmd` AST expression. */
|
||||
class CallCfgNode extends StmtCfgNode {
|
||||
override string getAPrimaryQlClass() { result = "CallCfgNode" }
|
||||
private class CmdChildMapping extends NonExprChildMapping, Cmd {
|
||||
override predicate relevantChild(Ast n) { n = this.getElement(_) }
|
||||
}
|
||||
|
||||
override Cmd s;
|
||||
/** A control-flow node that wraps a `Cmd` AST expression. */
|
||||
class CmdCfgNode extends StmtCfgNode {
|
||||
override string getAPrimaryQlClass() { result = "CmdCfgNode" }
|
||||
|
||||
override CmdChildMapping s;
|
||||
|
||||
override Cmd getStmt() { result = super.getStmt() }
|
||||
}
|
||||
|
||||
private class AssignStmtChildMapping extends NonExprChildMapping, AssignStmt {
|
||||
override predicate relevantChild(Ast n) {
|
||||
n = this.getLeftHandSide() or n = this.getRightHandSide()
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps an `AssignStmt` AST expression. */
|
||||
class AssignStmtCfgNode extends StmtCfgNode {
|
||||
override string getAPrimaryQlClass() { result = "AssignCfgNode" }
|
||||
|
||||
override AssignStmtChildMapping s;
|
||||
|
||||
override AssignStmt getStmt() { result = super.getStmt() }
|
||||
|
||||
/** Gets the LHS of this assignment. */
|
||||
final ExprCfgNode getLeftHandSide() { s.hasCfgChild(s.getLeftHandSide(), this, result) }
|
||||
|
||||
/** Gets the RHS of this assignment. */
|
||||
final StmtCfgNode getRightHandSide() { s.hasCfgChild(s.getRightHandSide(), this, result) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
|
||||
/** Gets the underlying source code call, if any. */
|
||||
abstract CfgNodes::StmtNodes::CallCfgNode asCall();
|
||||
abstract CfgNodes::StmtNodes::CmdCfgNode asCall();
|
||||
|
||||
/** Gets a textual representation of this call. */
|
||||
abstract string toString();
|
||||
@@ -107,11 +107,11 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
}
|
||||
|
||||
class NormalCall extends DataFlowCall, TNormalCall {
|
||||
private CfgNodes::StmtNodes::CallCfgNode c;
|
||||
private CfgNodes::StmtNodes::CmdCfgNode c;
|
||||
|
||||
NormalCall() { this = TNormalCall(c) }
|
||||
|
||||
override CfgNodes::StmtNodes::CallCfgNode asCall() { result = c }
|
||||
override CfgNodes::StmtNodes::CmdCfgNode asCall() { result = c }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result = TCfgScope(c.getScope()) }
|
||||
|
||||
@@ -121,7 +121,7 @@ class NormalCall extends DataFlowCall, TNormalCall {
|
||||
}
|
||||
|
||||
/** A call for which we want to compute call targets. */
|
||||
private class RelevantCall extends CfgNodes::StmtNodes::CallCfgNode { }
|
||||
private class RelevantCall extends CfgNodes::StmtNodes::CmdCfgNode { }
|
||||
|
||||
/** Holds if `call` may resolve to the returned source-code method. */
|
||||
private DataFlowCallable viableSourceCallable(DataFlowCall call) {
|
||||
@@ -139,7 +139,7 @@ class AdditionalCallTarget extends Unit {
|
||||
/**
|
||||
* Gets a viable target for `call`.
|
||||
*/
|
||||
abstract DataFlowCallable viableTarget(CfgNodes::StmtNodes::CallCfgNode call);
|
||||
abstract DataFlowCallable viableTarget(CfgNodes::StmtNodes::CmdCfgNode call);
|
||||
}
|
||||
|
||||
/** Holds if `call` may resolve to the returned summarized library method. */
|
||||
@@ -158,7 +158,7 @@ private module Cached {
|
||||
TLibraryCallable(LibraryCallable callable)
|
||||
|
||||
cached
|
||||
newtype TDataFlowCall = TNormalCall(CfgNodes::StmtNodes::CallCfgNode c)
|
||||
newtype TDataFlowCall = TNormalCall(CfgNodes::StmtNodes::CmdCfgNode c)
|
||||
|
||||
/** Gets a viable run-time target for the call `call`. */
|
||||
cached
|
||||
|
||||
@@ -56,13 +56,13 @@ module LocalFlow {
|
||||
|
||||
/** An argument of a call (including qualifier arguments and block arguments). */
|
||||
private class Argument extends CfgNodes::ExprCfgNode {
|
||||
private CfgNodes::StmtNodes::CallCfgNode call;
|
||||
private CfgNodes::StmtNodes::CmdCfgNode call;
|
||||
private ArgumentPosition arg;
|
||||
|
||||
Argument() { none() }
|
||||
|
||||
/** Holds if this expression is the `i`th argument of `c`. */
|
||||
predicate isArgumentOf(CfgNodes::StmtNodes::CallCfgNode c, ArgumentPosition pos) {
|
||||
predicate isArgumentOf(CfgNodes::StmtNodes::CmdCfgNode c, ArgumentPosition pos) {
|
||||
c = call and pos = arg
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ abstract class ArgumentNode extends Node {
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
|
||||
|
||||
abstract predicate sourceArgumentOf(CfgNodes::StmtNodes::CallCfgNode call, ArgumentPosition pos);
|
||||
abstract predicate sourceArgumentOf(CfgNodes::StmtNodes::CmdCfgNode call, ArgumentPosition pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
final DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
@@ -293,7 +293,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
|
||||
* Holds if `call` is a from-source lambda call of kind `kind` where `receiver`
|
||||
* is the lambda expression.
|
||||
*/
|
||||
predicate lambdaSourceCall(CfgNodes::StmtNodes::CallCfgNode call, LambdaCallKind kind, Node receiver) {
|
||||
predicate lambdaSourceCall(CfgNodes::StmtNodes::CmdCfgNode call, LambdaCallKind kind, Node receiver) {
|
||||
none()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user