Skip to content

Commit bc9a16b

Browse files
committed
C#: Revert to using GuardedDataFlowNode in TaintedPath.qll
1 parent a75e75f commit bc9a16b

8 files changed

Lines changed: 42 additions & 87 deletions

File tree

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

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ class GuardedExpr extends AccessOrCallExpr {
498498
* left-most qualifier, then so must the other (accessing the same SSA
499499
* variable).
500500
*/
501-
Expr getAGuard(Expr sub, AbstractValue v) {
501+
Guard getAGuard(Expr sub, AbstractValue v) {
502502
result = g and
503503
sub = sub0 and
504504
v = v0
@@ -509,7 +509,7 @@ class GuardedExpr extends AccessOrCallExpr {
509509
* expression is guarded by a structurally equal expression having abstract
510510
* value `v`.
511511
*/
512-
predicate mustHaveValue(AbstractValue v) { exists(Expr e | e = this.getAGuard(e, v)) }
512+
predicate mustHaveValue(AbstractValue v) { g = this.getAGuard(g, v) }
513513

514514
/**
515515
* Holds if this expression is guarded by expression `cond`, which must
@@ -563,7 +563,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
563563
* left-most qualifier, then so must the other (accessing the same SSA
564564
* variable).
565565
*/
566-
Expr getAGuard(Expr sub, AbstractValue v) {
566+
Guard getAGuard(Expr sub, AbstractValue v) {
567567
result = g and
568568
sub = sub0 and
569569
v = v0
@@ -574,12 +574,10 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
574574
* control flow node is guarded by a structurally equal expression having
575575
* abstract value `v`.
576576
*/
577-
predicate mustHaveValue(AbstractValue v) { exists(Expr e | e = this.getAGuard(e, v)) }
577+
predicate mustHaveValue(AbstractValue v) { g = this.getAGuard(g, v) }
578578
}
579579

580580
/**
581-
* DEPRECATED: Use `DataFlow::BarrierGuard` instead.
582-
*
583581
* A guarded data flow node. A guarded data flow node is like a guarded expression
584582
* (`GuardedExpr`), except control flow graph splitting is taken into account. That
585583
* is, one data flow node belonging to an expression may be guarded, while another
@@ -597,7 +595,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
597595
* In the example above, the node for `x.ToString()` is null-guarded in the
598596
* split `b == true`, but not in the split `b == false`.
599597
*/
600-
deprecated class GuardedDataFlowNode extends DataFlow::ExprNode {
598+
class GuardedDataFlowNode extends DataFlow::ExprNode {
601599
private Guard g;
602600

603601
private AccessOrCallExpr sub0;
@@ -621,7 +619,7 @@ deprecated class GuardedDataFlowNode extends DataFlow::ExprNode {
621619
* left-most qualifier, then so must the other (accessing the same SSA
622620
* variable).
623621
*/
624-
Expr getAGuard(Expr sub, AbstractValue v) {
622+
Guard getAGuard(Expr sub, AbstractValue v) {
625623
result = g and
626624
sub = sub0 and
627625
v = v0
@@ -632,20 +630,16 @@ deprecated class GuardedDataFlowNode extends DataFlow::ExprNode {
632630
* data flow node is guarded by a structurally equal expression having
633631
* abstract value `v`.
634632
*/
635-
predicate mustHaveValue(AbstractValue v) { exists(Expr e | e = this.getAGuard(e, v)) }
633+
predicate mustHaveValue(AbstractValue v) { g = this.getAGuard(g, v) }
636634
}
637635

638636
/** An expression guarded by a `null` check. */
639637
class NullGuardedExpr extends GuardedExpr {
640638
NullGuardedExpr() { this.mustHaveValue(any(NullValue v | not v.isNull())) }
641639
}
642640

643-
/**
644-
* DEPRECATED: Use `DataFlow::BarrierGuard` instead.
645-
*
646-
* A data flow node guarded by a `null` check.
647-
*/
648-
deprecated class NullGuardedDataFlowNode extends GuardedDataFlowNode {
641+
/** A data flow node guarded by a `null` check. */
642+
class NullGuardedDataFlowNode extends GuardedDataFlowNode {
649643
NullGuardedDataFlowNode() { this.mustHaveValue(any(NullValue v | not v.isNull())) }
650644
}
651645

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -190,31 +190,3 @@ class BarrierGuard extends Guard {
190190
)
191191
}
192192
}
193-
194-
module BarrierGuards {
195-
import AbstractValues
196-
197-
/** A simple guard that checks that this expression has an abstract value. */
198-
abstract class ValueBarrierGuard extends BarrierGuard {
199-
AbstractValue val;
200-
201-
ValueBarrierGuard() { this.controlsNode(_, this, val) }
202-
203-
/**
204-
* Gets the abstract value that this expression is checked against.
205-
*
206-
* For example, in
207-
*
208-
* ```
209-
* if (x == null)
210-
* ...
211-
* ```
212-
*
213-
* `x == null` is checked against an abstract Boolean value (`BooleanValue`),
214-
* and `x` is checked against an abstract nullness value (`NullValue`).
215-
*/
216-
AbstractValue getCheckedValue() { result = val }
217-
218-
final override predicate checks(Expr e, AbstractValue v) { e = this and v = val }
219-
}
220-
}

csharp/ql/src/semmle/code/csharp/security/dataflow/TaintedPath.qll

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ module TaintedPath {
2727
*/
2828
abstract class Sanitizer extends DataFlow::ExprNode { }
2929

30-
/**
31-
* A guard for uncontrolled data in path expression vulnerabilities.
32-
*/
33-
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
34-
3530
/**
3631
* A taint-tracking configuration for uncontrolled data in path expression vulnerabilities.
3732
*/
@@ -43,10 +38,6 @@ module TaintedPath {
4338
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
4439

4540
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
46-
47-
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
48-
guard instanceof SanitizerGuard
49-
}
5041
}
5142

5243
/** A source of remote user input. */
@@ -102,31 +93,39 @@ module TaintedPath {
10293
}
10394
}
10495

105-
class NullBarrierGuard extends DataFlow::BarrierGuards::ValueBarrierGuard {
106-
NullBarrierGuard() { val instanceof DataFlow::BarrierGuards::NullValue }
96+
/**
97+
* A weak guard that is insufficient to prevent path tampering.
98+
*/
99+
private class WeakGuard extends Guard {
100+
WeakGuard() {
101+
// None of these are sufficient to guarantee that a string is safe.
102+
exists(MethodCall mc, Method m | this = mc and mc.getTarget() = m |
103+
m.getName() = "StartsWith" or
104+
m.getName() = "EndsWith" or
105+
m.getName() = "IsNullOrEmpty" or
106+
m.getName() = "IsNullOrWhitespace" or
107+
m = any(SystemIOFileClass f).getAMethod("Exists") or
108+
m = any(SystemIODirectoryClass f).getAMethod("Exists")
109+
)
110+
or
111+
// Checking against `null` has no bearing on path traversal.
112+
this.controlsNode(_, _, any(AbstractValues::NullValue nv))
113+
}
107114
}
108115

109116
/**
110117
* A conditional involving the path, that is not considered to be a weak check.
111118
*
112119
* A weak check is one that is insufficient to prevent path tampering.
113120
*/
114-
class PathCheck extends SanitizerGuard {
121+
class PathCheck extends Sanitizer {
115122
PathCheck() {
116-
// None of these are sufficient to guarantee that a string is safe.
117-
not this.(MethodCall).getTarget() = any(Method m |
118-
m.getName() = "StartsWith" or
119-
m.getName() = "EndsWith" or
120-
m.getName() = "IsNullOrEmpty" or
121-
m.getName() = "IsNullOrWhitespace" or
122-
m = any(SystemIOFileClass f).getAMethod("Exists") or
123-
m = any(SystemIODirectoryClass f).getAMethod("Exists")
124-
) and
125-
// Checking against `null` has no bearing on path traversal.
126-
not this instanceof NullBarrierGuard
123+
// This expression is structurally replicated in a dominating guard which is not a "weak" check
124+
exists(Guard g, AbstractValues::BooleanValue v |
125+
g = this.(GuardedDataFlowNode).getAGuard(_, v) and
126+
not g instanceof WeakGuard
127+
)
127128
}
128-
129-
override predicate checks(Expr e, AbstractValue v) { this.controlsNode(_, e, v) }
130129
}
131130

132131
/**

csharp/ql/test/library-tests/dataflow/callablereturnsarg/Common.qll

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import csharp
2-
private import DataFlow::BarrierGuards
3-
4-
private class AntiNullBarrierGuard extends ValueBarrierGuard {
5-
AntiNullBarrierGuard() { val.(NullValue).isNull() }
6-
}
2+
private import semmle.code.csharp.controlflow.Guards
73

84
class Configuration extends DataFlow::Configuration {
95
Configuration() { this = "Configuration" }
@@ -12,8 +8,10 @@ class Configuration extends DataFlow::Configuration {
128

139
override predicate isSink(DataFlow::Node sink) { any() }
1410

15-
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
16-
guard instanceof AntiNullBarrierGuard
11+
override predicate isBarrier(DataFlow::Node node) {
12+
exists(AbstractValues::NullValue nv | node.(GuardedDataFlowNode).mustHaveValue(nv) |
13+
nv.isNull()
14+
)
1715
}
1816
}
1917

csharp/ql/test/library-tests/dataflow/callablereturnsarg/TaintTracking.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
1010

1111
override predicate isSink(DataFlow::Node sink) { c.isSink(sink) }
1212

13-
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { c.isBarrierGuard(guard) }
13+
override predicate isSanitizer(DataFlow::Node node) { c.isBarrier(node) }
1414
}
1515

1616
from TaintTrackingConfiguration c, Parameter p, int outRefArg

csharp/ql/test/library-tests/dataflow/local/Common.qll

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import csharp
2+
import semmle.code.csharp.controlflow.Guards
23
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
3-
private import DataFlow::BarrierGuards
4-
5-
private class NullBarrierGuard extends ValueBarrierGuard {
6-
NullBarrierGuard() { val = any(NullValue nv | not nv.isNull()) }
7-
}
84

95
class MyFlowSource extends DataFlow::Node {
106
MyFlowSource() {
@@ -23,7 +19,3 @@ class MyFlowSource extends DataFlow::Node {
2319
)
2420
}
2521
}
26-
27-
class MyNullGuardedDataFlowNode extends DataFlow::Node {
28-
MyNullGuardedDataFlowNode() { this = any(NullBarrierGuard ng).getAGuardedNode() }
29-
}

csharp/ql/test/library-tests/dataflow/local/DataFlow.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Common
33

44
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
55
DataFlow::localFlowStep(pred, succ) and
6-
not succ instanceof MyNullGuardedDataFlowNode
6+
not succ instanceof NullGuardedDataFlowNode
77
}
88

99
from MyFlowSource source, DataFlow::Node sink, Access target

csharp/ql/test/library-tests/dataflow/local/TaintTracking.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Common
33

44
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
55
TaintTracking::localTaintStep(pred, succ) and
6-
not succ instanceof MyNullGuardedDataFlowNode
6+
not succ instanceof NullGuardedDataFlowNode
77
}
88

99
from MyFlowSource source, DataFlow::Node sink, Access target

0 commit comments

Comments
 (0)