Commit 28e77cc1 authored by Kees Cook's avatar Kees Cook
Browse files

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: default avatarKees Cook <keescook@chromium.org>
parent 938a000e
Loading
Loading
Loading
Loading
+46 −8
Original line number Diff line number Diff line
@@ -200,16 +200,55 @@ __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)
		/* Error when size is larger than enclosing struct. */
		if (p_size > p_size_field && p_size < size)
			__write_overflow();
	if (p_size < size)
		fortify_panic(__func__);
	return __underlying_memset(p, c, size);

		/* Warn when write size is larger than dest field. */
		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,
@@ -401,7 +440,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
+5 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
#define TEST	\
	memset(instance.buf, 0x42, sizeof(instance.buf) + 1)

#include "test_fortify.h"