fortify: Detect struct member overflows in memset() at compile-time
As done for memcpy(), also update memset() to use the same tightened compile-time bounds checking under CONFIG_FORTIFY_SOURCE. Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
parent
938a000e3f
commit
28e77cc1c0
|
@ -200,17 +200,56 @@ __FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count)
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
__FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size)
|
__FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size,
|
||||||
|
const size_t p_size,
|
||||||
|
const size_t p_size_field)
|
||||||
{
|
{
|
||||||
size_t p_size = __builtin_object_size(p, 0);
|
if (__builtin_constant_p(size)) {
|
||||||
|
/*
|
||||||
|
* Length argument is a constant expression, so we
|
||||||
|
* can perform compile-time bounds checking where
|
||||||
|
* buffer sizes are known.
|
||||||
|
*/
|
||||||
|
|
||||||
if (__builtin_constant_p(size) && p_size < size)
|
/* Error when size is larger than enclosing struct. */
|
||||||
|
if (p_size > p_size_field && p_size < size)
|
||||||
__write_overflow();
|
__write_overflow();
|
||||||
if (p_size < size)
|
|
||||||
fortify_panic(__func__);
|
/* Warn when write size is larger than dest field. */
|
||||||
return __underlying_memset(p, c, size);
|
if (p_size_field < size)
|
||||||
|
__write_overflow_field(p_size_field, size);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* At this point, length argument may not be a constant expression,
|
||||||
|
* so run-time bounds checking can be done where buffer sizes are
|
||||||
|
* known. (This is not an "else" because the above checks may only
|
||||||
|
* be compile-time warnings, and we want to still warn for run-time
|
||||||
|
* overflows.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always stop accesses beyond the struct that contains the
|
||||||
|
* field, when the buffer's remaining size is known.
|
||||||
|
* (The -1 test is to optimize away checks where the buffer
|
||||||
|
* lengths are unknown.)
|
||||||
|
*/
|
||||||
|
if (p_size != (size_t)(-1) && p_size < size)
|
||||||
|
fortify_panic("memset");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({ \
|
||||||
|
size_t __fortify_size = (size_t)(size); \
|
||||||
|
fortify_memset_chk(__fortify_size, p_size, p_size_field), \
|
||||||
|
__underlying_memset(p, c, __fortify_size); \
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
* __builtin_object_size() must be captured here to avoid evaluating argument
|
||||||
|
* side-effects further into the macro layers.
|
||||||
|
*/
|
||||||
|
#define memset(p, c, s) __fortify_memset_chk(p, c, s, \
|
||||||
|
__builtin_object_size(p, 0), __builtin_object_size(p, 1))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To make sure the compiler can enforce protection against buffer overflows,
|
* To make sure the compiler can enforce protection against buffer overflows,
|
||||||
* memcpy(), memmove(), and memset() must not be used beyond individual
|
* memcpy(), memmove(), and memset() must not be used beyond individual
|
||||||
|
@ -401,7 +440,6 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q)
|
||||||
/* Don't use these outside the FORITFY_SOURCE implementation */
|
/* Don't use these outside the FORITFY_SOURCE implementation */
|
||||||
#undef __underlying_memchr
|
#undef __underlying_memchr
|
||||||
#undef __underlying_memcmp
|
#undef __underlying_memcmp
|
||||||
#undef __underlying_memset
|
|
||||||
#undef __underlying_strcat
|
#undef __underlying_strcat
|
||||||
#undef __underlying_strcpy
|
#undef __underlying_strcpy
|
||||||
#undef __underlying_strlen
|
#undef __underlying_strlen
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
#define TEST \
|
||||||
|
memset(instance.buf, 0x42, sizeof(instance.buf) + 1)
|
||||||
|
|
||||||
|
#include "test_fortify.h"
|
Loading…
Reference in New Issue