Skip to content

Commit 6b1b308

Browse files
committed
C#: Adopt shared SSA data-flow integration
1 parent 7928d75 commit 6b1b308

File tree

11 files changed

+1720
-936
lines changed

11 files changed

+1720
-936
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: 98 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,9 @@ module VariableCapture {
256256
private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) {
257257
e1 = LocalFlow::getALastEvalNode(e2)
258258
or
259-
exists(Ssa::Definition def |
260-
LocalFlow::ssaDefAssigns(def.getAnUltimateDefinition(), e1) and
259+
exists(Ssa::Definition def, AssignableDefinition adef |
260+
LocalFlow::defAssigns(adef, _, e1) and
261+
def.getAnUltimateDefinition().(Ssa::ExplicitDefinition).getADefinition() = adef and
261262
exists(def.getAReadAtNode(e2))
262263
)
263264
}
@@ -481,6 +482,30 @@ module VariableCapture {
481482
}
482483
}
483484

485+
/** Provides logic related to SSA. */
486+
module SsaFlow {
487+
private module Impl = SsaImpl::DataFlowIntegration;
488+
489+
Impl::Node asNode(Node n) {
490+
n = TSsaNode(result)
491+
or
492+
result.(Impl::ExprNode).getExpr() = n.(ExprNode).getControlFlowNode()
493+
or
494+
result.(Impl::ExprPostUpdateNode).getExpr() =
495+
n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode()
496+
or
497+
TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n
498+
}
499+
500+
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
501+
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
502+
}
503+
504+
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
505+
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
506+
}
507+
}
508+
484509
/** Provides predicates related to local data flow. */
485510
module LocalFlow {
486511
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -606,105 +631,6 @@ module LocalFlow {
606631
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
607632
}
608633

609-
predicate ssaDefAssigns(Ssa::ExplicitDefinition ssaDef, ControlFlow::Nodes::ExprNode value) {
610-
exists(AssignableDefinition def, ControlFlow::Node cfnDef |
611-
any(LocalExprStepConfiguration conf).hasDefPath(_, value, def, cfnDef) and
612-
ssaDef.getADefinition() = def and
613-
ssaDef.getControlFlowNode() = cfnDef
614-
)
615-
}
616-
617-
/**
618-
* An uncertain SSA definition. Either an uncertain explicit definition or an
619-
* uncertain qualifier definition.
620-
*
621-
* Restricts `Ssa::UncertainDefinition` by excluding implicit call definitions,
622-
* as we---conservatively---consider such definitions to be certain.
623-
*/
624-
class UncertainExplicitSsaDefinition extends Ssa::UncertainDefinition {
625-
UncertainExplicitSsaDefinition() {
626-
this instanceof Ssa::ExplicitDefinition
627-
or
628-
this =
629-
any(Ssa::ImplicitQualifierDefinition qdef |
630-
qdef.getQualifierDefinition() instanceof UncertainExplicitSsaDefinition
631-
)
632-
}
633-
}
634-
635-
/** An SSA definition into which another SSA definition may flow. */
636-
private class SsaInputDefinitionExtNode extends SsaDefinitionExtNode {
637-
SsaInputDefinitionExtNode() {
638-
def instanceof Ssa::PhiNode
639-
or
640-
def instanceof SsaImpl::PhiReadNode
641-
or
642-
def instanceof LocalFlow::UncertainExplicitSsaDefinition
643-
}
644-
}
645-
646-
/**
647-
* Holds if `nodeFrom` is a last node referencing SSA definition `def`, which
648-
* can reach `next`.
649-
*/
650-
private predicate localFlowSsaInputFromDef(
651-
Node nodeFrom, SsaImpl::DefinitionExt def, SsaInputDefinitionExtNode next
652-
) {
653-
exists(ControlFlow::BasicBlock bb, int i |
654-
SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and
655-
def.definesAt(_, bb, i, _) and
656-
def = getSsaDefinitionExt(nodeFrom) and
657-
nodeFrom != next
658-
)
659-
}
660-
661-
/**
662-
* Holds if `read` is a last node reading SSA definition `def`, which
663-
* can reach `next`.
664-
*/
665-
predicate localFlowSsaInputFromRead(
666-
Node read, SsaImpl::DefinitionExt def, SsaInputDefinitionExtNode next
667-
) {
668-
exists(ControlFlow::BasicBlock bb, int i |
669-
SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and
670-
read.asExprAtNode(bb.getNode(i)) instanceof AssignableRead
671-
)
672-
}
673-
674-
private SsaImpl::DefinitionExt getSsaDefinitionExt(Node n) {
675-
result = n.(SsaDefinitionExtNode).getDefinitionExt()
676-
or
677-
result = n.(ExplicitParameterNode).getSsaDefinition()
678-
}
679-
680-
/**
681-
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
682-
* involving SSA definition `def`.
683-
*/
684-
predicate localSsaFlowStepUseUse(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
685-
exists(ControlFlow::Node cfnFrom, ControlFlow::Node cfnTo |
686-
SsaImpl::adjacentReadPairSameVarExt(def, cfnFrom, cfnTo) and
687-
nodeTo = TExprNode(cfnTo) and
688-
nodeFrom = TExprNode(cfnFrom)
689-
)
690-
}
691-
692-
/**
693-
* Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving
694-
* SSA definition `def`.
695-
*/
696-
predicate localSsaFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
697-
// Flow from SSA definition/parameter to first read
698-
def = getSsaDefinitionExt(nodeFrom) and
699-
SsaImpl::firstReadSameVarExt(def, nodeTo.(ExprNode).getControlFlowNode())
700-
or
701-
// Flow from read to next read
702-
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
703-
or
704-
// Flow into phi (read)/uncertain SSA definition node from def
705-
localFlowSsaInputFromDef(nodeFrom, def, nodeTo)
706-
}
707-
708634
/**
709635
* Holds if the source variable of SSA definition `def` is an instance field.
710636
*/
@@ -788,10 +714,7 @@ module LocalFlow {
788714
node2.asExpr() instanceof AssignExpr
789715
)
790716
or
791-
exists(SsaImpl::Definition def |
792-
def = getSsaDefinitionExt(node1) and
793-
exists(SsaImpl::getAReadAtNode(def, node2.(ExprNode).getControlFlowNode()))
794-
)
717+
SsaFlow::localMustFlowStep(_, node1, node2)
795718
or
796719
node2 = node1.(LocalFunctionCreationNode).getAnAccess(true)
797720
or
@@ -815,23 +738,14 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
815738
(
816739
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
817740
or
818-
exists(SsaImpl::DefinitionExt def |
741+
exists(SsaImpl::DefinitionExt def, boolean isUseStep |
742+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and
819743
not LocalFlow::usesInstanceField(def) and
820744
not def instanceof VariableCapture::CapturedSsaDefinitionExt
821745
|
822-
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo)
823-
or
824-
LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and
825-
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) and
826-
nodeFrom != nodeTo
746+
isUseStep = false
827747
or
828-
// Flow into phi (read)/uncertain SSA definition node from read
829-
exists(Node read | LocalFlow::localFlowSsaInputFromRead(read, def, nodeTo) |
830-
nodeFrom = read and
831-
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
832-
or
833-
nodeFrom.(PostUpdateNode).getPreUpdateNode() = read
834-
)
748+
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
835749
)
836750
or
837751
nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode)
@@ -1097,10 +1011,7 @@ private module Cached {
10971011
cached
10981012
newtype TNode =
10991013
TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getAstNode() instanceof Expr } or
1100-
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) {
1101-
// Handled by `TExplicitParameterNode` below
1102-
not def instanceof Ssa::ImplicitParameterDefinition
1103-
} or
1014+
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
11041015
TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
11051016
cfn = def.getExpr().getAControlFlowNode()
11061017
} or
@@ -1165,17 +1076,7 @@ private module Cached {
11651076
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
11661077
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
11671078
or
1168-
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) and
1169-
nodeFrom != nodeTo
1170-
or
1171-
LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo)
1172-
or
1173-
// Flow into phi (read)/uncertain SSA definition node from read
1174-
exists(Node read | LocalFlow::localFlowSsaInputFromRead(read, _, nodeTo) |
1175-
nodeFrom = read
1176-
or
1177-
nodeFrom.(PostUpdateNode).getPreUpdateNode() = read
1178-
)
1079+
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
11791080
or
11801081
// Simple flow through library code is included in the exposed local
11811082
// step relation, even though flow is technically inter-procedural
@@ -1233,13 +1134,18 @@ private module Cached {
12331134
newtype TDataFlowType =
12341135
TGvnDataFlowType(Gvn::GvnType t) or
12351136
TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) }
1137+
1138+
cached
1139+
Node getABarrierNode(Guard guard, Ssa::Definition def, boolean branch) {
1140+
SsaFlow::asNode(result) = SsaImpl::DataFlowIntegration::getABarrierNode(guard, def, branch)
1141+
}
12361142
}
12371143

12381144
import Cached
12391145

12401146
/** Holds if `n` should be hidden from path explanations. */
12411147
predicate nodeIsHidden(Node n) {
1242-
n instanceof SsaDefinitionExtNode
1148+
n instanceof SsaNode
12431149
or
12441150
exists(Parameter p | p = n.(ParameterNode).getParameter() | not p.fromSource())
12451151
or
@@ -1273,13 +1179,16 @@ predicate nodeIsHidden(Node n) {
12731179
n instanceof CaptureNode
12741180
}
12751181

1276-
/** An SSA definition, viewed as a node in a data flow graph. */
1277-
class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
1182+
/** An SSA node. */
1183+
abstract class SsaNode extends NodeImpl, TSsaNode {
1184+
SsaImpl::DataFlowIntegration::SsaNode node;
12781185
SsaImpl::DefinitionExt def;
12791186

1280-
SsaDefinitionExtNode() { this = TSsaDefinitionExtNode(def) }
1187+
SsaNode() {
1188+
this = TSsaNode(node) and
1189+
def = node.getDefinitionExt()
1190+
}
12811191

1282-
/** Gets the underlying SSA definition. */
12831192
SsaImpl::DefinitionExt getDefinitionExt() { result = def }
12841193

12851194
override DataFlowCallable getEnclosingCallableImpl() {
@@ -1292,9 +1201,57 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
12921201
result = def.(Ssa::Definition).getControlFlowNode()
12931202
}
12941203

1295-
override Location getLocationImpl() { result = def.getLocation() }
1204+
override Location getLocationImpl() { result = node.getLocation() }
1205+
1206+
override string toStringImpl() { result = node.toString() }
1207+
}
1208+
1209+
/** An (extended) SSA definition, viewed as a node in a data flow graph. */
1210+
class SsaDefinitionExtNode extends SsaNode {
1211+
override SsaImpl::DataFlowIntegration::SsaDefinitionExtNode node;
1212+
}
12961213

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

13001257
/** A definition, viewed as a node in a data flow graph. */
@@ -2844,7 +2801,7 @@ private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
28442801
/** Extra data-flow steps needed for lambda flow analysis. */
28452802
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
28462803
exists(SsaImpl::DefinitionExt def |
2847-
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and
2804+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, _) and
28482805
preservesValue = true
28492806
|
28502807
LocalFlow::usesInstanceField(def)

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,22 @@ signature predicate guardChecksSig(Guard g, Expr e, AbstractValue v);
171171
* in data flow and taint tracking.
172172
*/
173173
module BarrierGuard<guardChecksSig/3 guardChecks> {
174+
pragma[nomagic]
175+
private predicate guardChecksSsaDef(Guard g, Ssa::Definition def, boolean branch) {
176+
exists(AbstractValues::BooleanValue v |
177+
guardChecks(g, def.getARead(), v) and
178+
branch = v.getValue()
179+
)
180+
}
181+
174182
/** Gets a node that is safely guarded by the given guard check. */
175-
ExprNode getABarrierNode() {
183+
pragma[nomagic]
184+
Node getABarrierNode() {
185+
exists(Guard g, Ssa::Definition def, boolean branch |
186+
result = getABarrierNode(g, def, branch) and
187+
guardChecksSsaDef(g, def, branch)
188+
)
189+
or
176190
exists(Guard g, Expr e, AbstractValue v |
177191
guardChecks(g, e, v) and
178192
g.controlsNode(result.getControlFlowNode(), e, v)

0 commit comments

Comments
 (0)