Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eef1abf

Browse files
authored
Merge pull request #743 from hvitved/csharp/dataflow-splitting
C#: Teach data flow library about CFG splitting
2 parents c503ec4 + 078becc commit eef1abf

47 files changed

Lines changed: 1977 additions & 540 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

csharp/ql/src/semmle/code/csharp/Assignable.qll

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ class AssignableMemberAccess extends MemberAccess, AssignableAccess {
3939
override AssignableMember getTarget() { result = AssignableAccess.super.getTarget() }
4040
}
4141

42+
private predicate nameOfChild(NameOfExpr noe, Expr child) {
43+
child = noe
44+
or
45+
exists(Expr mid | nameOfChild(noe, mid) | child = mid.getAChildExpr())
46+
}
47+
4248
/**
4349
* An access to an assignable that reads the underlying value. Either a
4450
* variable read (`VariableRead`), a property read (`PropertyRead`), an
@@ -67,7 +73,7 @@ class AssignableRead extends AssignableAccess {
6773
or
6874
this = any(AssignableDefinitions::AddressOfDefinition def).getTargetAccess()
6975
) and
70-
not this = any(NameOfExpr noe).getAChildExpr*()
76+
not nameOfChild(_, this)
7177
}
7278

7379
/**
@@ -696,6 +702,9 @@ module AssignableDefinitions {
696702

697703
IsPatternDefinition() { this = TIsPatternDefinition(ipe) }
698704

705+
/** Gets the underlying `is` expression. */
706+
IsPatternExpr getIsPatternExpr() { result = ipe }
707+
699708
/** Gets the underlying local variable declaration. */
700709
LocalVariableDeclExpr getDeclaration() { result = ipe.getVariableDeclExpr() }
701710

@@ -721,6 +730,9 @@ module AssignableDefinitions {
721730

722731
TypeCasePatternDefinition() { this = TTypeCasePatternDefinition(tc) }
723732

733+
/** Gets the underlying `case` statement. */
734+
TypeCase getTypeCase() { result = tc }
735+
724736
/** Gets the underlying local variable declaration. */
725737
LocalVariableDeclExpr getDeclaration() { result = tc.getVariableDeclExpr() }
726738

csharp/ql/src/semmle/code/csharp/Stmt.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ class CaseStmt extends Stmt, @case {
262262
* ```
263263
*/
264264
Expr getCondition() { result = this.getChild(2) }
265+
266+
/** Gets the `switch` statement that this `case` statement belongs to. */
267+
SwitchStmt getSwitchStmt() { result.getACase() = this }
265268
}
266269

267270
/**

csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,8 @@ module ControlFlow {
867867
* pre-order.
868868
*/
869869
private module Successor {
870+
private import semmle.code.csharp.ExprOrStmtParent
871+
870872
/**
871873
* A control flow element where the children are evaluated following a
872874
* standard left-to-right evaluation. The actual evaluation order is
@@ -2436,11 +2438,16 @@ module ControlFlow {
24362438
* Gets the control flow element that is first executed when entering
24372439
* callable `c`.
24382440
*/
2439-
ControlFlowElement succEntry(Callable c) {
2440-
if exists(c.(Constructor).getInitializer()) then
2441-
result = first(c.(Constructor).getInitializer())
2442-
else
2443-
result = first(c.getBody())
2441+
ControlFlowElement succEntry(@top_level_exprorstmt_parent p) {
2442+
p = any(Callable c |
2443+
if exists(c.(Constructor).getInitializer()) then
2444+
result = first(c.(Constructor).getInitializer())
2445+
else
2446+
result = first(c.getBody())
2447+
)
2448+
or
2449+
expr_parent_top_level_adjusted(any(Expr e | result = first(e)), _, p) and
2450+
not p instanceof Callable
24442451
}
24452452

24462453
/**
@@ -3807,8 +3814,8 @@ module ControlFlow {
38073814
* Holds if `succ` with splits `succSplits` is the first element that is executed
38083815
* when entering callable `pred`.
38093816
*/
3810-
pragma [noinline]
3811-
predicate succEntrySplits(Callable pred, ControlFlowElement succ, Splits succSplits, SuccessorType t) {
3817+
pragma[noinline]
3818+
predicate succEntrySplits(@top_level_exprorstmt_parent pred, ControlFlowElement succ, Splits succSplits, SuccessorType t) {
38123819
succ = succEntry(pred) and
38133820
t instanceof NormalSuccessor and
38143821
succSplits = TSplitsNil() // initially no splits

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 158 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -417,26 +417,30 @@ class AccessOrCallExpr extends Expr {
417417
* An expression can have more than one SSA qualifier in the presence
418418
* of control flow splitting.
419419
*/
420-
Ssa::Definition getAnSsaQualifier() { result = getAnSsaQualifier(this) }
420+
Ssa::Definition getAnSsaQualifier(ControlFlow::Node cfn) {
421+
result = getAnSsaQualifier(this, cfn)
422+
}
421423
}
422424

423425
private Declaration getDeclarationTarget(Expr e) {
424426
e = any(AssignableAccess aa | result = aa.getTarget()) or
425427
result = e.(Call).getTarget()
426428
}
427429

428-
private Ssa::Definition getAnSsaQualifier(Expr e) {
429-
e = getATrackedAccess(result)
430+
private Ssa::Definition getAnSsaQualifier(Expr e, ControlFlow::Node cfn) {
431+
e = getATrackedAccess(result, cfn)
430432
or
431-
not e = getATrackedAccess(_) and
432-
result = getAnSsaQualifier(e.(QualifiableExpr).getQualifier())
433+
not e = getATrackedAccess(_, _) and
434+
result = getAnSsaQualifier(e.(QualifiableExpr).getQualifier(), cfn)
433435
}
434436

435-
private AssignableAccess getATrackedAccess(Ssa::Definition def) {
437+
private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Node cfn) {
436438
(
437-
result = def.getARead()
439+
result = def.getAReadAtNode(cfn)
438440
or
439-
result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess()
441+
result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() and
442+
result.getAControlFlowNode() = cfn and
443+
cfn.getBasicBlock() = def.getBasicBlock()
440444
) and
441445
not def instanceof Ssa::ImplicitUntrackedDefinition
442446
}
@@ -483,7 +487,7 @@ class GuardedExpr extends AccessOrCallExpr {
483487
private AccessOrCallExpr sub0;
484488
private AbstractValue v0;
485489

486-
GuardedExpr() { isGuardedBy(this, g, sub0, v0) }
490+
GuardedExpr() { isGuardedByExpr(this, g, sub0, v0) }
487491

488492
/**
489493
* Gets an expression that guards this expression. That is, this expression is
@@ -525,13 +529,129 @@ class GuardedExpr extends AccessOrCallExpr {
525529
}
526530
}
527531

532+
/**
533+
* A guarded control flow node. A guarded control flow node is like a guarded
534+
* expression (`GuardedExpr`), except control flow graph splitting is taken
535+
* into account. That is, one control flow node belonging to an expression may
536+
* be guarded, while another split need not be guarded:
537+
*
538+
* ```
539+
* if (b)
540+
* if (x == null)
541+
* return;
542+
* x.ToString();
543+
* if (b)
544+
* ...
545+
* ```
546+
*
547+
* In the example above, the node for `x.ToString()` is null-guarded in the
548+
* split `b == true`, but not in the split `b == false`.
549+
*/
550+
class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
551+
private Guard g;
552+
private AccessOrCallExpr sub0;
553+
private AbstractValue v0;
554+
555+
GuardedControlFlowNode() { isGuardedByNode(this, g, sub0, v0) }
556+
557+
/**
558+
* Gets an expression that guards this control flow node. That is, this control
559+
* flow node is only reached when the returned expression has abstract value `v`.
560+
*
561+
* The expression `sub` is a sub expression of the guarding expression that is
562+
* structurally equal to the expression belonging to this control flow node.
563+
*
564+
* In case this control flow node or `sub` accesses an SSA variable in its
565+
* left-most qualifier, then so must the other (accessing the same SSA
566+
* variable).
567+
*/
568+
Expr getAGuard(Expr sub, AbstractValue v) {
569+
result = g and
570+
sub = sub0 and
571+
v = v0
572+
}
573+
574+
/**
575+
* Holds if this control flow node must have abstract value `v`. That is, this
576+
* control flow node is guarded by a structurally equal expression having
577+
* abstract value `v`.
578+
*/
579+
predicate mustHaveValue(AbstractValue v) {
580+
exists(Expr e | e = this.getAGuard(e, v))
581+
}
582+
}
583+
584+
/**
585+
* A guarded data flow node. A guarded data flow node is like a guarded expression
586+
* (`GuardedExpr`), except control flow graph splitting is taken into account. That
587+
* is, one data flow node belonging to an expression may be guarded, while another
588+
* split need not be guarded:
589+
*
590+
* ```
591+
* if (b)
592+
* if (x == null)
593+
* return;
594+
* x.ToString();
595+
* if (b)
596+
* ...
597+
* ```
598+
*
599+
* In the example above, the node for `x.ToString()` is null-guarded in the
600+
* split `b == true`, but not in the split `b == false`.
601+
*/
602+
class GuardedDataFlowNode extends DataFlow::ExprNode {
603+
private Guard g;
604+
private AccessOrCallExpr sub0;
605+
private AbstractValue v0;
606+
607+
GuardedDataFlowNode() {
608+
exists(ControlFlow::Nodes::ElementNode cfn |
609+
exists(this.getExprAtNode(cfn)) |
610+
isGuardedByNode(cfn, g, sub0, v0)
611+
)
612+
}
613+
614+
/**
615+
* Gets an expression that guards this data flow node. That is, this data flow
616+
* node is only reached when the returned expression has abstract value `v`.
617+
*
618+
* The expression `sub` is a sub expression of the guarding expression that is
619+
* structurally equal to the expression belonging to this data flow node.
620+
*
621+
* In case this data flow node or `sub` accesses an SSA variable in its
622+
* left-most qualifier, then so must the other (accessing the same SSA
623+
* variable).
624+
*/
625+
Expr getAGuard(Expr sub, AbstractValue v) {
626+
result = g and
627+
sub = sub0 and
628+
v = v0
629+
}
630+
631+
/**
632+
* Holds if this data flow node must have abstract value `v`. That is, this
633+
* data flow node is guarded by a structurally equal expression having
634+
* abstract value `v`.
635+
*/
636+
predicate mustHaveValue(AbstractValue v) {
637+
exists(Expr e | e = this.getAGuard(e, v))
638+
}
639+
}
640+
528641
/** An expression guarded by a `null` check. */
529642
class NullGuardedExpr extends GuardedExpr {
530643
NullGuardedExpr() {
531644
this.mustHaveValue(any(NullValue v | not v.isNull()))
532645
}
533646
}
534647

648+
/** A data flow node guarded by a `null` check. */
649+
class NullGuardedDataFlowNode extends GuardedDataFlowNode {
650+
NullGuardedDataFlowNode() {
651+
this.mustHaveValue(any(NullValue v | not v.isNull()))
652+
}
653+
}
654+
535655
/** INTERNAL: Do not use. */
536656
module Internal {
537657
private import ControlFlow::Internal
@@ -653,7 +773,7 @@ module Internal {
653773
* Holds if control flow node `cfn` only is reached when this guard evaluates to `v`,
654774
* because of an assertion.
655775
*/
656-
private predicate assertionControlsNode(ControlFlow::Node cfn, AbstractValue v) {
776+
predicate assertionControlsNode(ControlFlow::Node cfn, AbstractValue v) {
657777
exists(Assertion a, Guard g, AbstractValue v0 |
658778
asserts(a, g, v0) and
659779
impliesSteps(g, v0, this, v)
@@ -1097,30 +1217,51 @@ module Internal {
10971217

10981218
private cached module Cached {
10991219
pragma[noinline]
1100-
private predicate isGuardedBy0(ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1220+
private predicate isGuardedByNode0(ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
11011221
cfn = guarded.getAControlFlowNode() and
11021222
g.controls(cfn.getBasicBlock(), v) and
11031223
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
11041224
}
11051225

11061226
pragma[noinline]
1107-
private predicate isGuardedBy1(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1227+
private predicate isGuardedByExpr1(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
11081228
forex(ControlFlow::Node cfn |
11091229
cfn = guarded.getAControlFlowNode() |
1110-
isGuardedBy0(cfn, guarded, g, sub, v)
1230+
isGuardedByNode0(cfn, guarded, g, sub, v)
11111231
)
11121232
or
11131233
g.assertionControlsElement(guarded, v) and
11141234
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
11151235
}
11161236

11171237
cached
1118-
predicate isGuardedBy(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1119-
isGuardedBy1(guarded, g, sub, v) and
1238+
predicate isGuardedByExpr(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1239+
isGuardedByExpr1(guarded, g, sub, v) and
1240+
sub = g.getAChildExpr*() and
1241+
forall(Ssa::Definition def |
1242+
def = sub.getAnSsaQualifier(_) |
1243+
def = guarded.getAnSsaQualifier(_)
1244+
)
1245+
}
1246+
1247+
pragma[noinline]
1248+
private predicate isGuardedByNode1(ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1249+
isGuardedByNode0(guarded, _, g, sub, v)
1250+
or
1251+
g.assertionControlsNode(guarded, v) and
1252+
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement()))
1253+
}
1254+
1255+
cached
1256+
predicate isGuardedByNode(ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1257+
isGuardedByNode1(guarded, g, sub, v) and
11201258
sub = g.getAChildExpr*() and
11211259
forall(Ssa::Definition def |
1122-
def = sub.getAnSsaQualifier() |
1123-
def = guarded.getAnSsaQualifier()
1260+
def = sub.getAnSsaQualifier(_) |
1261+
exists(ControlFlow::Node cfn |
1262+
def = guarded.getElement().(AccessOrCallExpr).getAnSsaQualifier(cfn) |
1263+
cfn.getBasicBlock() = guarded.getBasicBlock()
1264+
)
11241265
)
11251266
}
11261267

0 commit comments

Comments
 (0)