Skip to content

Commit 28e77cc

Browse files
committed
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>
1 parent 938a000 commit 28e77cc

2 files changed

Lines changed: 51 additions & 8 deletions

File tree

include/linux/fortify-string.h

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,17 +200,56 @@ __FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count)
200200
return p;
201201
}
202202

203-
__FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size)
203+
__FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size,
204+
const size_t p_size,
205+
const size_t p_size_field)
204206
{
205-
size_t p_size = __builtin_object_size(p, 0);
207+
if (__builtin_constant_p(size)) {
208+
/*
209+
* Length argument is a constant expression, so we
210+
* can perform compile-time bounds checking where
211+
* buffer sizes are known.
212+
*/
206213

207-
if (__builtin_constant_p(size) && p_size < size)
208-
__write_overflow();
209-
if (p_size < size)
210-
fortify_panic(__func__);
211-
return __underlying_memset(p, c, size);
214+
/* Error when size is larger than enclosing struct. */
215+
if (p_size > p_size_field && p_size < size)
216+
__write_overflow();
217+
218+
/* Warn when write size is larger than dest field. */
219+
if (p_size_field < size)
220+
__write_overflow_field(p_size_field, size);
221+
}
222+
/*
223+
* At this point, length argument may not be a constant expression,
224+
* so run-time bounds checking can be done where buffer sizes are
225+
* known. (This is not an "else" because the above checks may only
226+
* be compile-time warnings, and we want to still warn for run-time
227+
* overflows.)
228+
*/
229+
230+
/*
231+
* Always stop accesses beyond the struct that contains the
232+
* field, when the buffer's remaining size is known.
233+
* (The -1 test is to optimize away checks where the buffer
234+
* lengths are unknown.)
235+
*/
236+
if (p_size != (size_t)(-1) && p_size < size)
237+
fortify_panic("memset");
212238
}
213239

240+
#define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({ \
241+
size_t __fortify_size = (size_t)(size); \
242+
fortify_memset_chk(__fortify_size, p_size, p_size_field), \
243+
__underlying_memset(p, c, __fortify_size); \
244+
})
245+
246+
/*
247+
* __builtin_object_size() must be captured here to avoid evaluating argument
248+
* side-effects further into the macro layers.
249+
*/
250+
#define memset(p, c, s) __fortify_memset_chk(p, c, s, \
251+
__builtin_object_size(p, 0), __builtin_object_size(p, 1))
252+
214253
/*
215254
* To make sure the compiler can enforce protection against buffer overflows,
216255
* memcpy(), memmove(), and memset() must not be used beyond individual
@@ -401,7 +440,6 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q)
401440
/* Don't use these outside the FORITFY_SOURCE implementation */
402441
#undef __underlying_memchr
403442
#undef __underlying_memcmp
404-
#undef __underlying_memset
405443
#undef __underlying_strcat
406444
#undef __underlying_strcpy
407445
#undef __underlying_strlen
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#define TEST \
3+
memset(instance.buf, 0x42, sizeof(instance.buf) + 1)
4+
5+
#include "test_fortify.h"

0 commit comments

Comments
 (0)