mirror of
https://github.com/github/codeql.git
synced 2025-12-23 20:26:32 +01:00
Swift: initial local data flow implementation
This commit is contained in:
69
swift/ql/lib/codeql/swift/dataflow/Ssa.qll
Normal file
69
swift/ql/lib/codeql/swift/dataflow/Ssa.qll
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
22
swift/ql/test/library-tests/dataflow/dataflow/test.swift
Normal file
22
swift/ql/test/library-tests/dataflow/dataflow/test.swift
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user