Skip to content

Commit

Permalink
fortify: Detect struct member overflows in memset() at compile-time
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
kees authored and intel-lab-lkp committed Jul 27, 2021
1 parent 91cf9b4 commit d6aff26
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 8 deletions.
54 changes: 46 additions & 8 deletions include/linux/fortify-string.h
Expand Up @@ -175,17 +175,56 @@ __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)
__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)
__write_overflow();
if (p_size < size)
fortify_panic(__func__);
return __underlying_memset(p, c, size);
/* Error when size is larger than enclosing struct. */
if (p_size > p_size_field && p_size < size)
__write_overflow();

/* Warn when write size is larger than dest field. */
if (p_size_field < size)
__write_overflow_field();
}
/*
* 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,
* memcpy(), memmove(), and memset() must not be used beyond individual
Expand Down Expand Up @@ -373,7 +412,6 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q)
/* Don't use these outside the FORITFY_SOURCE implementation */
#undef __underlying_memchr
#undef __underlying_memcmp
#undef __underlying_memset
#undef __underlying_strcat
#undef __underlying_strcpy
#undef __underlying_strlen
Expand Down
5 changes: 5 additions & 0 deletions lib/test_fortify/write_overflow_field-memset.c
@@ -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"

0 comments on commit d6aff26

Please sign in to comment.