Skip to content

Commit b1794cc

Browse files
committed
C#: Adopt shared SSA data-flow integration
1 parent 4815885 commit b1794cc

File tree

10 files changed

+1698
-935
lines changed

10 files changed

+1698
-935
lines changed

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

Lines changed: 166 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ module VariableCapture {
257257
e1 = LocalFlow::getALastEvalNode(e2)
258258
or
259259
exists(Ssa::Definition def |
260-
LocalFlow::ssaDefAssigns(def.getAnUltimateDefinition(), e1) and
260+
SsaFlow::Input::ssaDefAssigns(def.getAnUltimateDefinition(), e1) and
261261
exists(def.getAReadAtNode(e2))
262262
)
263263
}
@@ -481,6 +481,103 @@ module VariableCapture {
481481
}
482482
}
483483

484+
/** Provides logic related to SSA. */
485+
module SsaFlow {
486+
module Input implements SsaImpl::Impl::DataFlowIntegrationInputSig {
487+
private import csharp as Cs
488+
489+
class Expr extends ControlFlow::Node {
490+
predicate hasCfgNode(ControlFlow::BasicBlock bb, int i) { this = bb.getNode(i) }
491+
}
492+
493+
predicate ssaDefAssigns(SsaImpl::WriteDefinition def, Expr value) {
494+
exists(AssignableDefinition adef, ControlFlow::Node cfnDef |
495+
any(LocalFlow::LocalExprStepConfiguration conf).hasDefPath(_, value, adef, cfnDef) and
496+
def.(Ssa::ExplicitDefinition).getADefinition() = adef and
497+
def.(Ssa::ExplicitDefinition).getControlFlowNode() = cfnDef
498+
)
499+
}
500+
501+
class Parameter = Cs::Parameter;
502+
503+
predicate ssaDefInitializesParam(SsaImpl::WriteDefinition def, Parameter p) {
504+
def.(Ssa::ImplicitParameterDefinition).getParameter() = p
505+
}
506+
507+
/**
508+
* An uncertain SSA definition. Either an uncertain explicit definition or an
509+
* uncertain qualifier definition.
510+
*
511+
* Restricts `Ssa::UncertainDefinition` by excluding implicit call definitions,
512+
* as we---conservatively---consider such definitions to be certain.
513+
*/
514+
predicate allowFlowIntoUncertainDef(SsaImpl::UncertainWriteDefinition def) {
515+
def instanceof Ssa::ExplicitDefinition
516+
or
517+
def =
518+
any(Ssa::ImplicitQualifierDefinition qdef |
519+
allowFlowIntoUncertainDef(qdef.getQualifierDefinition())
520+
)
521+
}
522+
}
523+
524+
module Impl = SsaImpl::Impl::DataFlowIntegration<Input>;
525+
526+
Impl::Node asNode(Node n) {
527+
n = TSsaNode(result)
528+
or
529+
result.(Impl::ExprNode).getExpr() = n.(ExprNode).getControlFlowNode()
530+
or
531+
result.(Impl::ExprPostUpdateNode).getExpr() =
532+
n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode()
533+
or
534+
TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n
535+
}
536+
537+
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
538+
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo)) and
539+
// exclude flow directly from RHS to SSA definition, as we instead want to
540+
// go from RHS to matching assingnable definition, and from there to SSA definition
541+
not Input::ssaDefAssigns(def, nodeFrom.(ExprNode).getControlFlowNode())
542+
}
543+
544+
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
545+
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
546+
}
547+
548+
module BarrierGuardsInput implements Impl::BarrierGuardsInputSig {
549+
private import semmle.code.csharp.controlflow.BasicBlocks
550+
private import semmle.code.csharp.controlflow.Guards as Guards
551+
552+
class Guard extends Guards::Guard {
553+
predicate hasCfgNode(ControlFlow::BasicBlock bb, int i) {
554+
this.getAControlFlowNode() = bb.getNode(i)
555+
}
556+
}
557+
558+
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
559+
predicate guardControlsBlock(Guard guard, ControlFlow::BasicBlock bb, boolean branch) {
560+
exists(ConditionBlock conditionBlock, ControlFlow::SuccessorTypes::ConditionalSuccessor s |
561+
guard.getAControlFlowNode() = conditionBlock.getLastNode() and
562+
s.getValue() = branch and
563+
conditionBlock.controls(bb, s)
564+
)
565+
}
566+
567+
/** Gets an immediate conditional successor of basic block `bb`, if any. */
568+
ControlFlow::BasicBlock getAConditionalBasicBlockSuccessor(
569+
ControlFlow::BasicBlock bb, boolean branch
570+
) {
571+
exists(ControlFlow::SuccessorTypes::ConditionalSuccessor s |
572+
result = bb.getASuccessorByType(s) and
573+
s.getValue() = branch
574+
)
575+
}
576+
}
577+
578+
module BarrierGuardsImpl = Impl::BarrierGuards<BarrierGuardsInput>;
579+
}
580+
484581
/** Provides predicates related to local data flow. */
485582
module LocalFlow {
486583
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -606,105 +703,6 @@ module LocalFlow {
606703
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
607704
}
608705

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-
708706
/**
709707
* Holds if the source variable of SSA definition `def` is an instance field.
710708
*/
@@ -788,10 +786,7 @@ module LocalFlow {
788786
node2.asExpr() instanceof AssignExpr
789787
)
790788
or
791-
exists(SsaImpl::Definition def |
792-
def = getSsaDefinitionExt(node1) and
793-
exists(SsaImpl::getAReadAtNode(def, node2.(ExprNode).getControlFlowNode()))
794-
)
789+
SsaFlow::localMustFlowStep(_, node1, node2)
795790
or
796791
node2 = node1.(LocalFunctionCreationNode).getAnAccess(true)
797792
or
@@ -816,22 +811,10 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
816811
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
817812
or
818813
exists(SsaImpl::DefinitionExt def |
814+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo) and
819815
not LocalFlow::usesInstanceField(def) and
820-
not def instanceof VariableCapture::CapturedSsaDefinitionExt
821-
|
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
827-
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-
)
816+
not def instanceof VariableCapture::CapturedSsaDefinitionExt and
817+
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
835818
)
836819
or
837820
nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode)
@@ -1097,10 +1080,7 @@ private module Cached {
10971080
cached
10981081
newtype TNode =
10991082
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
1083+
TSsaNode(SsaFlow::Impl::SsaNode node) or
11041084
TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
11051085
cfn = def.getExpr().getAControlFlowNode()
11061086
} or
@@ -1165,17 +1145,7 @@ private module Cached {
11651145
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
11661146
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
11671147
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-
)
1148+
SsaFlow::localFlowStep(_, nodeFrom, nodeTo)
11791149
or
11801150
// Simple flow through library code is included in the exposed local
11811151
// step relation, even though flow is technically inter-procedural
@@ -1239,7 +1209,7 @@ import Cached
12391209

12401210
/** Holds if `n` should be hidden from path explanations. */
12411211
predicate nodeIsHidden(Node n) {
1242-
n instanceof SsaDefinitionExtNode
1212+
n instanceof SsaNode
12431213
or
12441214
exists(Parameter p | p = n.(ParameterNode).getParameter() | not p.fromSource())
12451215
or
@@ -1273,13 +1243,16 @@ predicate nodeIsHidden(Node n) {
12731243
n instanceof CaptureNode
12741244
}
12751245

1276-
/** An SSA definition, viewed as a node in a data flow graph. */
1277-
class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
1246+
/** An SSA node. */
1247+
abstract class SsaNode extends NodeImpl, TSsaNode {
1248+
SsaFlow::Impl::SsaNode node;
12781249
SsaImpl::DefinitionExt def;
12791250

1280-
SsaDefinitionExtNode() { this = TSsaDefinitionExtNode(def) }
1251+
SsaNode() {
1252+
this = TSsaNode(node) and
1253+
def = node.getDefinitionExt()
1254+
}
12811255

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

12851258
override DataFlowCallable getEnclosingCallableImpl() {
@@ -1292,9 +1265,60 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
12921265
result = def.(Ssa::Definition).getControlFlowNode()
12931266
}
12941267

1295-
override Location getLocationImpl() { result = def.getLocation() }
1268+
override Location getLocationImpl() { result = node.getLocation() }
1269+
1270+
override string toStringImpl() { result = node.toString() }
1271+
}
1272+
1273+
/** An (extended) SSA definition, viewed as a node in a data flow graph. */
1274+
class SsaDefinitionExtNode extends SsaNode {
1275+
override SsaFlow::Impl::SsaDefinitionExtNode node;
1276+
}
1277+
1278+
/**
1279+
* A node that represents an input to an SSA phi (read) definition.
1280+
*
1281+
* This allows for barrier guards to filter input to phi nodes. For example, in
1282+
*
1283+
* ```csharp
1284+
* var x = taint;
1285+
* if (x != "safe")
1286+
* {
1287+
* x = "safe";
1288+
* }
1289+
* sink(x);
1290+
* ```
1291+
*
1292+
* the `false` edge out of `x != "safe"` guards the input from `x = taint` into the
1293+
* `phi` node after the condition.
1294+
*
1295+
* It is also relevant to filter input into phi read nodes:
1296+
*
1297+
* ```csharp
1298+
* var x = taint;
1299+
* if (b)
1300+
* {
1301+
* if (x != "safe1")
1302+
* {
1303+
* return;
1304+
* }
1305+
* } else {
1306+
* if (x != "safe2")
1307+
* {
1308+
* return;
1309+
* }
1310+
* }
1311+
*
1312+
* sink(x);
1313+
* ```
1314+
*
1315+
* both inputs into the phi read node after the outer condition are guarded.
1316+
*/
1317+
class SsaInputNode extends SsaNode {
1318+
override SsaFlow::Impl::SsaInputNode node;
1319+
ControlFlow::BasicBlock input;
12961320

1297-
override string toStringImpl() { result = def.toString() }
1321+
SsaInputNode() { node.isInputInto(def, input) }
12981322
}
12991323

13001324
/** A definition, viewed as a node in a data flow graph. */
@@ -2844,7 +2868,7 @@ private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
28442868
/** Extra data-flow steps needed for lambda flow analysis. */
28452869
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
28462870
exists(SsaImpl::DefinitionExt def |
2847-
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and
2871+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo) and
28482872
preservesValue = true
28492873
|
28502874
LocalFlow::usesInstanceField(def)

0 commit comments

Comments
 (0)