Skip to content

Commit 5f8a6f7

Browse files
author
Nikita Kraiouchkine
committed
Implement OutOfBounds.qll, ARR38-C, and ARR30-C
1 parent dfda651 commit 5f8a6f7

9 files changed

+593
-123
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ARR30-C: Do not form or use out-of-bounds pointers or array subscripts
2+
3+
This query implements the CERT-C rule ARR30-C:
4+
5+
> Do not form or use out-of-bounds pointers or array subscripts
6+
## CERT
7+
8+
** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` **
9+
10+
## Implementation notes
11+
12+
None
13+
14+
## References
15+
16+
* CERT-C: [ARR30-C: Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @id c/cert/do-not-form-out-of-bounds-pointers-or-array-subscripts
3+
* @name ARR30-C: Do not form or use out-of-bounds pointers or array subscripts
4+
* @description Forming or using an out-of-bounds pointer is undefined behavior and can result in
5+
* invalid memory accesses.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/arr30-c
10+
* correctness
11+
* security
12+
* external/cert/obligation/rule
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.cert
17+
import codingstandards.c.OutOfBounds
18+
19+
from
20+
OOB::BufferAccess ba, Expr bufferArg, Expr sizeArg, OOB::PointerToObjectSource bufferSource,
21+
string message
22+
where
23+
not isExcluded(ba, OutOfBoundsPackage::doNotFormOutOfBoundsPointersOrArraySubscriptsQuery()) and
24+
(
25+
exists(int sizeArgValue, int bufferArgSize |
26+
OOB::isSizeArgGreaterThanBufferSize(bufferArg, sizeArg, bufferSource, bufferArgSize, sizeArgValue, ba) and
27+
message =
28+
"Buffer accesses offset " + sizeArgValue +
29+
" which is greater than the fixed size " + bufferArgSize + " of the $@."
30+
)
31+
or
32+
exists(int sizeArgUpperBound, int sizeMult, int bufferArgSize |
33+
OOB::isSizeArgNotCheckedLessThanFixedBufferSize(bufferArg, sizeArg, bufferSource,
34+
bufferArgSize, ba, sizeArgUpperBound, sizeMult) and
35+
message =
36+
"Buffer accesses may access up to offset " + sizeArgUpperBound + "*" + sizeMult +
37+
" which is greater than the fixed size " + bufferArgSize + " of the $@."
38+
)
39+
or
40+
OOB::isSizeArgNotCheckedGreaterThanZero(bufferArg, sizeArg, bufferSource, ba) and
41+
message = "Buffer access may be to a negative index in the buffer."
42+
)
43+
select ba, message, bufferSource, "buffer"

Diff for: c/cert/src/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.ql

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @id c/cert/library-function-argument-out-of-bounds
33
* @name ARR38-C: Guarantee that library functions do not form invalid pointers
4-
* @description
4+
* @description
55
* @kind problem
66
* @precision high
77
* @problem.severity error
@@ -21,4 +21,4 @@ from
2121
where
2222
not isExcluded(fc, OutOfBoundsPackage::libraryFunctionArgumentOutOfBoundsQuery()) and
2323
OOB::problems(fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr)
24-
select fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr
24+
select fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| test.c:8:3:8:11 | ... + ... | Buffer accesses offset 404 which is greater than the fixed size 400 of the $@. | test.c:8:3:8:5 | arr | buffer |
2+
| test.c:16:3:16:13 | ... + ... | Buffer access may be to a negative index in the buffer. | test.c:16:3:16:5 | arr | buffer |
3+
| test.c:21:5:21:15 | ... + ... | Buffer access may be to a negative index in the buffer. | test.c:21:5:21:7 | arr | buffer |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.ql

Diff for: c/cert/test/rules/ARR30-C/test.c

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
3+
enum { ARRAY_SIZE = 100 };
4+
5+
static int arr[ARRAY_SIZE];
6+
7+
void test_fixed_wrong() {
8+
arr + 101; // NON_COMPLIANT
9+
}
10+
11+
void test_fixed_right() {
12+
arr + 2; // COMPLIANT
13+
}
14+
15+
void test_no_check(int index) {
16+
arr + index; // NON_COMPLIANT
17+
}
18+
19+
void test_invalid_check(int index) {
20+
if (index < ARRAY_SIZE) {
21+
arr + index; // NON_COMPLIANT - `index` could be negative
22+
}
23+
}
24+
25+
void test_valid_check(int index) {
26+
if (index > 0 && index < ARRAY_SIZE) {
27+
arr + index; // COMPLIANT - `index` cannot be negative
28+
}
29+
}
30+
31+
void test_valid_check_by_type(unsigned int index) {
32+
if (index < ARRAY_SIZE) {
33+
arr + index; // COMPLIANT - `index` cannot be be negative
34+
}
35+
}

Diff for: c/cert/test/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.expected

+79-1
Large diffs are not rendered by default.

Diff for: c/cert/test/rules/ARR38-C/test.c

+40-30
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
#include <stdio.h>
22
#include <stdlib.h>
33
#include <string.h>
4+
#include <time.h>
45
#include <wchar.h>
56

6-
char *get_ca_5(void) { return malloc(5 * sizeof(char)); }
7-
8-
char *get_ca_5_zeroed(void) {
9-
char *p = malloc(5 * sizeof(char));
10-
memset(p, 0, 5 * sizeof(char));
7+
char *get_ca_5(void) {
8+
void *ptr = malloc(5 * sizeof(char));
9+
memset(ptr, 0, 5 * sizeof(char));
10+
return (char *)ptr;
1111
}
1212

13-
int compare(void *, void *) {}
13+
int compare(void *a, void *b) {}
14+
15+
void test_strings_loop(void) {
16+
char ca5[5] = "test"; // ok
17+
char buf5[5] = {0};
18+
19+
for (int i = 0; i < 5; i++) {
20+
strcpy(buf5, ca5); // COMPLIANT
21+
strcpy(buf5 + i, ca5); // NON_COMPLIANT[FALSE_NEGATIVE]
22+
strncpy(buf5, ca5, i); // COMPLIANT
23+
strncpy(buf5, ca5, i + 1); // NON_COMPLIANT[FALSE_NEGATIVE]
24+
}
25+
}
1426

1527
void test_strings(int flow, int unk_size) {
1628
char ca5_good[5] = "test"; // ok
@@ -95,7 +107,7 @@ void test_strings(int flow, int unk_size) {
95107
} // COMPLIANT
96108
if (flow) {
97109
strncpy(ca6_bad, ca5_good, 6);
98-
} // NON_COMPLIANT[FALSE_POSITIVE]
110+
} // COMPLIANT
99111
if (flow) {
100112
strncpy(ca5_good + 1, ca5_good + 2, 3);
101113
} // COMPLIANT
@@ -132,7 +144,7 @@ void test_strings(int flow, int unk_size) {
132144
char buf3[10] = {'\0'};
133145
char buf4[10] = "12345";
134146

135-
strcat(buf0, " "); // COMPLIANT[FALSE_NEGATIVE] - not null terminated at
147+
strcat(buf0, " "); // NON_COMPLIANT[FALSE_NEGATIVE] - not null terminated at
136148
// initialization
137149

138150
memset(buf0, 0, sizeof(buf0)); // COMPLIANT
@@ -156,25 +168,22 @@ void test_strings(int flow, int unk_size) {
156168
wchar_t buf3[10] = {L'\0'};
157169
wchar_t buf4[10] = L"12345";
158170

159-
wcsncat(
160-
buf0, L" ",
161-
1); // COMPLIANT[FALSE_NEGATIVE] - not null terminated at initialization
171+
wcsncat(buf0, L" ",
172+
1); // NON_COMPLIANT[FALSE_NEGATIVE] - not null terminated at
173+
// initialization
162174

163175
memset(buf0, 0, sizeof(buf0)); // COMPLIANT
164176
memset(buf2, 0, sizeof(buf2)); // COMPLIANT
165177

166178
wcsncat(buf1, L" ", 1); // NON_COMPLIANT - not null terminated
167179
wcsncat(buf2, L" ", 1); // COMPLIANT
168180
wcsncat(buf3, L" ", 1); // COMPLIANT
169-
wcsncat(buf4, L"12345", 5); // NON_COMPLIANT
170-
171-
wcsncat(get_ca_5_zeroed(), L"12345", 5); // NON_COMPLIANT
172-
wcsncat(get_ca_5_zeroed(), L"1234", 4); // NON_COMPLIANT
173-
wcsncat(get_ca_5_zeroed() + 1, L"1234", 4); // NON_COMPLIANT
174-
wcsncat(get_ca_5_zeroed(), L"12",
175-
2); // NON_COMPLIANT - 4 (bytes) + 2 (null term) copied
176-
wcsncat(get_ca_5_zeroed() + 1, L"1",
177-
1); // COMPLIANT - 2 (bytes) + 2 (null term) copied
181+
wcsncat(buf4, L"12345", 5); // NON_COMPLIANT[FALSE_NEGATIVE]
182+
183+
wcsncat(get_ca_5(), L"12345", 5); // NON_COMPLIANT
184+
wcsncat(get_ca_5(), L"1234", 4); // NON_COMPLIANT
185+
wcsncat(get_ca_5() + 1, L"1234", 4); // NON_COMPLIANT
186+
wcsncat(get_ca_5(), L"12", 2); // NON_COMPLIANT
178187
}
179188

180189
// strcmp
@@ -189,7 +198,8 @@ void test_strings(int flow, int unk_size) {
189198
// strncmp
190199
if (flow) {
191200
strncmp(ca5_good, ca5_bad, 4); // COMPLIANT
192-
strncmp(ca5_good, ca5_bad, 5); // NON_COMPLIANT
201+
strncmp(ca5_good, ca5_bad, 5); // COMPLIANT
202+
strncmp(ca5_good, ca5_bad, 6); // NON_COMPLIANT
193203
}
194204
}
195205

@@ -201,7 +211,7 @@ void test_wrong_buf_size(void) {
201211
fgets(buf, sizeof(buf), stdin); // COMPLIANT
202212
fgets(buf, sizeof(buf) - 1, stdin); // COMPLIANT
203213
fgets(buf, sizeof(buf) + 1, stdin); // NON_COMPLIANT
204-
fgets(buf, 0, stdin); // NON_COMPLIANT
214+
fgets(buf, 0, stdin); // COMPLIANT
205215
fgets(buf + 1, sizeof(buf) - 1, stdin); // COMPLIANT
206216
fgets(buf + 1, sizeof(buf), stdin); // NON_COMPLIANT
207217
}
@@ -213,8 +223,7 @@ void test_wrong_buf_size(void) {
213223
fgetws(wbuf, sizeof(wbuf) / sizeof(*wbuf), stdin); // COMPLIANT
214224
fgetws(wbuf, sizeof(wbuf) / sizeof(*wbuf) - 1, stdin); // COMPLIANT
215225
fgetws(wbuf, sizeof(wbuf) / sizeof(*wbuf) + 1, stdin); // NON_COMPLIANT
216-
fgetws(wbuf, 0, stdin); // NON_COMPLIANT
217-
fgetws(wbuf + 1, sizeof(wbuf) / sizeof(*wbuf) - 1, stdin); // NON_COMPLIANT
226+
fgetws(wbuf, 0, stdin); // COMPLIANT
218227
fgetws(wbuf + 1, sizeof(wbuf) / sizeof(*wbuf) - 2, stdin); // COMPLIANT
219228
fgetws(wbuf + 1, sizeof(wbuf) / sizeof(*wbuf), stdin); // NON_COMPLIANT
220229
}
@@ -246,7 +255,7 @@ void test_wrong_buf_size(void) {
246255
// mbtowc
247256
{
248257
wchar_t c;
249-
char buf[2];
258+
char buf[2] = {0};
250259
mbtowc(&c, buf, sizeof(buf)); // COMPLIANT
251260
mbtowc(&c, buf, sizeof(buf) - 1); // COMPLIANT
252261
mbtowc(&c, buf, sizeof(buf) + 1); // NON_COMPLIANT
@@ -255,7 +264,7 @@ void test_wrong_buf_size(void) {
255264

256265
// mblen
257266
{
258-
char buf[3];
267+
char buf[3] = {0};
259268
mblen(buf, sizeof(buf)); // COMPLIANT
260269
mblen(buf, sizeof(buf) + 1); // NON_COMPLIANT
261270
mblen((char *)malloc(5), sizeof(buf) * 2); // NON_COMPLIANT
@@ -302,10 +311,11 @@ void test_wrong_buf_size(void) {
302311
{
303312
char buf[64];
304313
char buf2[128];
305-
strxfrm(buf, "abc", sizeof(buf)); // COMPLIANT
306-
strxfrm(buf, "abc", sizeof(buf) + 1); // NON_COMPLIANT
307-
strxfrm(buf, "abc", sizeof(buf) - 1); // COMPLIANT
308-
strxfrm(buf + 1, buf2, sizeof(buf) - 1); // COMPLIANT
314+
strxfrm(buf, "abc", sizeof(buf)); // COMPLIANT
315+
strxfrm(buf, "abc", sizeof(buf) + 1); // NON_COMPLIANT
316+
strxfrm(buf, "abc", sizeof(buf) - 1); // COMPLIANT
317+
strxfrm(buf + 1, buf2,
318+
sizeof(buf) - 1); // NON_COMPLIANT - not null terminated
309319
}
310320

311321
// wcsxfrm

0 commit comments

Comments
 (0)