diff options
| author | Kees Cook <keescook@chromium.org> | 2021-06-16 14:42:23 -0700 |
|---|---|---|
| committer | Kees Cook <keescook@chromium.org> | 2021-06-21 08:40:32 -0700 |
| commit | bcd1e43915effef8bcc583726d55e91780ad7a63 (patch) | |
| tree | 04387ad1105c03bffb459b2ad689d189fb2133eb | |
| parent | 962a1228e8642febb31b8da8143b51667ae155ba (diff) | |
| download | linux-bcd1e43915effef8bcc583726d55e91780ad7a63.tar.gz | |
fortify: Detect struct member overflows in memset()
As done for memcpy(), also update memset() to use the same tightened
bounds checking under CONFIG_FORTIFY_SOURCE.
Signed-off-by: Kees Cook <keescook@chromium.org>
| -rw-r--r-- | include/linux/fortify-string.h | 40 | ||||
| -rw-r--r-- | lib/test_fortify/write_overflow_field-memset.c (renamed from lib/test_fortify/write_overflow-memset.c) | 2 |
2 files changed, 36 insertions, 6 deletions
diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 2f0af14545b03..65189da83c907 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -176,14 +176,44 @@ __FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count) return p; } -__FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size) +#define memset(p, c, s) __fortify_memset(p, c, s) +__FORTIFY_INLINE void *__fortify_memset(void *p, int c, __kernel_size_t size) { size_t p_size = __builtin_object_size(p, 0); + size_t p_size_field = __builtin_object_size(p, 1); - if (__builtin_constant_p(size) && p_size < size) - __write_overflow(); - if (p_size < size) - fortify_panic(__func__); + if (__builtin_constant_p(size)) { + /* + * Length argument is a constant expression, so we + * can perform compile-time bounds checking where + * buffer sizes are known. + */ + + /* Disallow size argument larger than dest field. */ + if (p_size_field < size) + __write_overflow_field(); + } else { + /* + * Length argument is not a constant expression, so + * run-time bounds checking can happen where buffer + * sizes are known. + */ + + /* + * Warn when writing beyond destination field size. + * Since flexible-arrays are considered 0 bytes, we + * must ignore 0 sizes at runtime for now. + */ + if (p_size_field && p_size != p_size_field && p_size_field < size) + fortify_warn_write("memset", p_size_field, size); + + /* + * Always stop accesses beyond the struct that contains the + * field, when the buffer's remaining size is known. + */ + if (p_size != (size_t)(-1) && p_size < size) + fortify_panic("memset"); + } return __underlying_memset(p, c, size); } diff --git a/lib/test_fortify/write_overflow-memset.c b/lib/test_fortify/write_overflow_field-memset.c index 35becb7b5ba4a..2331da26909e7 100644 --- a/lib/test_fortify/write_overflow-memset.c +++ b/lib/test_fortify/write_overflow_field-memset.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only #define TEST \ - memset(small, 0x5A, sizeof(small) + 1) + memset(instance.buf, 0x42, sizeof(instance.buf) + 1) #include "test_fortify.h" |
