Skip to content

Commit 89a2381

Browse files
committed
C#: Adopt shared SSA data-flow integration
1 parent fbcb449 commit 89a2381

File tree

11 files changed

+1718
-937
lines changed

11 files changed

+1718
-937
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module BaseSsa {
2929
) {
3030
exists(ControlFlow::ControlFlow::BasicBlocks::EntryBlock entry |
3131
c = entry.getCallable() and
32-
// In case `c` has multiple bodies, we want each body to gets its own implicit
32+
// In case `c` has multiple bodies, we want each body to get its own implicit
3333
// entry definition. In case `c` doesn't have multiple bodies, the line below
3434
// is simply the same as `bb = entry`, because `entry.getFirstNode().getASuccessor()`
3535
// will be in the entry block.

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

Lines changed: 93 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,9 @@ module VariableCapture {
267267
private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) {
268268
e1 = LocalFlow::getALastEvalNode(e2)
269269
or
270-
exists(Ssa::Definition def |
271-
LocalFlow::ssaDefAssigns(def.getAnUltimateDefinition(), e1) and
270+
exists(Ssa::Definition def, AssignableDefinition adef |
271+
LocalFlow::defAssigns(adef, _, e1) and
272+
def.getAnUltimateDefinition().(Ssa::ExplicitDefinition).getADefinition() = adef and
272273
exists(def.getAReadAtNode(e2))
273274
)
274275
}
@@ -492,6 +493,30 @@ module VariableCapture {
492493
}
493494
}
494495

496+
/** Provides logic related to SSA. */
497+
module SsaFlow {
498+
private module Impl = SsaImpl::DataFlowIntegration;
499+
500+
Impl::Node asNode(Node n) {
501+
n = TSsaNode(result)
502+
or
503+
result.(Impl::ExprNode).getExpr() = n.(ExprNode).getControlFlowNode()
504+
or
505+
result.(Impl::ExprPostUpdateNode).getExpr() =
506+
n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode()
507+
or
508+
result.(Impl::ParameterNode).getParameter() = n.(ExplicitParameterNode).getSsaDefinition()
509+
}
510+
511+
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
512+
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
513+
}
514+
515+
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
516+
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
517+
}
518+
}
519+
495520
/** Provides predicates related to local data flow. */
496521
module LocalFlow {
497522
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -617,105 +642,6 @@ module LocalFlow {
617642
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
618643
}
619644

620-
predicate ssaDefAssigns(Ssa::ExplicitDefinition ssaDef, ControlFlow::Nodes::ExprNode value) {
621-
exists(AssignableDefinition def, ControlFlow::Node cfnDef |
622-
any(LocalExprStepConfiguration conf).hasDefPath(_, value, def, cfnDef) and
623-
ssaDef.getADefinition() = def and
624-
ssaDef.getControlFlowNode() = cfnDef
625-
)
626-
}
627-
628-
/**
629-
* An uncertain SSA definition. Either an uncertain explicit definition or an
630-
* uncertain qualifier definition.
631-
*
632-
* Restricts `Ssa::UncertainDefinition` by excluding implicit call definitions,
633-
* as we---conservatively---consider such definitions to be certain.
634-
*/
635-
class UncertainExplicitSsaDefinition extends Ssa::UncertainDefinition {
636-
UncertainExplicitSsaDefinition() {
637-
this instanceof Ssa::ExplicitDefinition
638-
or
639-
this =
640-
any(Ssa::ImplicitQualifierDefinition qdef |
641-
qdef.getQualifierDefinition() instanceof UncertainExplicitSsaDefinition
642-
)
643-
}
644-
}
645-
646-
/** An SSA definition into which another SSA definition may flow. */
647-
private class SsaInputDefinitionExtNode extends SsaDefinitionExtNode {
648-
SsaInputDefinitionExtNode() {
649-
def instanceof Ssa::PhiNode
650-
or
651-
def instanceof SsaImpl::PhiReadNode
652-
or
653-
def instanceof LocalFlow::UncertainExplicitSsaDefinition
654-
}
655-
}
656-
657-
/**
658-
* Holds if `nodeFrom` is a last node referencing SSA definition `def`, which
659-
* can reach `next`.
660-
*/
661-
private predicate localFlowSsaInputFromDef(
662-
Node nodeFrom, SsaImpl::DefinitionExt def, SsaInputDefinitionExtNode next
663-
) {
664-
exists(ControlFlow::BasicBlock bb, int i |
665-
SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and
666-
def.definesAt(_, bb, i, _) and
667-
def = getSsaDefinitionExt(nodeFrom) and
668-
nodeFrom != next
669-
)
670-
}
671-
672-
/**
673-
* Holds if `read` is a last node reading SSA definition `def`, which
674-
* can reach `next`.
675-
*/
676-
predicate localFlowSsaInputFromRead(
677-
Node read, SsaImpl::DefinitionExt def, SsaInputDefinitionExtNode next
678-
) {
679-
exists(ControlFlow::BasicBlock bb, int i |
680-
SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and
681-
read.asExprAtNode(bb.getNode(i)) instanceof AssignableRead
682-
)
683-
}
684-
685-
private SsaImpl::DefinitionExt getSsaDefinitionExt(Node n) {
686-
result = n.(SsaDefinitionExtNode).getDefinitionExt()
687-
or
688-
result = n.(ExplicitParameterNode).getSsaDefinition()
689-
}
690-
691-
/**
692-
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
693-
* involving SSA definition `def`.
694-
*/
695-
predicate localSsaFlowStepUseUse(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
696-
exists(ControlFlow::Node cfnFrom, ControlFlow::Node cfnTo |
697-
SsaImpl::adjacentReadPairSameVarExt(def, cfnFrom, cfnTo) and
698-
nodeTo = TExprNode(cfnTo) and
699-
nodeFrom = TExprNode(cfnFrom)
700-
)
701-
}
702-
703-
/**
704-
* Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving
705-
* SSA definition `def`.
706-
*/
707-
predicate localSsaFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
708-
// Flow from SSA definition/parameter to first read
709-
def = getSsaDefinitionExt(nodeFrom) and
710-
SsaImpl::firstReadSameVarExt(def, nodeTo.(ExprNode).getControlFlowNode())
711-
or
712-
// Flow from read to next read
713-
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
714-
or
715-
// Flow into phi (read)/uncertain SSA definition node from def
716-
localFlowSsaInputFromDef(nodeFrom, def, nodeTo)
717-
}
718-
719645
/**
720646
* Holds if the source variable of SSA definition `def` is an instance field.
721647
*/
@@ -800,10 +726,7 @@ module LocalFlow {
800726
node2.asExpr() instanceof AssignExpr
801727
)
802728
or
803-
exists(SsaImpl::Definition def |
804-
def = getSsaDefinitionExt(node1) and
805-
exists(SsaImpl::getAReadAtNode(def, node2.(ExprNode).getControlFlowNode()))
806-
)
729+
SsaFlow::localMustFlowStep(_, node1, node2)
807730
or
808731
node2 = node1.(LocalFunctionCreationNode).getAnAccess(true)
809732
or
@@ -827,23 +750,14 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
827750
(
828751
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
829752
or
830-
exists(SsaImpl::DefinitionExt def |
753+
exists(SsaImpl::DefinitionExt def, boolean isUseStep |
754+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and
831755
not LocalFlow::usesInstanceField(def) and
832756
not def instanceof VariableCapture::CapturedSsaDefinitionExt
833757
|
834-
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo)
835-
or
836-
LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and
837-
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) and
838-
nodeFrom != nodeTo
758+
isUseStep = false
839759
or
840-
// Flow into phi (read)/uncertain SSA definition node from read
841-
exists(Node read | LocalFlow::localFlowSsaInputFromRead(read, def, nodeTo) |
842-
nodeFrom = read and
843-
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
844-
or
845-
nodeFrom.(PostUpdateNode).getPreUpdateNode() = read
846-
)
760+
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
847761
)
848762
or
849763
nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode)
@@ -1113,11 +1027,7 @@ private module Cached {
11131027
cached
11141028
newtype TNode =
11151029
TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getAstNode() instanceof Expr } or
1116-
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) {
1117-
// Handled by `TExplicitParameterNode` below
1118-
not def instanceof Ssa::ImplicitParameterDefinition and
1119-
def.getBasicBlock() = any(DataFlowCallable c).getAControlFlowNode().getBasicBlock()
1120-
} or
1030+
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
11211031
TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
11221032
cfn = def.getExpr().getAControlFlowNode()
11231033
} or
@@ -1180,17 +1090,7 @@ private module Cached {
11801090
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
11811091
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
11821092
or
1183-
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) and
1184-
nodeFrom != nodeTo
1185-
or
1186-
LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo)
1187-
or
1188-
// Flow into phi (read)/uncertain SSA definition node from read
1189-
exists(Node read | LocalFlow::localFlowSsaInputFromRead(read, _, nodeTo) |
1190-
nodeFrom = read
1191-
or
1192-
nodeFrom.(PostUpdateNode).getPreUpdateNode() = read
1193-
)
1093+
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
11941094
or
11951095
// Simple flow through library code is included in the exposed local
11961096
// step relation, even though flow is technically inter-procedural
@@ -1254,7 +1154,7 @@ import Cached
12541154

12551155
/** Holds if `n` should be hidden from path explanations. */
12561156
predicate nodeIsHidden(Node n) {
1257-
n instanceof SsaDefinitionExtNode
1157+
n instanceof SsaNode
12581158
or
12591159
exists(Parameter p | p = n.(ParameterNode).getParameter() | not p.fromSource())
12601160
or
@@ -1288,13 +1188,16 @@ predicate nodeIsHidden(Node n) {
12881188
n instanceof CaptureNode
12891189
}
12901190

1291-
/** An SSA definition, viewed as a node in a data flow graph. */
1292-
class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
1191+
/** An SSA node. */
1192+
abstract class SsaNode extends NodeImpl, TSsaNode {
1193+
SsaImpl::DataFlowIntegration::SsaNode node;
12931194
SsaImpl::DefinitionExt def;
12941195

1295-
SsaDefinitionExtNode() { this = TSsaDefinitionExtNode(def) }
1196+
SsaNode() {
1197+
this = TSsaNode(node) and
1198+
def = node.getDefinitionExt()
1199+
}
12961200

1297-
/** Gets the underlying SSA definition. */
12981201
SsaImpl::DefinitionExt getDefinitionExt() { result = def }
12991202

13001203
override DataFlowCallable getEnclosingCallableImpl() {
@@ -1307,9 +1210,57 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
13071210
result = def.(Ssa::Definition).getControlFlowNode()
13081211
}
13091212

1310-
override Location getLocationImpl() { result = def.getLocation() }
1213+
override Location getLocationImpl() { result = node.getLocation() }
13111214

1312-
override string toStringImpl() { result = def.toString() }
1215+
override string toStringImpl() { result = node.toString() }
1216+
}
1217+
1218+
/** An (extended) SSA definition, viewed as a node in a data flow graph. */
1219+
class SsaDefinitionExtNode extends SsaNode {
1220+
override SsaImpl::DataFlowIntegration::SsaDefinitionExtNode node;
1221+
}
1222+
1223+
/**
1224+
* A node that represents an input to an SSA phi (read) definition.
1225+
*
1226+
* This allows for barrier guards to filter input to phi nodes. For example, in
1227+
*
1228+
* ```csharp
1229+
* var x = taint;
1230+
* if (x != "safe")
1231+
* {
1232+
* x = "safe";
1233+
* }
1234+
* sink(x);
1235+
* ```
1236+
*
1237+
* the `false` edge out of `x != "safe"` guards the input from `x = taint` into the
1238+
* `phi` node after the condition.
1239+
*
1240+
* It is also relevant to filter input into phi read nodes:
1241+
*
1242+
* ```csharp
1243+
* var x = taint;
1244+
* if (b)
1245+
* {
1246+
* if (x != "safe1")
1247+
* {
1248+
* return;
1249+
* }
1250+
* } else {
1251+
* if (x != "safe2")
1252+
* {
1253+
* return;
1254+
* }
1255+
* }
1256+
*
1257+
* sink(x);
1258+
* ```
1259+
*
1260+
* both inputs into the phi read node after the outer condition are guarded.
1261+
*/
1262+
class SsaInputNode extends SsaNode {
1263+
override SsaImpl::DataFlowIntegration::SsaInputNode node;
13131264
}
13141265

13151266
/** A definition, viewed as a node in a data flow graph. */
@@ -2907,7 +2858,7 @@ private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
29072858
/** Extra data-flow steps needed for lambda flow analysis. */
29082859
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
29092860
exists(SsaImpl::DefinitionExt def |
2910-
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and
2861+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, _) and
29112862
preservesValue = true
29122863
|
29132864
LocalFlow::usesInstanceField(def)

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,14 @@ signature predicate guardChecksSig(Guard g, Expr e, AbstractValue v);
171171
* in data flow and taint tracking.
172172
*/
173173
module BarrierGuard<guardChecksSig/3 guardChecks> {
174+
private import SsaImpl as SsaImpl
175+
174176
/** Gets a node that is safely guarded by the given guard check. */
175-
ExprNode getABarrierNode() {
177+
pragma[nomagic]
178+
Node getABarrierNode() {
179+
SsaFlow::asNode(result) =
180+
SsaImpl::DataFlowIntegration::BarrierGuard<guardChecks/3>::getABarrierNode()
181+
or
176182
exists(Guard g, Expr e, AbstractValue v |
177183
guardChecks(g, e, v) and
178184
g.controlsNode(result.getControlFlowNode(), e, v)

0 commit comments

Comments
 (0)