diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll new file mode 100644 index 00000000000..e942fcb68d3 --- /dev/null +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -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() + ) + } + } +} diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index a7fa6c7d31d..280dcaff5eb 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -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() diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll index 3494cc87569..c46f4a40422 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll @@ -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 diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll index 0eed5427740..95b168ca30a 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll @@ -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 } diff --git a/swift/ql/lib/codeql/swift/elements/decl/VarDecl.qll b/swift/ql/lib/codeql/swift/elements/decl/VarDecl.qll index f6940944c34..7b955cc8a84 100644 --- a/swift/ql/lib/codeql/swift/elements/decl/VarDecl.qll +++ b/swift/ql/lib/codeql/swift/elements/decl/VarDecl.qll @@ -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 } +} diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected new file mode 100644 index 00000000000..52bdacc7477 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -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 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.ql b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.ql new file mode 100644 index 00000000000..cbad8dff2f2 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.ql @@ -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 \ No newline at end of file diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift new file mode 100644 index 00000000000..d20fecb54d7 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -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) +}