Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+51 -8
+46 -8
include/linux/fortify-string.h
··· 200 200 return p; 201 201 } 202 202 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) 204 206 { 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 + */ 206 213 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"); 212 238 } 239 + 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)) 213 252 214 253 /* 215 254 * To make sure the compiler can enforce protection against buffer overflows, ··· 440 401 /* Don't use these outside the FORITFY_SOURCE implementation */ 441 402 #undef __underlying_memchr 442 403 #undef __underlying_memcmp 443 - #undef __underlying_memset 444 404 #undef __underlying_strcat 445 405 #undef __underlying_strcpy 446 406 #undef __underlying_strlen
+5
lib/test_fortify/write_overflow_field-memset.c
··· 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"