Skip to content

Commit bd303e8

Browse files
committed
C#: Adopt shared SSA data-flow integration
1 parent 615f2a2 commit bd303e8

File tree

11 files changed

+1694
-937
lines changed

11 files changed

+1694
-937
lines changed

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

+1-1
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

+160-142
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,100 @@ 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+
* Allows for flow into uncertain defintions that are not call definitions,
509+
* as we, conservatively, consider such definitions to be certain.
510+
*/
511+
predicate allowFlowIntoUncertainDef(SsaImpl::UncertainWriteDefinition def) {
512+
def instanceof Ssa::ExplicitDefinition
513+
or
514+
def =
515+
any(Ssa::ImplicitQualifierDefinition qdef |
516+
allowFlowIntoUncertainDef(qdef.getQualifierDefinition())
517+
)
518+
}
519+
}
520+
521+
module Impl = SsaImpl::Impl::DataFlowIntegration<Input>;
522+
523+
Impl::Node asNode(Node n) {
524+
n = TSsaNode(result)
525+
or
526+
result.(Impl::ExprNode).getExpr() = n.(ExprNode).getControlFlowNode()
527+
or
528+
result.(Impl::ExprPostUpdateNode).getExpr() =
529+
n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode()
530+
or
531+
TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n
532+
}
533+
534+
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
535+
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo)) and
536+
// exclude flow directly from RHS to SSA definition, as we instead want to
537+
// go from RHS to matching assingnable definition, and from there to SSA definition
538+
not Input::ssaDefAssigns(def, nodeFrom.(ExprNode).getControlFlowNode())
539+
}
540+
541+
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
542+
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
543+
}
544+
545+
module BarrierGuardsInput implements Impl::BarrierGuardsInputSig {
546+
private import semmle.code.csharp.controlflow.BasicBlocks
547+
private import semmle.code.csharp.controlflow.Guards as Guards
548+
549+
class Guard extends Guards::Guard {
550+
predicate hasCfgNode(ControlFlow::BasicBlock bb, int i) {
551+
this.getAControlFlowNode() = bb.getNode(i)
552+
}
553+
}
554+
555+
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
556+
predicate guardControlsBlock(Guard guard, ControlFlow::BasicBlock bb, boolean branch) {
557+
exists(ConditionBlock conditionBlock, ControlFlow::SuccessorTypes::ConditionalSuccessor s |
558+
guard.getAControlFlowNode() = conditionBlock.getLastNode() and
559+
s.getValue() = branch and
560+
conditionBlock.controls(bb, s)
561+
)
562+
}
563+
564+
/** Gets an immediate conditional successor of basic block `bb`, if any. */
565+
ControlFlow::BasicBlock getAConditionalBasicBlockSuccessor(
566+
ControlFlow::BasicBlock bb, boolean branch
567+
) {
568+
exists(ControlFlow::SuccessorTypes::ConditionalSuccessor s |
569+
result = bb.getASuccessorByType(s) and
570+
s.getValue() = branch
571+
)
572+
}
573+
}
574+
575+
module BarrierGuardsImpl = Impl::BarrierGuards<BarrierGuardsInput>;
576+
}
577+
484578
/** Provides predicates related to local data flow. */
485579
module LocalFlow {
486580
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -606,105 +700,6 @@ module LocalFlow {
606700
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
607701
}
608702

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-
708703
/**
709704
* Holds if the source variable of SSA definition `def` is an instance field.
710705
*/
@@ -788,10 +783,7 @@ module LocalFlow {
788783
node2.asExpr() instanceof AssignExpr
789784
)
790785
or
791-
exists(SsaImpl::Definition def |
792-
def = getSsaDefinitionExt(node1) and
793-
exists(SsaImpl::getAReadAtNode(def, node2.(ExprNode).getControlFlowNode()))
794-
)
786+
SsaFlow::localMustFlowStep(_, node1, node2)
795787
or
796788
node2 = node1.(LocalFunctionCreationNode).getAnAccess(true)
797789
or
@@ -816,22 +808,10 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
816808
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
817809
or
818810
exists(SsaImpl::DefinitionExt def |
811+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo) and
819812
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-
)
813+
not def instanceof VariableCapture::CapturedSsaDefinitionExt and
814+
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
835815
)
836816
or
837817
nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode)
@@ -1097,10 +1077,7 @@ private module Cached {
10971077
cached
10981078
newtype TNode =
10991079
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
1080+
TSsaNode(SsaFlow::Impl::SsaNode node) or
11041081
TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
11051082
cfn = def.getExpr().getAControlFlowNode()
11061083
} or
@@ -1165,17 +1142,7 @@ private module Cached {
11651142
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
11661143
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
11671144
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-
)
1145+
SsaFlow::localFlowStep(_, nodeFrom, nodeTo)
11791146
or
11801147
// Simple flow through library code is included in the exposed local
11811148
// step relation, even though flow is technically inter-procedural
@@ -1239,7 +1206,7 @@ import Cached
12391206

12401207
/** Holds if `n` should be hidden from path explanations. */
12411208
predicate nodeIsHidden(Node n) {
1242-
n instanceof SsaDefinitionExtNode
1209+
n instanceof SsaNode
12431210
or
12441211
exists(Parameter p | p = n.(ParameterNode).getParameter() | not p.fromSource())
12451212
or
@@ -1273,13 +1240,16 @@ predicate nodeIsHidden(Node n) {
12731240
n instanceof CaptureNode
12741241
}
12751242

1276-
/** An SSA definition, viewed as a node in a data flow graph. */
1277-
class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
1243+
/** An SSA node. */
1244+
abstract class SsaNode extends NodeImpl, TSsaNode {
1245+
SsaFlow::Impl::SsaNode node;
12781246
SsaImpl::DefinitionExt def;
12791247

1280-
SsaDefinitionExtNode() { this = TSsaDefinitionExtNode(def) }
1248+
SsaNode() {
1249+
this = TSsaNode(node) and
1250+
def = node.getDefinitionExt()
1251+
}
12811252

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

12851255
override DataFlowCallable getEnclosingCallableImpl() {
@@ -1292,9 +1262,57 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
12921262
result = def.(Ssa::Definition).getControlFlowNode()
12931263
}
12941264

1295-
override Location getLocationImpl() { result = def.getLocation() }
1265+
override Location getLocationImpl() { result = node.getLocation() }
12961266

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

13001318
/** A definition, viewed as a node in a data flow graph. */
@@ -2844,7 +2862,7 @@ private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
28442862
/** Extra data-flow steps needed for lambda flow analysis. */
28452863
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
28462864
exists(SsaImpl::DefinitionExt def |
2847-
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and
2865+
SsaFlow::localFlowStep(def, nodeFrom, nodeTo) and
28482866
preservesValue = true
28492867
|
28502868
LocalFlow::usesInstanceField(def)

0 commit comments

Comments
 (0)