Skip to content

Commit a7a5eaa

Browse files
author
AndreiDiaconu1
committed
Address PR comments
1 parent d6e4a2a commit a7a5eaa

File tree

7 files changed

+116
-106
lines changed

7 files changed

+116
-106
lines changed

csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeAnalysis.qll

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,19 +233,25 @@ private class SafeCastInstruction extends ConvertInstruction {
233233
* Holds if `typ` is a small integral type with the given lower and upper bounds.
234234
*/
235235
private predicate typeBound(IntegralType typ, int lowerbound, int upperbound) {
236-
typ instanceof SignedIntegralType and typ.getSize() = 1 and lowerbound = -128 and upperbound = 127
236+
typ instanceof SignedIntegralType and
237+
typ.getSize() = 1 and
238+
lowerbound = typ.minValue() and
239+
upperbound = typ.maxValue()
237240
or
238-
typ instanceof UnsignedIntegralType and typ.getSize() = 1 and lowerbound = 0 and upperbound = 255
241+
typ instanceof UnsignedIntegralType and
242+
typ.getSize() = 1 and
243+
lowerbound = typ.minValue() and
244+
upperbound = typ.maxValue()
239245
or
240246
typ instanceof SignedIntegralType and
241247
typ.getSize() = 2 and
242-
lowerbound = -32768 and
243-
upperbound = 32767
248+
lowerbound = typ.minValue() and
249+
upperbound = typ.maxValue()
244250
or
245251
typ instanceof UnsignedIntegralType and
246252
typ.getSize() = 2 and
247-
lowerbound = 0 and
248-
upperbound = 65535
253+
lowerbound = typ.minValue() and
254+
upperbound = typ.maxValue()
249255
}
250256

251257
/**

csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeUtils.qll

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,20 @@ IntValue getConstantValue(Instruction instr) {
4040
)
4141
}
4242

43+
/**
44+
* Gets the dimension of the array (either the declared size, or the
45+
* size of the initializer); if no size is declared and no initializer used,
46+
* the predicate does not hold.
47+
*/
4348
IntValue getArrayDim(Variable arr) {
4449
exists(ArrayCreation ac |
4550
arr.getInitializer() = ac and
46-
result = ac.getInitializer().getNumberOfElements()
51+
if exists(ac.getLengthArgument(0))
52+
then result = ac.getLengthArgument(0).getValue().toInt()
53+
else
54+
if exists(ac.getInitializer())
55+
then result = ac.getInitializer().getNumberOfElements()
56+
else none()
4757
)
4858
}
4959

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
| test.cs:10:17:10:21 | test1 | test.cs:17:41:17:51 | access to array element | This array access is ok, the index is at most the lenth of the array + -1 |
2-
| test.cs:21:17:21:21 | test2 | test.cs:33:41:33:51 | access to array element | This array access might be out of bounds, as the index might be equal to the array length |
3-
| test.cs:37:17:37:21 | test3 | test.cs:50:41:50:51 | access to array element | This array access is ok, the index is at most the lenth of the array + -1 |
4-
| test.cs:54:17:54:21 | test4 | test.cs:64:41:64:51 | access to array element | This array access might be out of bounds, as the index might be equal to the array length |
5-
| test.cs:68:17:68:21 | test5 | test.cs:74:22:74:27 | access to indexer | This array access might be out of bounds, as the index might be equal to the array length |
6-
| test.cs:78:17:78:21 | test6 | test.cs:87:41:87:55 | access to array element | This array access might be out of bounds, as the index might be equal to the array length |
7-
| test.cs:91:17:91:21 | test7 | test.cs:101:41:101:50 | access to array element | This array access might be out of bounds, as the index might be equal to the array length + 1 |
1+
| test.cs:21:17:21:21 | Test2 | test.cs:34:41:34:51 | access to array element | This array access might be out of bounds, as the index might be equal to the array length |
2+
| test.cs:56:17:56:21 | Test4 | test.cs:67:41:67:51 | access to array element | This array access might be out of bounds, as the index might be equal to the array length |
3+
| test.cs:71:17:71:21 | Test5 | test.cs:77:22:77:27 | access to indexer | This array access might be out of bounds, as the index might be equal to the array length |
4+
| test.cs:81:17:81:21 | Test6 | test.cs:90:41:90:55 | access to array element | This array access might be out of bounds, as the index might be equal to the array length |
5+
| test.cs:94:17:94:21 | Test7 | test.cs:104:41:104:50 | access to array element | This array access might be out of bounds, as the index might be equal to the array length + 1 |

csharp/ql/test/library-tests/ir/offbyone/OffByOneRA.ql

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import csharp
22
import semmle.code.csharp.ir.IR
33
import semmle.code.csharp.ir.rangeanalysis.RangeAnalysis
4+
import semmle.code.csharp.ir.rangeanalysis.RangeUtils
45

56
/**
67
* Holds if the index expression of `aa` is less than or equal to the array length plus `k`.
@@ -27,19 +28,8 @@ predicate boundedArrayAccess(ElementAccess aa, int k) {
2728
.getTarget()
2829
)
2930
or
30-
exists(ArrayCreation ac |
31-
ac.getParent().(LocalVariableDeclExpr).getVariable() = aa
32-
.getQualifier()
33-
.(VariableAccess)
34-
.getTarget() and
35-
b instanceof ZeroBound and
36-
if exists(ac.getLengthArgument(0))
37-
then k = delta - ac.getLengthArgument(0).getValue().toInt()
38-
else
39-
if exists(ac.getInitializer())
40-
then k = delta - ac.getInitializer().getNumberOfElements()
41-
else none()
42-
)
31+
b instanceof ZeroBound and
32+
k = delta - getArrayDim(aa.getQualifier().(VariableAccess).getTarget())
4333
)
4434
}
4535

@@ -54,12 +44,8 @@ predicate bestArrayAccessBound(ElementAccess aa, int k) {
5444
from ElementAccess aa, int k, string msg, string add
5545
where
5646
bestArrayAccessBound(aa, k) and
47+
k >= 0 and
5748
(if k = 0 then add = "" else add = " + " + k) and
58-
(
59-
if k >= 0
60-
then
61-
msg = "This array access might be out of bounds, as the index might be equal to the array length"
62-
+ add
63-
else msg = "This array access is ok, the index is at most the lenth of the array " + add
64-
)
49+
msg = "This array access might be out of bounds, as the index might be equal to the array length" +
50+
add
6551
select aa.getEnclosingCallable(), aa, msg

csharp/ql/test/library-tests/ir/offbyone/test.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,23 @@ class ContainerLengthOffByOne
33
public int[] arr;
44
public string str;
55

6-
public static void fun(int elem)
6+
public static void Fun(int elem)
77
{
88
}
99

10-
public void test1()
10+
public void Test1()
1111
{
1212
int len1 = this.arr.Length;
1313
int len2 = len1 + 1;
1414
// OK
1515
for(int i = 0; i < len2 - 1; i++)
1616
{
17-
ContainerLengthOffByOne.fun(this.arr[i]);
17+
ContainerLengthOffByOne.Fun(this.arr[i]);
1818
}
1919
}
2020

21-
public void test2() {
21+
public void Test2()
22+
{
2223
int len1 = this.arr.Length;
2324

2425
int len2;
@@ -30,11 +31,12 @@ public void test2() {
3031
// exceeds the size of the array.
3132
for(int i = 0; i < len2; i++)
3233
{
33-
ContainerLengthOffByOne.fun(this.arr[i]);
34+
ContainerLengthOffByOne.Fun(this.arr[i]);
3435
}
3536
}
3637

37-
public void test3() {
38+
public void Test3()
39+
{
3840
int len1 = this.arr.Length;
3941
int len2 = len1 - 1;
4042

@@ -47,11 +49,12 @@ public void test3() {
4749
// we don't get an off by one error.
4850
for(int i = 0; i < len3; i++)
4951
{
50-
ContainerLengthOffByOne.fun(this.arr[i]);
52+
ContainerLengthOffByOne.Fun(this.arr[i]);
5153
}
5254
}
5355

54-
public void test4() {
56+
public void Test4()
57+
{
5558
int len1 = this.arr.Length;
5659

5760
int len2 = len1 + 1;
@@ -61,11 +64,11 @@ public void test4() {
6164
// Not OK, len5 is off by one.
6265
for(int i = 0; i < len5; i++)
6366
{
64-
ContainerLengthOffByOne.fun(this.arr[i]);
67+
ContainerLengthOffByOne.Fun(this.arr[i]);
6568
}
6669
}
6770

68-
public void test5()
71+
public void Test5()
6972
{
7073
int len = this.str.Length;
7174
// Not OK; test for indexers
@@ -75,20 +78,20 @@ public void test5()
7578
}
7679
}
7780

78-
public void test6()
81+
public void Test6()
7982
{
8083
int len = this.arr.Length - 2;
8184
int len1 = len + 3;
8285
int len2 = len1 - 1;
83-
// Not OK, of by one
86+
// Not OK, off by one
8487
// The test shows that more complex expressions are treated correctly
8588
for (int i = 0; i < len2; i++)
8689
{
87-
ContainerLengthOffByOne.fun(this.arr[i + 1]);
90+
ContainerLengthOffByOne.Fun(this.arr[i + 1]);
8891
}
8992
}
9093

91-
public void test7()
94+
public void Test7()
9295
{
9396
int[] arrInit = { 1, 2, 3 };
9497
int len = (arrInit.Length * 2 + 2) / 2 * 2;
@@ -98,7 +101,7 @@ public void test7()
98101
// are used in bounds
99102
for (int i = 0; i < len1; i++)
100103
{
101-
ContainerLengthOffByOne.fun(arrInit[i]);
104+
ContainerLengthOffByOne.Fun(arrInit[i]);
102105
}
103106
}
104107
}
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
| test.cs:19:12:19:12 | Store: access to parameter x | test.cs:15:24:15:24 | InitializeParameter: x | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
2-
| test.cs:19:12:19:12 | Store: access to parameter x | test.cs:15:31:15:31 | InitializeParameter: y | 0 | false | CompareLT: ... < ... | test.cs:16:9:16:13 | test.cs:16:9:16:13 |
3-
| test.cs:19:12:19:12 | Store: access to parameter x | test.cs:15:31:15:31 | InitializeParameter: y | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
4-
| test.cs:29:12:29:12 | Store: access to parameter x | test.cs:23:24:23:24 | InitializeParameter: x | -2 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
5-
| test.cs:29:12:29:12 | Store: access to parameter x | test.cs:23:31:23:31 | InitializeParameter: y | -2 | false | CompareLT: ... < ... | test.cs:24:9:24:13 | test.cs:24:9:24:13 |
6-
| test.cs:36:12:36:12 | Load: access to local variable i | file://:0:0:0:0 | 0 | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
7-
| test.cs:36:12:36:12 | Load: access to local variable i | test.cs:33:25:33:25 | InitializeParameter: x | -1 | true | CompareLT: ... < ... | test.cs:35:20:35:24 | test.cs:35:20:35:24 |
8-
| test.cs:39:12:39:12 | Load: access to local variable i | file://:0:0:0:0 | 0 | 1 | false | CompareGT: ... > ... | test.cs:38:20:38:24 | test.cs:38:20:38:24 |
9-
| test.cs:39:12:39:12 | Load: access to local variable i | test.cs:33:25:33:25 | InitializeParameter: x | 0 | true | NoReason | file://:0:0:0:0 | :0:0:0:0 |
10-
| test.cs:39:12:39:12 | Load: access to local variable i | test.cs:35:20:35:20 | Phi: access to local variable i | 0 | true | CompareLT: ... < ... | test.cs:35:20:35:24 | test.cs:35:20:35:24 |
11-
| test.cs:42:12:42:12 | Load: access to local variable i | file://:0:0:0:0 | 0 | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
12-
| test.cs:42:12:42:12 | Load: access to local variable i | test.cs:33:25:33:25 | InitializeParameter: x | 1 | true | CompareLT: ... < ... | test.cs:41:20:41:28 | test.cs:41:20:41:28 |
13-
| test.cs:42:12:42:12 | Load: access to local variable i | test.cs:35:20:35:20 | Phi: access to local variable i | 1 | true | CompareLT: ... < ... | test.cs:41:20:41:28 | test.cs:41:20:41:28 |
14-
| test.cs:42:12:42:12 | Load: access to local variable i | test.cs:38:20:38:20 | Phi: access to local variable i | 0 | false | CompareGT: ... > ... | test.cs:38:20:38:24 | test.cs:38:20:38:24 |
15-
| test.cs:49:13:49:17 | Load: access to parameter begin | test.cs:47:33:47:37 | InitializeParameter: begin | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
16-
| test.cs:58:14:58:14 | Load: access to parameter x | test.cs:55:32:55:32 | InitializeParameter: y | -1 | true | CompareLT: ... < ... | test.cs:57:11:57:15 | test.cs:57:11:57:15 |
17-
| test.cs:58:14:58:14 | Load: access to parameter x | test.cs:55:39:55:39 | InitializeParameter: z | -2 | true | CompareLT: ... < ... | test.cs:57:11:57:15 | test.cs:57:11:57:15 |
18-
| test.cs:63:14:63:14 | Load: access to parameter x | test.cs:55:32:55:32 | InitializeParameter: y | -1 | true | CompareLT: ... < ... | test.cs:61:9:61:13 | test.cs:61:9:61:13 |
1+
| test.cs:22:12:22:12 | Store: access to parameter x | test.cs:16:24:16:24 | InitializeParameter: x | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
2+
| test.cs:22:12:22:12 | Store: access to parameter x | test.cs:16:31:16:31 | InitializeParameter: y | 0 | false | CompareLT: ... < ... | test.cs:18:9:18:13 | test.cs:18:9:18:13 |
3+
| test.cs:22:12:22:12 | Store: access to parameter x | test.cs:16:31:16:31 | InitializeParameter: y | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
4+
| test.cs:36:12:36:12 | Store: access to parameter x | test.cs:26:24:26:24 | InitializeParameter: x | -2 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
5+
| test.cs:36:12:36:12 | Store: access to parameter x | test.cs:26:31:26:31 | InitializeParameter: y | -2 | false | CompareLT: ... < ... | test.cs:28:9:28:13 | test.cs:28:9:28:13 |
6+
| test.cs:45:12:45:12 | Load: access to local variable i | file://:0:0:0:0 | 0 | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
7+
| test.cs:45:12:45:12 | Load: access to local variable i | test.cs:40:25:40:25 | InitializeParameter: x | -1 | true | CompareLT: ... < ... | test.cs:43:20:43:24 | test.cs:43:20:43:24 |
8+
| test.cs:49:12:49:12 | Load: access to local variable i | file://:0:0:0:0 | 0 | 1 | false | CompareGT: ... > ... | test.cs:47:20:47:24 | test.cs:47:20:47:24 |
9+
| test.cs:49:12:49:12 | Load: access to local variable i | test.cs:40:25:40:25 | InitializeParameter: x | 0 | true | NoReason | file://:0:0:0:0 | :0:0:0:0 |
10+
| test.cs:49:12:49:12 | Load: access to local variable i | test.cs:43:20:43:20 | Phi: access to local variable i | 0 | true | CompareLT: ... < ... | test.cs:43:20:43:24 | test.cs:43:20:43:24 |
11+
| test.cs:53:12:53:12 | Load: access to local variable i | file://:0:0:0:0 | 0 | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
12+
| test.cs:53:12:53:12 | Load: access to local variable i | test.cs:40:25:40:25 | InitializeParameter: x | 1 | true | CompareLT: ... < ... | test.cs:51:20:51:28 | test.cs:51:20:51:28 |
13+
| test.cs:53:12:53:12 | Load: access to local variable i | test.cs:43:20:43:20 | Phi: access to local variable i | 1 | true | CompareLT: ... < ... | test.cs:51:20:51:28 | test.cs:51:20:51:28 |
14+
| test.cs:53:12:53:12 | Load: access to local variable i | test.cs:47:20:47:20 | Phi: access to local variable i | 0 | false | CompareGT: ... > ... | test.cs:47:20:47:24 | test.cs:47:20:47:24 |
15+
| test.cs:62:13:62:17 | Load: access to parameter begin | test.cs:58:33:58:37 | InitializeParameter: begin | 0 | false | NoReason | file://:0:0:0:0 | :0:0:0:0 |
16+
| test.cs:74:14:74:14 | Load: access to parameter x | test.cs:68:32:68:32 | InitializeParameter: y | -1 | true | CompareLT: ... < ... | test.cs:72:11:72:15 | test.cs:72:11:72:15 |
17+
| test.cs:74:14:74:14 | Load: access to parameter x | test.cs:68:39:68:39 | InitializeParameter: z | -2 | true | CompareLT: ... < ... | test.cs:72:11:72:15 | test.cs:72:11:72:15 |
18+
| test.cs:81:14:81:14 | Load: access to parameter x | test.cs:68:32:68:32 | InitializeParameter: y | -1 | true | CompareLT: ... < ... | test.cs:77:9:77:13 | test.cs:77:9:77:13 |
Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,85 @@
11
class RangeAnalysis {
2-
static void sink(int val) {
3-
2+
static void Sink(int val)
3+
{
44
}
55

6-
static unsafe void sinkp(int* p) {
7-
6+
static unsafe void Sinkp(int* p)
7+
{
88
}
99

10-
static int source() {
10+
static int Source()
11+
{
1112
return 0;
1213
}
1314

1415
// Guards, inference, critical edges
15-
static int test1(int x, int y) {
16-
if (x < y) {
16+
static int Test1(int x, int y)
17+
{
18+
if (x < y)
19+
{
1720
x = y;
1821
}
1922
return x;
2023
}
2124

2225
// Bounds mergers at phi nodes
23-
static int test2(int x, int y) {
24-
if (x < y) {
26+
static int Test2(int x, int y)
27+
{
28+
if (x < y)
29+
{
2530
x = y;
26-
} else {
27-
x = x-2;
31+
}
32+
else
33+
{
34+
x = x - 2;
2835
}
2936
return x;
3037
}
3138

3239
// for loops
33-
static void test3(int x) {
40+
static void Test3(int x)
41+
{
3442
int y = x;
35-
for(int i = 0; i < x; i++) {
36-
sink(i);
43+
for(int i = 0; i < x; i++)
44+
{
45+
Sink(i);
3746
}
38-
for(int i = y; i > 0; i--) {
39-
sink(i);
47+
for(int i = y; i > 0; i--)
48+
{
49+
Sink(i);
4050
}
41-
for(int i = 0; i < y + 2; i++) {
42-
sink(i);
51+
for(int i = 0; i < y + 2; i++)
52+
{
53+
Sink(i);
4354
}
4455
}
4556

4657
// pointer bounds
47-
unsafe static void test4(int *begin, int *end) {
48-
while (begin < end) {
49-
sinkp(begin);
58+
unsafe static void Test4(int *begin, int *end)
59+
{
60+
while (begin < end)
61+
{
62+
Sinkp(begin);
5063
begin++;
5164
}
5265
}
5366

5467
// bound propagation through conditionals
55-
static void test5(int x, int y, int z) {
56-
if (y < z) {
57-
if (x < y) {
58-
sink(x);
68+
static void Test5(int x, int y, int z)
69+
{
70+
if (y < z)
71+
{
72+
if (x < y)
73+
{
74+
Sink(x);
5975
}
6076
}
61-
if (x < y) {
62-
if (y < z) {
63-
sink(x); // x < z is not inferred here
77+
if (x < y)
78+
{
79+
if (y < z)
80+
{
81+
Sink(x); // x < z is not inferred here
6482
}
6583
}
6684
}
67-
68-
unsafe static void addone(int[] arr)
69-
{
70-
int length = arr.Length;
71-
fixed (int* b = arr)
72-
{
73-
int *p = b;
74-
for(int i = 0; i <= length; i++)
75-
*p++ += 1;
76-
}
77-
}
7885
}

0 commit comments

Comments
 (0)