Skip to content

Commit 78380f5

Browse files
authored
Merge pull request #2658 from calumgrant/cs/serialization-check-bypass-type
C#: Fix cs/serialization-check-bypass
2 parents 5b7c150 + 803cb3f commit 78380f5

File tree

3 files changed

+48
-22
lines changed

3 files changed

+48
-22
lines changed

csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import semmle.code.csharp.serialization.Serialization
1313
import semmle.code.csharp.controlflow.Guards
14+
import semmle.code.csharp.dataflow.DataFlow
1415

1516
/**
1617
* The result is a write to the field `f`, assigning it the value
@@ -29,7 +30,11 @@ GuardedExpr checkedWrite(Field f, Variable v, IfStmt check) {
2930
Expr uncheckedWrite(Callable callable, Field f) {
3031
result = f.getAnAssignedValue() and
3132
result.getEnclosingCallable() = callable and
32-
not callable.calls*(checkedWrite(f, _, _).getEnclosingCallable())
33+
not callable.calls*(checkedWrite(f, _, _).getEnclosingCallable()) and
34+
// Exclude object creations because they were not deserialized
35+
not exists(Expr src | DataFlow::localExprFlow(src, result) |
36+
src instanceof ObjectCreation or src.hasValue()
37+
)
3338
}
3439

3540
from BinarySerializableType t, Field f, IfStmt check, Expr write, Expr unsafeWrite

csharp/ql/test/query-tests/Security Features/CWE-020/RuntimeChecksBypass.cs

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ public Test1(string v)
1010
{
1111
if (v == "valid")
1212
{
13-
f = v /* safe write */;
13+
f = v; // GOOD
1414
}
1515
}
1616

1717
[OnDeserializing]
1818
public void Deserialize()
1919
{
20-
f = "invalid" /* unsafe write */;
20+
f = $"invalid"; // BAD
2121
}
2222
}
2323

@@ -30,19 +30,19 @@ public Test2(string v)
3030
{
3131
if (v == "valid")
3232
{
33-
f = v /* safe write */;
33+
f = v; // GOOD
3434
}
3535
}
3636

3737
[OnDeserializing]
3838
public void Deserialize()
3939
{
40-
var v = "invalid";
41-
f = v /* unsafe write -- false negative */;
40+
var v = $"invalid";
41+
f = v; // BAD: False negative
4242

4343
if (v == "valid")
4444
{
45-
f = v; /* safe write */
45+
f = v; // GOOD
4646
}
4747
}
4848
}
@@ -56,25 +56,25 @@ public Test3(string v)
5656
{
5757
if (v == "valid")
5858
{
59-
f = v /* safe write */;
59+
f = v; // GOOD
6060
}
6161
}
6262

6363
[OnDeserializing]
6464
public void Deserialize()
6565
{
66-
var v = "invalid";
67-
f = v /* unsafe write -- false negative */;
66+
var v = $"invalid";
67+
f = v; // GOOD: False negative
6868
Assign(v);
6969
}
7070

7171
private void Assign(string v)
7272
{
73-
f = v /* unsafe write -- false negative */;
73+
f = v; // GOOD: False negative
7474

7575
if (v == "valid")
7676
{
77-
f = v /* safe write */;
77+
f = v; // GOOD
7878
}
7979
}
8080
}
@@ -88,21 +88,21 @@ public Test4(string v)
8888
{
8989
if (v == "valid")
9090
{
91-
f = v /* safe write */;
91+
f = v; // GOOD
9292
}
9393
}
9494

9595
[OnDeserializing]
9696
public void Deserialize()
9797
{
98-
var v = "invalid";
98+
var v = $"invalid";
9999
if (v == "valid")
100100
Assign(v);
101101
}
102102

103103
private void Assign(string v)
104104
{
105-
f = v /* safe write */;
105+
f = v; // GOOD
106106
}
107107
}
108108

@@ -115,13 +115,13 @@ public Test5(int age)
115115
{
116116
if (age < 0)
117117
throw new ArgumentException(nameof(age));
118-
Age = age /* safe write */;
118+
Age = age; // GOOD
119119
}
120120

121121
[OnDeserializing]
122122
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
123123
{
124-
Age = info.GetInt32("age"); /* unsafe write */;
124+
Age = info.GetInt32("age"); // BAD
125125
}
126126
}
127127

@@ -134,7 +134,7 @@ public Test6(int age)
134134
{
135135
if (age < 0)
136136
throw new ArgumentException(nameof(age));
137-
Age = age /* safe write */;
137+
Age = age; // GOOD
138138
}
139139

140140
[OnDeserializing]
@@ -143,7 +143,7 @@ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext contex
143143
int age = info.GetInt32("age");
144144
if (age < 0)
145145
throw new SerializationException("age");
146-
Age = age; /* safe write */;
146+
Age = age; // GOOD
147147
}
148148
}
149149

@@ -156,7 +156,7 @@ public Test7(int age)
156156
{
157157
if (age < 0)
158158
throw new ArgumentException(nameof(age));
159-
Age = age /* safe write */;
159+
Age = age; // GOOD
160160
}
161161

162162
[OnDeserializing]
@@ -165,6 +165,27 @@ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext contex
165165
int age = info.GetInt32("age");
166166
if (false)
167167
throw new SerializationException("age");
168-
Age = age; /* unsafe write */;
168+
Age = age; // BAD
169+
}
170+
}
171+
172+
[Serializable]
173+
public class Test8 : ISerializable
174+
{
175+
string Options;
176+
177+
public int Age;
178+
179+
public Test8(string options)
180+
{
181+
if (options == null)
182+
throw new ArgumentNullException(nameof(options));
183+
Options = options; // GOOD
184+
}
185+
186+
[OnDeserializing]
187+
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
188+
{
189+
Options = new string(""); // GOOD: A created object
169190
}
170191
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
| RuntimeChecksBypass.cs:20:13:20:21 | "invalid" | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:7:19:7:19 | f | f | RuntimeChecksBypass.cs:11:9:14:9 | if (...) ... | check |
1+
| RuntimeChecksBypass.cs:20:13:20:22 | $"..." | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:7:19:7:19 | f | f | RuntimeChecksBypass.cs:11:9:14:9 | if (...) ... | check |
22
| RuntimeChecksBypass.cs:124:15:124:34 | call to method GetInt32 | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:112:16:112:18 | Age | Age | RuntimeChecksBypass.cs:116:9:117:53 | if (...) ... | check |
33
| RuntimeChecksBypass.cs:168:15:168:17 | access to local variable age | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:153:16:153:18 | Age | Age | RuntimeChecksBypass.cs:157:9:158:53 | if (...) ... | check |
44
| RuntimeChecksBypassBad.cs:19:15:19:34 | call to method GetInt32 | This write to $@ may be circumventing a $@. | RuntimeChecksBypassBad.cs:7:16:7:18 | Age | Age | RuntimeChecksBypassBad.cs:11:9:12:53 | if (...) ... | check |

0 commit comments

Comments
 (0)