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

Skip to content

Commit b2f99db

Browse files
committed
C#: Teach data flow library about CFG splitting
Data flow nodes for expressions do not take CFG splitting into account. Example: ``` if (b) x = tainted; x = x.ToLower(); if (!b) Use(x); ``` Flow is incorrectly reported from `tainted` to `x` in `Use(x)`, because the step from `tainted` to `x.ToLower()` throws away the information that `b = true`. The solution is to remember the splitting in data flow expression nodes, that is, to represent the exact control flow node instead of just the expression. With that we get flow from `tainted` to `[b = true] x.ToLower()`, but not from `tainted` to `[b = false] x.ToLower()`. The data flow API remains unchanged, but in order for analyses to fully benefit from CFG splitting, sanitizers in particular should be CFG-based instead of expression-based: ``` if (b) x = tainted; if (IsInvalid(x)) return; Use(x); ``` If the call to `IsInvalid()` is a sanitizer, then defining an expression node to be a sanitizer using `GuardedExpr` will be too conservative (`x` in `Use(x)` is in fact not guarded). However, `[b = true] x` in `[b = true] Use(x)` is guarded, and to help defining guard-based sanitizers, the class `GuardedDataFlowNode` has been introduced.
1 parent f768abb commit b2f99db

27 files changed

Lines changed: 811 additions & 344 deletions

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
/**
@@ -669,6 +675,9 @@ module AssignableDefinitions {
669675

670676
IsPatternDefinition() { this = TIsPatternDefinition(ipe) }
671677

678+
/** Gets the underlying `is` expression. */
679+
IsPatternExpr getIsPatternExpr() { result = ipe }
680+
672681
/** Gets the underlying local variable declaration. */
673682
LocalVariableDeclExpr getDeclaration() { result = ipe.getVariableDeclExpr() }
674683

@@ -696,6 +705,9 @@ module AssignableDefinitions {
696705

697706
TypeCasePatternDefinition() { this = TTypeCasePatternDefinition(tc) }
698707

708+
/** Gets the underlying `case` statement. */
709+
TypeCase getTypeCase() { result = tc }
710+
699711
/** Gets the underlying local variable declaration. */
700712
LocalVariableDeclExpr getDeclaration() { result = tc.getVariableDeclExpr() }
701713

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/Guards.qll

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,77 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
581581
}
582582
}
583583

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+
584641
/** An expression guarded by a `null` check. */
585642
class NullGuardedExpr extends GuardedExpr {
586643
NullGuardedExpr() {
587644
this.mustHaveValue(any(NullValue v | not v.isNull()))
588645
}
589646
}
590647

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+
591655
/** INTERNAL: Do not use. */
592656
module Internal {
593657
private import ControlFlow::Internal

0 commit comments

Comments
 (0)