Skip to content

Commit 8b4e065

Browse files
committed
PS: Port the Ruby framework for lifting parent/child relations at the AST level to parent/child relations at the CFG level.
1 parent 9107075 commit 8b4e065

File tree

3 files changed

+142
-14
lines changed

3 files changed

+142
-14
lines changed

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,144 @@ class StmtCfgNode extends AstCfgNode {
6060
Stmt getStmt() { result = s }
6161
}
6262

63+
/**
64+
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
65+
*/
66+
abstract private class ChildMapping extends Ast {
67+
/**
68+
* Holds if `child` is a (possibly nested) child of this expression
69+
* for which we would like to find a matching CFG child.
70+
*/
71+
abstract predicate relevantChild(Ast child);
72+
73+
pragma[nomagic]
74+
abstract predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb);
75+
76+
/**
77+
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
78+
* is a control-flow node for this expression, and `cfnChild` is a control-flow
79+
* node for `child`.
80+
*
81+
* The path never escapes the syntactic scope of this expression.
82+
*/
83+
cached
84+
predicate hasCfgChild(Ast child, CfgNode cfn, CfgNode cfnChild) {
85+
this.reachesBasicBlock(child, cfn, cfnChild.getBasicBlock()) and
86+
cfnChild.getAstNode() = child
87+
}
88+
}
89+
90+
/**
91+
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
92+
*/
93+
abstract private class ExprChildMapping extends Expr, ChildMapping {
94+
pragma[nomagic]
95+
override predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb) {
96+
this.relevantChild(child) and
97+
cfn.getAstNode() = this and
98+
bb.getANode() = cfn
99+
or
100+
exists(BasicBlock mid |
101+
this.reachesBasicBlock(child, cfn, mid) and
102+
bb = mid.getAPredecessor() and
103+
not mid.getANode().getAstNode() = child
104+
)
105+
}
106+
}
107+
108+
/**
109+
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
110+
*/
111+
abstract private class NonExprChildMapping extends ChildMapping {
112+
NonExprChildMapping() { not this instanceof Expr }
113+
114+
pragma[nomagic]
115+
override predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb) {
116+
this.relevantChild(child) and
117+
cfn.getAstNode() = this and
118+
bb.getANode() = cfn
119+
or
120+
exists(BasicBlock mid |
121+
this.reachesBasicBlock(child, cfn, mid) and
122+
bb = mid.getASuccessor() and
123+
not mid.getANode().getAstNode() = child
124+
)
125+
}
126+
}
127+
63128
/** Provides classes for control-flow nodes that wrap AST expressions. */
64-
module ExprNodes { }
129+
module ExprNodes {
130+
private class VarAccessChildMapping extends ExprChildMapping, VarAccess {
131+
override predicate relevantChild(Ast n) { none() }
132+
}
133+
134+
class VarAccessCfgNode extends ExprCfgNode {
135+
override string getAPrimaryQlClass() { result = "VarAccessCfgNode" }
136+
137+
override VarAccessChildMapping e;
138+
139+
override VarAccess getExpr() { result = super.getExpr() }
140+
}
141+
142+
private class VarReadAccessChildMapping extends VarAccessChildMapping, VarReadAccess { }
143+
144+
class VarReadAccessCfgNode extends VarAccessCfgNode {
145+
override string getAPrimaryQlClass() { result = "VarReadAccessCfgNode" }
146+
147+
override VarReadAccessChildMapping e;
148+
149+
override VarReadAccess getExpr() { result = super.getExpr() }
150+
}
151+
152+
private class VarWriteAccessChildMapping extends VarAccessChildMapping, VarWriteAccess { }
153+
154+
class VariableWriteAccessCfgNode extends VarAccessCfgNode {
155+
override string getAPrimaryQlClass() { result = "VarWriteAccessCfgNode" }
156+
157+
override VarWriteAccessChildMapping e;
158+
159+
override VarWriteAccess getExpr() { result = super.getExpr() }
160+
161+
Variable getVariable() { result = e.getVariable() }
162+
163+
predicate isExplicitWrite(StmtNodes::AssignStmtCfgNode assignment) {
164+
this = assignment.getLeftHandSide()
165+
}
166+
}
167+
}
65168

66169
module StmtNodes {
170+
private class CmdChildMapping extends NonExprChildMapping, Cmd {
171+
override predicate relevantChild(Ast n) { n = this.getElement(_) }
172+
}
173+
67174
/** A control-flow node that wraps a `Cmd` AST expression. */
68-
class CallCfgNode extends StmtCfgNode {
69-
override string getAPrimaryQlClass() { result = "CallCfgNode" }
175+
class CmdCfgNode extends StmtCfgNode {
176+
override string getAPrimaryQlClass() { result = "CmdCfgNode" }
70177

71-
override Cmd s;
178+
override CmdChildMapping s;
72179

73180
override Cmd getStmt() { result = super.getStmt() }
74181
}
182+
183+
private class AssignStmtChildMapping extends NonExprChildMapping, AssignStmt {
184+
override predicate relevantChild(Ast n) {
185+
n = this.getLeftHandSide() or n = this.getRightHandSide()
186+
}
187+
}
188+
189+
/** A control-flow node that wraps an `AssignStmt` AST expression. */
190+
class AssignStmtCfgNode extends StmtCfgNode {
191+
override string getAPrimaryQlClass() { result = "AssignCfgNode" }
192+
193+
override AssignStmtChildMapping s;
194+
195+
override AssignStmt getStmt() { result = super.getStmt() }
196+
197+
/** Gets the LHS of this assignment. */
198+
final ExprCfgNode getLeftHandSide() { s.hasCfgChild(s.getLeftHandSide(), this, result) }
199+
200+
/** Gets the RHS of this assignment. */
201+
final StmtCfgNode getRightHandSide() { s.hasCfgChild(s.getRightHandSide(), this, result) }
202+
}
75203
}

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowDispatch.qll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ abstract class DataFlowCall extends TDataFlowCall {
7777
abstract DataFlowCallable getEnclosingCallable();
7878

7979
/** Gets the underlying source code call, if any. */
80-
abstract CfgNodes::StmtNodes::CallCfgNode asCall();
80+
abstract CfgNodes::StmtNodes::CmdCfgNode asCall();
8181

8282
/** Gets a textual representation of this call. */
8383
abstract string toString();
@@ -107,11 +107,11 @@ abstract class DataFlowCall extends TDataFlowCall {
107107
}
108108

109109
class NormalCall extends DataFlowCall, TNormalCall {
110-
private CfgNodes::StmtNodes::CallCfgNode c;
110+
private CfgNodes::StmtNodes::CmdCfgNode c;
111111

112112
NormalCall() { this = TNormalCall(c) }
113113

114-
override CfgNodes::StmtNodes::CallCfgNode asCall() { result = c }
114+
override CfgNodes::StmtNodes::CmdCfgNode asCall() { result = c }
115115

116116
override DataFlowCallable getEnclosingCallable() { result = TCfgScope(c.getScope()) }
117117

@@ -121,7 +121,7 @@ class NormalCall extends DataFlowCall, TNormalCall {
121121
}
122122

123123
/** A call for which we want to compute call targets. */
124-
private class RelevantCall extends CfgNodes::StmtNodes::CallCfgNode { }
124+
private class RelevantCall extends CfgNodes::StmtNodes::CmdCfgNode { }
125125

126126
/** Holds if `call` may resolve to the returned source-code method. */
127127
private DataFlowCallable viableSourceCallable(DataFlowCall call) {
@@ -139,7 +139,7 @@ class AdditionalCallTarget extends Unit {
139139
/**
140140
* Gets a viable target for `call`.
141141
*/
142-
abstract DataFlowCallable viableTarget(CfgNodes::StmtNodes::CallCfgNode call);
142+
abstract DataFlowCallable viableTarget(CfgNodes::StmtNodes::CmdCfgNode call);
143143
}
144144

145145
/** Holds if `call` may resolve to the returned summarized library method. */
@@ -158,7 +158,7 @@ private module Cached {
158158
TLibraryCallable(LibraryCallable callable)
159159

160160
cached
161-
newtype TDataFlowCall = TNormalCall(CfgNodes::StmtNodes::CallCfgNode c)
161+
newtype TDataFlowCall = TNormalCall(CfgNodes::StmtNodes::CmdCfgNode c)
162162

163163
/** Gets a viable run-time target for the call `call`. */
164164
cached

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ module LocalFlow {
5656

5757
/** An argument of a call (including qualifier arguments and block arguments). */
5858
private class Argument extends CfgNodes::ExprCfgNode {
59-
private CfgNodes::StmtNodes::CallCfgNode call;
59+
private CfgNodes::StmtNodes::CmdCfgNode call;
6060
private ArgumentPosition arg;
6161

6262
Argument() { none() }
6363

6464
/** Holds if this expression is the `i`th argument of `c`. */
65-
predicate isArgumentOf(CfgNodes::StmtNodes::CallCfgNode c, ArgumentPosition pos) {
65+
predicate isArgumentOf(CfgNodes::StmtNodes::CmdCfgNode c, ArgumentPosition pos) {
6666
c = call and pos = arg
6767
}
6868
}
@@ -140,7 +140,7 @@ abstract class ArgumentNode extends Node {
140140
/** Holds if this argument occurs at the given position in the given call. */
141141
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
142142

143-
abstract predicate sourceArgumentOf(CfgNodes::StmtNodes::CallCfgNode call, ArgumentPosition pos);
143+
abstract predicate sourceArgumentOf(CfgNodes::StmtNodes::CmdCfgNode call, ArgumentPosition pos);
144144

145145
/** Gets the call in which this node is an argument. */
146146
final DataFlowCall getCall() { this.argumentOf(result, _) }
@@ -293,7 +293,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
293293
* Holds if `call` is a from-source lambda call of kind `kind` where `receiver`
294294
* is the lambda expression.
295295
*/
296-
predicate lambdaSourceCall(CfgNodes::StmtNodes::CallCfgNode call, LambdaCallKind kind, Node receiver) {
296+
predicate lambdaSourceCall(CfgNodes::StmtNodes::CmdCfgNode call, LambdaCallKind kind, Node receiver) {
297297
none()
298298
}
299299

0 commit comments

Comments
 (0)