Skip to content

Commit 7f56c67

Browse files
authored
Merge pull request #18837 from geoffw0/overflowbuffer
C++: Improve and promote cpp/overflow-buffer
2 parents ee08e8b + 7169c4b commit 7f56c67

17 files changed

+447
-137
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Fixed an issue where the `getBufferSize` predicate in `commons/Buffer.qll` was returning results for references inside `offsetof` expressions, which are not accesses to a buffer.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Modified the `getBufferSize` predicate in `commons/Buffer.qll` to be more tolerant in some cases involving member variables in a larger struct or class.

cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll

+12-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ private int getSize(VariableAccess va) {
7171
result = t.getSize()
7272
)
7373
or
74-
exists(Class c |
74+
exists(Class c, int trueSize |
7575
// Otherwise, we find the "outermost" object and compute the size
7676
// as the difference between the size of the type of the "outermost
7777
// object" and the offset of the field relative to that type.
@@ -91,7 +91,9 @@ private int getSize(VariableAccess va) {
9191
// of `y` relative to the type `S2` (i.e., `4`). So the size of the
9292
// buffer is `12 - 4 = 8`.
9393
c = getRootType(va) and
94-
result = c.getSize() - v.(Field).getOffsetInClass(c)
94+
// we calculate the size based on the last field, to avoid including any padding after it
95+
trueSize = max(Field f | | f.getOffsetInClass(c) + f.getUnspecifiedType().getSize()) and
96+
result = trueSize - v.(Field).getOffsetInClass(c)
9597
)
9698
)
9799
}
@@ -105,9 +107,16 @@ private int getSize(VariableAccess va) {
105107
private int isSource(Expr bufferExpr, Element why) {
106108
exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() |
107109
// buffer is a fixed size array
108-
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
110+
exists(bufferVar.getUnspecifiedType().(ArrayType).getSize()) and
111+
result =
112+
unique(int size | // more generous than .getSize() itself, when the array is a class field or similar.
113+
size = getSize(bufferExpr)
114+
|
115+
size
116+
) and
109117
why = bufferVar and
110118
not memberMayBeVarSize(_, bufferVar) and
119+
not exists(BuiltInOperationBuiltInOffsetOf offsetof | offsetof.getAChild*() = bufferExpr) and
111120
// zero sized arrays are likely to have special usage, for example
112121
// behaving a bit like a 'union' overlapping other fields.
113122
not result = 0

cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
* buffer.
66
* @kind problem
77
* @id cpp/overflow-buffer
8-
* @problem.severity recommendation
8+
* @problem.severity warning
99
* @security-severity 9.3
10+
* @precision medium
1011
* @tags security
1112
* external/cwe/cwe-119
1213
* external/cwe/cwe-121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The query "Call to memory access function may overflow buffer" (`cpp/overflow-buffer`) has been added to the security-extended query suite. The query detects a range of buffer overflow and underflow issues.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Due to changes in libraries the query "Static array access may cause overflow" (`cpp/static-buffer-overflow`) will no longer report cases where multiple fields of a struct or class are written with a single `memset` or similar operation.

cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowBuffer.expected

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
| tests.cpp:45:9:45:14 | call to memcpy | This 'memcpy' operation accesses 32 bytes but the $@ is only 16 bytes. | tests.cpp:32:10:32:18 | charFirst | destination buffer |
2-
| tests.cpp:60:9:60:14 | call to memcpy | This 'memcpy' operation accesses 32 bytes but the $@ is only 16 bytes. | tests.cpp:32:10:32:18 | charFirst | destination buffer |
31
| tests.cpp:171:9:171:14 | call to memcpy | This 'memcpy' operation accesses 100 bytes but the $@ is only 50 bytes. | tests.cpp:164:20:164:25 | call to malloc | destination buffer |
42
| tests.cpp:172:9:172:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:164:20:164:25 | call to malloc | array |
53
| tests.cpp:192:9:192:14 | call to memcpy | This 'memcpy' operation accesses 100 bytes but the $@ is only 50 bytes. | tests.cpp:181:10:181:22 | dataBadBuffer | destination buffer |
Original file line numberDiff line numberDiff line change
@@ -1,2 +0,0 @@
1-
| tests.cpp:45:51:45:72 | sizeof(<expr>) | Potential buffer-overflow: 'charFirst' has size 16 not 32. |
2-
| tests.cpp:60:52:60:74 | sizeof(<expr>) | Potential buffer-overflow: 'charFirst' has size 16 not 32. |

cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/tests.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void CWE121_Stack_Based_Buffer_Overflow__char_type_overrun_memcpy_01_bad()
4242
/* Print the initial block pointed to by structCharVoid.voidSecond */
4343
printLine((char *)structCharVoid.voidSecond);
4444
/* FLAW: Use the sizeof(structCharVoid) which will overwrite the pointer voidSecond */
45-
memcpy(structCharVoid.charFirst, SRC_STR, sizeof(structCharVoid));
45+
memcpy(structCharVoid.charFirst, SRC_STR, sizeof(structCharVoid)); // [NOT DETECTED]
4646
structCharVoid.charFirst[(sizeof(structCharVoid.charFirst)/sizeof(char))-1] = '\0'; /* null terminate the string */
4747
printLine((char *)structCharVoid.charFirst);
4848
printLine((char *)structCharVoid.voidSecond);
@@ -57,7 +57,7 @@ void CWE122_Heap_Based_Buffer_Overflow__char_type_overrun_memcpy_01_bad()
5757
/* Print the initial block pointed to by structCharVoid->voidSecond */
5858
printLine((char *)structCharVoid->voidSecond);
5959
/* FLAW: Use the sizeof(*structCharVoid) which will overwrite the pointer y */
60-
memcpy(structCharVoid->charFirst, SRC_STR, sizeof(*structCharVoid));
60+
memcpy(structCharVoid->charFirst, SRC_STR, sizeof(*structCharVoid)); // [NOT DETECTED]
6161
structCharVoid->charFirst[(sizeof(structCharVoid->charFirst)/sizeof(char))-1] = '\0'; /* null terminate the string */
6262
printLine((char *)structCharVoid->charFirst);
6363
printLine((char *)structCharVoid->voidSecond);
@@ -292,7 +292,7 @@ namespace CWE122_Heap_Based_Buffer_Overflow__cpp_CWE193_wchar_t_ncpy_01
292292
delete [] data;
293293
}
294294
}
295-
295+
296296
static void goodG2B()
297297
{
298298
wchar_t * data;
@@ -459,7 +459,7 @@ void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_wchar_t_ncpy_01_bad()
459459
#ifdef _WIN32
460460
int _snwprintf(wchar_t *buffer, size_t count, const wchar_t *format, ...);
461461
#define SNPRINTF _snwprintf
462-
#else
462+
#else
463463
int snprintf(char *s, size_t n, const char *format, ...);
464464
int swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...);
465465
//#define SNPRINTF snprintf --- original code; using snprintf appears to be a mistake in samate?
@@ -485,14 +485,14 @@ void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_wchar_t_snprintf_01_bad()
485485
}
486486

487487
/* classes used in some test cases as a custom type */
488-
class TwoIntsClass
488+
class TwoIntsClass
489489
{
490490
public: // Needed to access variables from label files
491491
int intOne;
492492
int intTwo;
493493
};
494494

495-
class OneIntClass
495+
class OneIntClass
496496
{
497497
public: // Needed to access variables from label files
498498
int intOne;
@@ -636,7 +636,7 @@ void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_wchar_t_snprintf_31_bad()
636636

637637
int rand(void);
638638

639-
int globalReturnsTrueOrFalse()
639+
int globalReturnsTrueOrFalse()
640640
{
641641
return (rand() % 2);
642642
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
| tests.cpp:1055:2:1055:8 | call to strncpy | This 'call to strncpy' operation is limited to 131 bytes but the destination is only 128 bytes. |
2+
| tests.cpp:1057:2:1057:8 | call to strncpy | This 'call to strncpy' operation is limited to 131 bytes but the destination is only 64 bytes. |
13
| var_size_struct.cpp:73:3:73:9 | call to strncpy | This 'call to strncpy' operation is limited to 1025 bytes but the destination is only 1024 bytes. |
24
| var_size_struct.cpp:103:3:103:9 | call to strncpy | This 'call to strncpy' operation is limited to 129 bytes but the destination is only 128 bytes. |

0 commit comments

Comments
 (0)