Skip to content

Commit 230e5a3

Browse files
authored
Merge pull request github#3326 from Cornelius-Riemenschneider/alloc-size-mul
C++: Allocation.qll: Analyze common pattern of malloc() invocations to provide more accurate getSizeMult()
2 parents 20c956e + a50d5b7 commit 230e5a3

File tree

10 files changed

+72
-7
lines changed

10 files changed

+72
-7
lines changed

cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,36 @@ class OperatorNewAllocationFunction extends AllocationFunction {
270270
}
271271
}
272272

273+
/**
274+
* The predicate analyzes a `sizeExpr`, which is an argument to an allocation
275+
* function like malloc, and tries to split it into an expression `lengthExpr`
276+
* that describes the length of the allocated array, and the size of the allocated
277+
* element type `sizeof`.
278+
* If this is not possible, the allocation is considered to be of size 1 and of
279+
* length `sizeExpr`.
280+
*/
281+
private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof) {
282+
if
283+
sizeExpr instanceof MulExpr and
284+
exists(SizeofOperator sizeofOp, Expr lengthOp |
285+
sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
286+
lengthOp = sizeExpr.(MulExpr).getAnOperand() and
287+
not lengthOp instanceof SizeofOperator and
288+
exists(sizeofOp.getValue().toInt())
289+
)
290+
then
291+
exists(SizeofOperator sizeofOp |
292+
sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
293+
lengthExpr = sizeExpr.(MulExpr).getAnOperand() and
294+
not lengthExpr instanceof SizeofOperator and
295+
sizeof = sizeofOp.getValue().toInt()
296+
)
297+
else (
298+
lengthExpr = sizeExpr and
299+
sizeof = 1
300+
)
301+
}
302+
273303
/**
274304
* An allocation expression that is a function call, such as call to `malloc`.
275305
*/
@@ -287,15 +317,25 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
287317
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
288318
}
289319

290-
override Expr getSizeExpr() { result = getArgument(target.getSizeArg()) }
320+
override Expr getSizeExpr() {
321+
exists(Expr sizeExpr | sizeExpr = getArgument(target.getSizeArg()) |
322+
if exists(target.getSizeMult())
323+
then result = sizeExpr
324+
else
325+
exists(Expr lengthExpr |
326+
deconstructSizeExpr(sizeExpr, lengthExpr, _) and
327+
result = lengthExpr
328+
)
329+
)
330+
}
291331

292332
override int getSizeMult() {
293333
// malloc with multiplier argument that is a constant
294334
result = getArgument(target.getSizeMult()).getValue().toInt()
295335
or
296336
// malloc with no multiplier argument
297337
not exists(target.getSizeMult()) and
298-
result = 1
338+
deconstructSizeExpr(getArgument(target.getSizeArg()), _, result)
299339
}
300340

301341
override int getSizeBytes() { result = getSizeExpr().getValue().toInt() * getSizeMult() }

cpp/ql/test/library-tests/allocators/allocators.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,15 @@ void directOperatorCall() {
149149
ptr = operator new(sizeof(int));
150150
operator delete(ptr);
151151
}
152+
153+
void *malloc(size_t);
154+
155+
void testMalloc(size_t count) {
156+
malloc(5);
157+
malloc(5 * sizeof(int));
158+
malloc(count);
159+
malloc(count * sizeof(int));
160+
malloc(count * sizeof(int) + 1);
161+
malloc(((int) count) * sizeof(void *));
162+
malloc(sizeof(void*) * sizeof(int));
163+
}

cpp/ql/test/library-tests/allocators/allocators.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ allocationFunctions
5555
| allocators.cpp:122:7:122:20 | operator new[] | getPlacementArgument = 1, getSizeArg = 0 |
5656
| allocators.cpp:123:7:123:18 | operator new | getSizeArg = 0, requiresDealloc |
5757
| allocators.cpp:124:7:124:20 | operator new[] | getSizeArg = 0, requiresDealloc |
58+
| allocators.cpp:153:7:153:12 | malloc | getSizeArg = 0, requiresDealloc |
5859
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
5960
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
6061
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
@@ -84,6 +85,13 @@ allocationExprs
8485
| allocators.cpp:143:13:143:28 | new[] | getSizeBytes = 400, requiresDealloc |
8586
| allocators.cpp:144:13:144:31 | new[] | getSizeExpr = x, getSizeMult = 900, requiresDealloc |
8687
| allocators.cpp:149:8:149:19 | call to operator new | getSizeBytes = 4, getSizeExpr = sizeof(int), getSizeMult = 1, requiresDealloc |
88+
| allocators.cpp:156:3:156:8 | call to malloc | getSizeBytes = 5, getSizeExpr = 5, getSizeMult = 1, requiresDealloc |
89+
| allocators.cpp:157:3:157:8 | call to malloc | getSizeBytes = 20, getSizeExpr = 5, getSizeMult = 4, requiresDealloc |
90+
| allocators.cpp:158:3:158:8 | call to malloc | getSizeExpr = count, getSizeMult = 1, requiresDealloc |
91+
| allocators.cpp:159:3:159:8 | call to malloc | getSizeExpr = count, getSizeMult = 4, requiresDealloc |
92+
| allocators.cpp:160:3:160:8 | call to malloc | getSizeExpr = ... + ..., getSizeMult = 1, requiresDealloc |
93+
| allocators.cpp:161:3:161:8 | call to malloc | getSizeExpr = count, getSizeMult = 8, requiresDealloc |
94+
| allocators.cpp:162:3:162:8 | call to malloc | getSizeBytes = 32, getSizeExpr = ... * ..., getSizeMult = 1, requiresDealloc |
8795
deallocationFunctions
8896
| allocators.cpp:11:6:11:20 | operator delete | getFreedArg = 0 |
8997
| allocators.cpp:12:6:12:22 | operator delete[] | getFreedArg = 0 |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
| tests1.cpp:26:21:26:26 | call to malloc | This allocation does not include space to null-terminate the string. |
2+
| tests1.cpp:36:21:36:26 | call to malloc | This allocation does not include space to null-terminate the string. |
23
| tests1.cpp:56:21:56:27 | call to realloc | This allocation does not include space to null-terminate the string. |
34
| tests1.cpp:67:21:67:26 | call to malloc | This allocation does not include space to null-terminate the string. |
45
| tests1.cpp:89:25:89:30 | call to malloc | This allocation does not include space to null-terminate the string. |
6+
| tests1.cpp:109:25:109:30 | call to malloc | This allocation does not include space to null-terminate the string. |
57
| tests3.cpp:25:21:25:31 | call to malloc | This allocation does not include space to null-terminate the string. |
68
| tests3.cpp:30:21:30:31 | call to malloc | This allocation does not include space to null-terminate the string. |
79
| tests3.cpp:53:17:53:44 | new[] | This allocation does not include space to null-terminate the string. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
| tests2.cpp:34:4:34:9 | call to strcat | This buffer only contains enough room for 'str1' (copied on line 33) |
2+
| tests2.cpp:52:4:52:9 | call to strcat | This buffer only contains enough room for 'str1' (copied on line 51) |

cpp/ql/test/query-tests/Critical/OverflowCalculated/tests1.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void tests1(int case_num)
3333
break;
3434

3535
case 3:
36-
buffer = (char *)malloc(strlen(str) * sizeof(char)); // BAD [NOT DETECTED]
36+
buffer = (char *)malloc(strlen(str) * sizeof(char)); // BAD
3737
strcpy(buffer, str);
3838
break;
3939

@@ -106,7 +106,7 @@ void tests1(int case_num)
106106
break;
107107

108108
case 105:
109-
wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t)); // BAD [NOT DETECTED]
109+
wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t)); // BAD
110110
wcscpy(wbuffer, wstr);
111111
break;
112112

cpp/ql/test/query-tests/Critical/OverflowCalculated/tests2.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ void tests2(int case_num)
4747
break;
4848

4949
case 4:
50-
buffer = (char *)malloc((strlen(str1) + 1) * sizeof(char)); // BAD [NOT DETECTED]
50+
buffer = (char *)malloc((strlen(str1) + 1) * sizeof(char)); // BAD
5151
strcpy(buffer, str1);
5252
strcat(buffer, str2);
5353
break;

cpp/ql/test/query-tests/Security/CWE/CWE-131/semmle/NoSpaceForZeroTerminator/NoSpaceForZeroTerminator.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
| test.c:16:20:16:25 | call to malloc | This allocation does not include space to null-terminate the string. |
44
| test.c:32:20:32:25 | call to malloc | This allocation does not include space to null-terminate the string. |
55
| test.c:49:20:49:25 | call to malloc | This allocation does not include space to null-terminate the string. |
6+
| test.c:64:20:64:25 | call to malloc | This allocation does not include space to null-terminate the string. |
67
| test.cpp:24:35:24:40 | call to malloc | This allocation does not include space to null-terminate the string. |
8+
| test.cpp:31:35:31:40 | call to malloc | This allocation does not include space to null-terminate the string. |
79
| test.cpp:45:28:45:33 | call to malloc | This allocation does not include space to null-terminate the string. |
810
| test.cpp:55:28:55:33 | call to malloc | This allocation does not include space to null-terminate the string. |
911
| test.cpp:63:28:63:33 | call to malloc | This allocation does not include space to null-terminate the string. |

cpp/ql/test/query-tests/Security/CWE/CWE-131/semmle/NoSpaceForZeroTerminator/test.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ void good2(char *str) {
6060
}
6161

6262
void bad3(char *str) {
63-
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
63+
// BAD -- Not allocating space for '\0' terminator
6464
char *buffer = malloc(strlen(str) * sizeof(char));
6565
strcpy(buffer, str);
6666
free(buffer);

cpp/ql/test/query-tests/Security/CWE/CWE-131/semmle/NoSpaceForZeroTerminator/test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void bad1(wchar_t *wstr) {
2727
}
2828

2929
void bad2(wchar_t *wstr) {
30-
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
30+
// BAD -- Not allocating space for '\0' terminator
3131
wchar_t *wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t));
3232
wcscpy(wbuffer, wstr);
3333
free(wbuffer);

0 commit comments

Comments
 (0)