aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2021-06-16 14:42:23 -0700
committerKees Cook <keescook@chromium.org>2021-06-21 08:40:32 -0700
commitbcd1e43915effef8bcc583726d55e91780ad7a63 (patch)
tree04387ad1105c03bffb459b2ad689d189fb2133eb
parent962a1228e8642febb31b8da8143b51667ae155ba (diff)
downloadlinux-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.h40
-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"