diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll index 8bc52342c33..c6fc2d55dd2 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll @@ -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) } + } } diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowDispatch.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowDispatch.qll index 1f6084ef4d5..8ec7d8f1911 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowDispatch.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowDispatch.qll @@ -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 diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll index 95cb4caa629..77a0eb804ca 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll @@ -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() }