Skip to content

Commit b1745f5

Browse files
authored
Merge pull request #2402 from geoffw0/nospace
CPP: Make NoSpaceForZeroTerminator.ql more conservative.
2 parents 18e1708 + d6cbc67 commit b1745f5

File tree

6 files changed

+177
-19
lines changed

6 files changed

+177
-19
lines changed

change-notes/1.24/analysis-cpp.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Improvements to C/C++ analysis
2+
3+
The following changes in version 1.24 affect C/C++ analysis in all applications.
4+
5+
## General improvements
6+
7+
## New queries
8+
9+
| **Query** | **Tags** | **Purpose** |
10+
|-----------------------------|-----------|--------------------------------------------------------------------|
11+
12+
## Changes to existing queries
13+
14+
| **Query** | **Expected impact** | **Change** |
15+
|----------------------------|------------------------|------------------------------------------------------------------|
16+
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
17+
18+
## Changes to libraries
19+
20+
*

cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,28 @@
1616

1717
import cpp
1818
import semmle.code.cpp.dataflow.DataFlow
19-
import semmle.code.cpp.models.implementations.Memcpy
19+
import semmle.code.cpp.models.interfaces.ArrayFunction
2020

2121
class MallocCall extends FunctionCall {
2222
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
2323

24-
Expr getAllocatedSize() {
25-
if this.getArgument(0) instanceof VariableAccess
26-
then
27-
exists(LocalScopeVariable v, ControlFlowNode def |
28-
definitionUsePair(v, def, this.getArgument(0)) and
29-
exprDefinition(v, def, result)
30-
)
31-
else result = this.getArgument(0)
32-
}
24+
Expr getAllocatedSize() { result = this.getArgument(0) }
3325
}
3426

3527
predicate terminationProblem(MallocCall malloc, string msg) {
36-
malloc.getAllocatedSize() instanceof StrlenCall and
37-
not exists(FunctionCall fc, MemcpyFunction memcpy, int ix |
38-
DataFlow::localExprFlow(malloc, fc.getArgument(ix)) and
39-
fc.getTarget() = memcpy and
40-
memcpy.hasArrayOutput(ix)
28+
// malloc(strlen(...))
29+
exists(StrlenCall strlen | DataFlow::localExprFlow(strlen, malloc.getAllocatedSize())) and
30+
// flows into a null-terminated string function
31+
exists(ArrayFunction af, FunctionCall fc, int arg |
32+
DataFlow::localExprFlow(malloc, fc.getArgument(arg)) and
33+
fc.getTarget() = af and
34+
(
35+
// null terminated string
36+
af.hasArrayWithNullTerminator(arg)
37+
or
38+
// likely a null terminated string (such as `strcpy`, `strcat`)
39+
af.hasArrayWithUnknownSize(arg)
40+
)
4141
) and
4242
msg = "This allocation does not include space to null-terminate the string."
4343
}
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
| test.c:15:20:15:25 | call to malloc | This allocation does not include space to null-terminate the string. |
2-
| test.c:29:20:29:25 | call to malloc | This allocation does not include space to null-terminate the string. |
3-
| test.c:44:20:44:25 | call to malloc | This allocation does not include space to null-terminate the string. |
4-
| test.cpp:18:35:18:40 | call to malloc | This allocation does not include space to null-terminate the string. |
1+
| test.c:16:20:16:25 | call to malloc | This allocation does not include space to null-terminate the string. |
2+
| test.c:32:20:32:25 | call to malloc | This allocation does not include space to null-terminate the string. |
3+
| test.c:49:20:49:25 | call to malloc | This allocation does not include space to null-terminate the string. |
4+
| test.cpp:24:35:24:40 | call to malloc | This allocation does not include space to null-terminate the string. |
5+
| test.cpp:63:28:63:33 | call to malloc | This allocation does not include space to null-terminate the string. |
6+
| test.cpp:71:28:71: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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77
typedef unsigned long size_t;
88
void *malloc(size_t size);
99
void free(void *ptr);
10+
char *strcpy(char *s1, const char *s2);
1011

1112
//// Test code /////
1213

1314
void bad0(char *str) {
1415
// BAD -- Not allocating space for '\0' terminator
1516
char *buffer = malloc(strlen(str));
17+
strcpy(buffer, str);
1618
free(buffer);
1719
}
1820

1921
void good0(char *str) {
2022
// GOOD -- Allocating extra byte for terminator
2123
char *buffer = malloc(strlen(str)+1);
24+
strcpy(buffer, str);
2225
free(buffer);
2326
}
2427

@@ -27,13 +30,15 @@ void bad1(char *str) {
2730
int len = strlen(str);
2831
// BAD -- Not allocating space for '\0' terminator
2932
char *buffer = malloc(len);
33+
strcpy(buffer, str);
3034
free(buffer);
3135
}
3236

3337
void good1(char *str) {
3438
int len = strlen(str);
3539
// GOOD -- Allocating extra byte for terminator
3640
char *buffer = malloc(len+1);
41+
strcpy(buffer, str);
3742
free(buffer);
3843
}
3944

@@ -42,25 +47,29 @@ void bad2(char *str) {
4247
int len = strlen(str);
4348
// BAD -- Not allocating space for '\0' terminator
4449
char *buffer = malloc(len);
50+
strcpy(buffer, str);
4551
free(buffer);
4652
}
4753

4854
void good2(char *str) {
4955
int len = strlen(str)+1;
5056
// GOOD -- Allocating extra byte for terminator
5157
char *buffer = malloc(len);
58+
strcpy(buffer, str);
5259
free(buffer);
5360
}
5461

5562
void bad3(char *str) {
5663
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
5764
char *buffer = malloc(strlen(str) * sizeof(char));
65+
strcpy(buffer, str);
5866
free(buffer);
5967
}
6068

6169
void good3(char *str) {
6270
// GOOD -- Allocating extra byte for terminator
6371
char *buffer = malloc((strlen(str) + 1) * sizeof(char));
72+
strcpy(buffer, str);
6473
free(buffer);
6574
}
6675

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,100 @@ typedef unsigned long size_t;
1010
void *malloc(size_t size);
1111
void free(void *ptr);
1212
size_t wcslen(const wchar_t *s);
13+
wchar_t* wcscpy(wchar_t* s1, const wchar_t* s2);
14+
int sprintf(char *s, const char *format, ...);
15+
int wprintf(const wchar_t *format, ...);
16+
char *strcat(char *s1, const char *s2);
17+
size_t strlen(const char *s);
18+
int strcmp(const char *s1, const char *s2);
1319

1420
//// Test code /////
1521

1622
void bad1(wchar_t *wstr) {
1723
// BAD -- Not allocating space for '\0' terminator
1824
wchar_t *wbuffer = (wchar_t *)malloc(wcslen(wstr));
25+
wcscpy(wbuffer, wstr);
1926
free(wbuffer);
2027
}
2128

2229
void bad2(wchar_t *wstr) {
2330
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
2431
wchar_t *wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t));
32+
wcscpy(wbuffer, wstr);
2533
free(wbuffer);
2634
}
2735

2836
void good1(wchar_t *wstr) {
2937
// GOOD -- Allocating extra character for terminator
3038
wchar_t *wbuffer = (wchar_t *)malloc((wcslen(wstr) + 1) * sizeof(wchar_t));
39+
wcscpy(wbuffer, wstr);
3140
free(wbuffer);
3241
}
42+
43+
void bad3(char *str) {
44+
// BAD -- zero-termination proved by sprintf (as destination) [NOT DETECTED]
45+
char *buffer = (char *)malloc(strlen(str));
46+
sprintf(buffer, "%s", str);
47+
free(buffer);
48+
}
49+
50+
void decode(char *dest, char *src);
51+
void wdecode(wchar_t *dest, wchar_t *src);
52+
53+
void bad4(char *str) {
54+
// BAD -- zero-termination proved by wprintf (as parameter) [NOT DETECTED]
55+
char *buffer = (char *)malloc(strlen(str));
56+
decode(buffer, str);
57+
wprintf(L"%s", buffer);
58+
free(buffer);
59+
}
60+
61+
void bad5(char *str) {
62+
// BAD -- zero-termination proved by strcat (as destination)
63+
char *buffer = (char *)malloc(strlen(str));
64+
buffer[0] = 0;
65+
strcat(buffer, str);
66+
free(buffer);
67+
}
68+
69+
void bad6(char *str, char *dest) {
70+
// BAD -- zero-termination proved by strcat (as source)
71+
char *buffer = (char *)malloc(strlen(str));
72+
decode(buffer, str);
73+
strcat(dest, buffer);
74+
free(buffer);
75+
}
76+
77+
void bad7(char *str, char *str2) {
78+
// BAD -- zero-termination proved by strcmp [NOT DETECTED]
79+
char *buffer = (char *)malloc(strlen(str));
80+
decode(buffer, str);
81+
if (strcmp(buffer, str2) == 0) {
82+
// ...
83+
}
84+
free(buffer);
85+
}
86+
87+
void bad8(wchar_t *str) {
88+
// BAD -- zero-termination proved by wcslen [NOT DETECTED]
89+
wchar_t *wbuffer = (wchar_t *)malloc(wcslen(str));
90+
wdecode(wbuffer, str);
91+
if (wcslen(wbuffer) == 0) {
92+
// ...
93+
}
94+
free(wbuffer);
95+
}
96+
97+
void good2(char *str, char *dest) {
98+
// GOOD -- zero-termination not proven
99+
char *buffer = (char *)malloc(strlen(str));
100+
decode(buffer, str);
101+
free(buffer);
102+
}
103+
104+
void bad9(wchar_t *wstr) {
105+
// BAD -- using new [NOT DETECTED]
106+
wchar_t *wbuffer = new wchar_t[wcslen(wstr)];
107+
wcscpy(wbuffer, wstr);
108+
delete wbuffer;
109+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
///// Library functions //////
3+
4+
typedef unsigned long size_t;
5+
6+
void *malloc(size_t size);
7+
void free(void *ptr);
8+
size_t strlen(const char *s);
9+
10+
namespace std
11+
{
12+
template<class charT> struct char_traits;
13+
14+
template <class T> class allocator {
15+
public:
16+
allocator() throw();
17+
typedef size_t size_type;
18+
};
19+
20+
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
21+
class basic_string {
22+
public:
23+
typedef typename Allocator::size_type size_type;
24+
explicit basic_string(const Allocator& a = Allocator());
25+
basic_string(const charT* s, const Allocator& a = Allocator());
26+
basic_string(const charT* s, size_type n, const Allocator& a = Allocator());
27+
28+
const charT* c_str() const;
29+
};
30+
31+
typedef basic_string<char> string;
32+
}
33+
34+
//// Test code /////
35+
36+
void bad1(char *str) {
37+
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
38+
char *buffer = (char *)malloc(strlen(str));
39+
std::string str2(buffer);
40+
free(buffer);
41+
}
42+
43+
void good1(char *str) {
44+
// GOOD --- copy does not overrun due to size limit
45+
char *buffer = (char *)malloc(strlen(str));
46+
std::string str2(buffer, strlen(str));
47+
free(buffer);
48+
}
49+
50+

0 commit comments

Comments
 (0)