Swift: initial local data flow implementation

This commit is contained in:
Robert Marsh
2022-05-25 14:12:35 +00:00
committed by GitHub
parent 117a1ad2f4
commit cf22ade9f3
8 changed files with 179 additions and 7 deletions

View File

@@ -0,0 +1,69 @@
cached
module Ssa {
private import swift
private import internal.SsaImplCommon as SsaImplCommon
private import internal.SsaImplSpecific as SsaImplSpecific
private import codeql.swift.controlflow.CfgNodes
private import codeql.swift.controlflow.ControlFlowGraph
private import codeql.swift.controlflow.BasicBlocks
cached
class Definition extends SsaImplCommon::Definition {
cached
Location getLocation() { none() }
cached
ControlFlowNode getARead() {
exists(VarDecl v, BasicBlock bb, int i |
SsaImplCommon::ssaDefReachesRead(v, this, bb, i) and
SsaImplSpecific::variableRead(bb, i, v, true) and
result = bb.getNode(i)
)
}
cached
ControlFlowNode getAFirstRead() {
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
this.definesAt(_, bb1, i1) and
SsaImplCommon::adjacentDefNoUncertainReads(this, bb1, i1, bb2, i2) and
result = bb2.getNode(i2)
)
}
cached
predicate adjacentReadPair(ControlFlowNode read1, ControlFlowNode read2) {
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
read1 = bb1.getNode(i1) and
SsaImplSpecific::variableRead(bb1, i1, _, true) and
SsaImplCommon::adjacentDefNoUncertainReads(this, bb1, i1, bb2, i2) and
read2 = bb2.getNode(i2)
)
}
}
cached
class WriteDefinition extends Definition, SsaImplCommon::WriteDefinition {
cached
override Location getLocation() {
exists(BasicBlock bb, int i |
this.definesAt(_, bb, i) and
result = bb.getNode(i).getLocation()
)
}
/**
* Holds if this SSA definition represents a direct assignment of `value`
* to the underlying variable.
*/
cached
predicate assigns(ExprCfgNode value) {
exists(
AssignExpr a, BasicBlock bb, int i // TODO: use CFG node for assignment expr
|
this.definesAt(_, bb, i) and
a = bb.getNode(i).getNode() and
value.getNode() = a.getSource()
)
}
}
}

View File

@@ -2,6 +2,7 @@ private import swift
private import DataFlowPublic
private import DataFlowDispatch
private import codeql.swift.controlflow.CfgNodes
private import codeql.swift.dataflow.Ssa
/** Gets the callable in which this node occurs. */
DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() }
@@ -32,24 +33,48 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
override string toStringImpl() { result = expr.toString() }
}
private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl {
override Location getLocationImpl() { result = def.getLocation() }
override string toStringImpl() { result = def.toString() }
}
/** A collection of cached types and predicates to be evaluated in the same stage. */
cached
private module Cached {
cached
newtype TNode =
TExprNode(ExprCfgNode e) or
TNormalParameterNode(ParamDecl p)
TNormalParameterNode(ParamDecl p) or
TSsaDefinitionNode(Ssa::Definition def)
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
exists(Ssa::Definition def |
// Step from assignment RHS to def
def.(Ssa::WriteDefinition).assigns(nodeFrom.getCfgNode()) and
nodeTo.asDefinition() = def
or
// step from def to first read
nodeFrom.asDefinition() = def and
nodeTo.getCfgNode() = def.getAFirstRead()
or
// use-use flow
def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode())
)
}
/**
* This is the local flow predicate that is used as a building block in global
* data flow.
*/
cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { none() }
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
localFlowStepCommon(nodeFrom, nodeTo)
}
/** This is the local flow predicate that is exposed. */
cached
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { none() }
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { localFlowStepCommon(nodeFrom, nodeTo) }
cached
newtype TContentSet = TODO_TContentSet()

View File

@@ -4,6 +4,7 @@ private import DataFlowPrivate
private import codeql.swift.controlflow.ControlFlowGraph
private import codeql.swift.controlflow.BasicBlocks
private import codeql.swift.controlflow.CfgNodes
private import codeql.swift.dataflow.Ssa
/**
* An element, viewed as a node in a data flow graph. Either an expression
@@ -30,6 +31,21 @@ class Node extends TNode {
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Gets this node's underlying expression, if any.
*/
Expr asExpr() { none() }
/**
* Gets this data flow node's corresponding control flow node.
*/
ControlFlowNode getCfgNode() { none() }
/**
* Gets this node's underlying SSA definition, if any.
*/
Ssa::Definition asDefinition() { none() }
}
/**
@@ -39,17 +55,31 @@ class Node extends TNode {
* to multiple `ExprNode`s, just like it may correspond to multiple
* `ControlFlow::Node`s.
*/
class ExprNode extends Node {
class ExprNode extends Node, TExprNode {
ExprCfgNode expr;
ExprNode() { this = TExprNode(expr) }
override Expr asExpr() { result = expr.getNode() }
override ControlFlowNode getCfgNode() { result = expr }
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends Node { }
class ParameterNode extends Node, TNormalParameterNode instanceof ParameterNodeImpl { }
/**
*/
class SsaDefinitionNode extends Node, TSsaDefinitionNode {
Ssa::Definition def;
SsaDefinitionNode() { this = TSsaDefinitionNode(def) }
override Ssa::Definition asDefinition() { result = def }
}
/**
* A node associated with an object after an operation that might have

View File

@@ -20,6 +20,13 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
assign.getDest() = v.getAnAccess() and
certain = true
)
or
exists(PatternBindingDecl decl, Pattern pattern |
bb.getNode(i).getNode() = pattern and
decl.getAPattern() = pattern and
v.getParentPattern() = pattern and
certain = true
)
}
private predicate isLValue(DeclRefExpr ref) { any(AssignExpr assign).getDest() = ref }

View File

@@ -1,4 +1,6 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.decl.VarDecl
private import codeql.swift.elements.expr.DeclRefExpr
class VarDecl extends VarDeclBase { }
class VarDecl extends VarDeclBase {
DeclRefExpr getAnAccess() { result.getDecl() = this }
}

View File

@@ -0,0 +1,11 @@
| file://:0:0:0:0 | Phi | test.swift:15:15:15:15 | DeclRefExpr[VarDecl[var t2]] |
| test.swift:6:9:6:13 | WriteDef | test.swift:7:15:7:15 | DeclRefExpr[VarDecl[var t1]] |
| test.swift:7:15:7:15 | DeclRefExpr[VarDecl[var t1]] | test.swift:8:10:8:10 | DeclRefExpr[VarDecl[var t1]] |
| test.swift:8:5:8:10 | WriteDef | test.swift:10:15:10:15 | DeclRefExpr[VarDecl[var t2]] |
| test.swift:8:10:8:10 | DeclRefExpr[VarDecl[var t1]] | test.swift:8:5:8:10 | WriteDef |
| test.swift:8:10:8:10 | DeclRefExpr[VarDecl[var t1]] | test.swift:9:15:9:15 | DeclRefExpr[VarDecl[var t1]] |
| test.swift:9:15:9:15 | DeclRefExpr[VarDecl[var t1]] | test.swift:11:8:11:8 | DeclRefExpr[VarDecl[var t1]] |
| test.swift:12:9:12:14 | WriteDef | test.swift:13:19:13:19 | DeclRefExpr[VarDecl[var t2]] |
| test.swift:12:14:12:14 | IntegerLiteralExpr(0) | test.swift:12:9:12:14 | WriteDef |
| test.swift:17:5:17:10 | WriteDef | test.swift:21:15:21:15 | DeclRefExpr[VarDecl[var t1]] |
| test.swift:17:10:17:10 | IntegerLiteralExpr(0) | test.swift:17:5:17:10 | WriteDef |

View File

@@ -0,0 +1,6 @@
import swift
import codeql.swift.dataflow.DataFlow
from DataFlow::Node pred, DataFlow::Node succ
where DataFlow::localFlowStep(pred, succ)
select pred, succ

View File

@@ -0,0 +1,22 @@
func source() -> Int { return 0; }
func sink(arg: Int) {}
func intraprocedural_with_local_flow() -> Void {
var t2: Int
var t1: Int = source()
sink(arg: t1)
t2 = t1
sink(arg: t1)
sink(arg: t2)
if(t1 != 0) {
t2 = 0
sink(arg: t2)
}
sink(arg: t2)
t1 = 0;
while(false) {
t1 = t2
}
sink(arg: t1)
}