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

Skip to content

Commit abb3f71

Browse files
committed
C#: Add GuardedControlFlowNode
1 parent f323049 commit abb3f71

5 files changed

Lines changed: 675 additions & 17 deletions

File tree

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

Lines changed: 94 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,6 +529,58 @@ 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+
528584
/** An expression guarded by a `null` check. */
529585
class NullGuardedExpr extends GuardedExpr {
530586
NullGuardedExpr() {
@@ -653,7 +709,7 @@ module Internal {
653709
* Holds if control flow node `cfn` only is reached when this guard evaluates to `v`,
654710
* because of an assertion.
655711
*/
656-
private predicate assertionControlsNode(ControlFlow::Node cfn, AbstractValue v) {
712+
predicate assertionControlsNode(ControlFlow::Node cfn, AbstractValue v) {
657713
exists(Assertion a, Guard g, AbstractValue v0 |
658714
asserts(a, g, v0) and
659715
impliesSteps(g, v0, this, v)
@@ -1097,30 +1153,51 @@ module Internal {
10971153

10981154
private cached module Cached {
10991155
pragma[noinline]
1100-
private predicate isGuardedBy0(ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1156+
private predicate isGuardedByNode0(ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
11011157
cfn = guarded.getAControlFlowNode() and
11021158
g.controls(cfn.getBasicBlock(), v) and
11031159
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
11041160
}
11051161

11061162
pragma[noinline]
1107-
private predicate isGuardedBy1(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1163+
private predicate isGuardedByExpr1(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
11081164
forex(ControlFlow::Node cfn |
11091165
cfn = guarded.getAControlFlowNode() |
1110-
isGuardedBy0(cfn, guarded, g, sub, v)
1166+
isGuardedByNode0(cfn, guarded, g, sub, v)
11111167
)
11121168
or
11131169
g.assertionControlsElement(guarded, v) and
11141170
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
11151171
}
11161172

11171173
cached
1118-
predicate isGuardedBy(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1119-
isGuardedBy1(guarded, g, sub, v) and
1174+
predicate isGuardedByExpr(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1175+
isGuardedByExpr1(guarded, g, sub, v) and
11201176
sub = g.getAChildExpr*() and
11211177
forall(Ssa::Definition def |
1122-
def = sub.getAnSsaQualifier() |
1123-
def = guarded.getAnSsaQualifier()
1178+
def = sub.getAnSsaQualifier(_) |
1179+
def = guarded.getAnSsaQualifier(_)
1180+
)
1181+
}
1182+
1183+
pragma[noinline]
1184+
private predicate isGuardedByNode1(ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1185+
isGuardedByNode0(guarded, _, g, sub, v)
1186+
or
1187+
g.assertionControlsNode(guarded, v) and
1188+
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement()))
1189+
}
1190+
1191+
cached
1192+
predicate isGuardedByNode(ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
1193+
isGuardedByNode1(guarded, g, sub, v) and
1194+
sub = g.getAChildExpr*() and
1195+
forall(Ssa::Definition def |
1196+
def = sub.getAnSsaQualifier(_) |
1197+
exists(ControlFlow::Node cfn |
1198+
def = guarded.getElement().(AccessOrCallExpr).getAnSsaQualifier(cfn) |
1199+
cfn.getBasicBlock() = guarded.getBasicBlock()
1200+
)
11241201
)
11251202
}
11261203

0 commit comments

Comments
 (0)