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:
Mathias Vorreiter Pedersen
2024-09-12 17:38:48 +01:00
parent 9107075f41
commit 8b4e065fa2
3 changed files with 143 additions and 15 deletions

View File

@@ -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) }
}
}

View File

@@ -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

View File

@@ -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()
}