Commit 4be240b1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'memcpy-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull FORTIFY_SOURCE updates from Kees Cook:
 "This series consists of two halves:

   - strict compile-time buffer size checking under FORTIFY_SOURCE for
     the memcpy()-family of functions (for extensive details and
     rationale, see the first commit)

   - enabling FORTIFY_SOURCE for Clang, which has had many overlapping
     bugs that we've finally worked past"

* tag 'memcpy-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  fortify: Add Clang support
  fortify: Make sure strlen() may still be used as a constant expression
  fortify: Use __diagnose_as() for better diagnostic coverage
  fortify: Make pointer arguments const
  Compiler Attributes: Add __diagnose_as for Clang
  Compiler Attributes: Add __overloadable for Clang
  Compiler Attributes: Add __pass_object_size for Clang
  fortify: Replace open-coded __gnu_inline attribute
  fortify: Update compile-time tests for Clang 14
  fortify: Detect struct member overflows in memset() at compile-time
  fortify: Detect struct member overflows in memmove() at compile-time
  fortify: Detect struct member overflows in memcpy() at compile-time
parents 3f728213 281d0c96
...@@ -37,10 +37,11 @@ ...@@ -37,10 +37,11 @@
* try to define their own functions if these are not defined as macros. * try to define their own functions if these are not defined as macros.
*/ */
#define memzero(s, n) memset((s), 0, (n)) #define memzero(s, n) memset((s), 0, (n))
#ifndef memmove
#define memmove memmove #define memmove memmove
/* Functions used by the included decompressor code below. */ /* Functions used by the included decompressor code below. */
void *memmove(void *dest, const void *src, size_t n); void *memmove(void *dest, const void *src, size_t n);
#endif
/* /*
* This is set up by the setup-routine at boot-time * This is set up by the setup-routine at boot-time
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#undef memcpy #undef memcpy
#undef memset #undef memset
#undef memmove
__visible void *memcpy(void *to, const void *from, size_t n) __visible void *memcpy(void *to, const void *from, size_t n)
{ {
......
...@@ -100,6 +100,19 @@ ...@@ -100,6 +100,19 @@
# define __copy(symbol) # define __copy(symbol)
#endif #endif
/*
* Optional: not supported by gcc
* Optional: only supported since clang >= 14.0
* Optional: not supported by icc
*
* clang: https://clang.llvm.org/docs/AttributeReference.html#diagnose_as_builtin
*/
#if __has_attribute(__diagnose_as_builtin__)
# define __diagnose_as(builtin...) __attribute__((__diagnose_as_builtin__(builtin)))
#else
# define __diagnose_as(builtin...)
#endif
/* /*
* Don't. Just don't. See commit 771c035372a0 ("deprecate the '__deprecated' * Don't. Just don't. See commit 771c035372a0 ("deprecate the '__deprecated'
* attribute warnings entirely and for good") for more information. * attribute warnings entirely and for good") for more information.
...@@ -257,12 +270,38 @@ ...@@ -257,12 +270,38 @@
*/ */
#define __noreturn __attribute__((__noreturn__)) #define __noreturn __attribute__((__noreturn__))
/*
* Optional: not supported by gcc.
* Optional: not supported by icc.
*
* clang: https://clang.llvm.org/docs/AttributeReference.html#overloadable
*/
#if __has_attribute(__overloadable__)
# define __overloadable __attribute__((__overloadable__))
#else
# define __overloadable
#endif
/* /*
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-packed-type-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-packed-type-attribute
* clang: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-packed-variable-attribute * clang: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-packed-variable-attribute
*/ */
#define __packed __attribute__((__packed__)) #define __packed __attribute__((__packed__))
/*
* Note: the "type" argument should match any __builtin_object_size(p, type) usage.
*
* Optional: not supported by gcc.
* Optional: not supported by icc.
*
* clang: https://clang.llvm.org/docs/AttributeReference.html#pass-object-size-pass-dynamic-object-size
*/
#if __has_attribute(__pass_object_size__)
# define __pass_object_size(type) __attribute__((__pass_object_size__(type)))
#else
# define __pass_object_size(type)
#endif
/* /*
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute
*/ */
......
...@@ -2,13 +2,17 @@ ...@@ -2,13 +2,17 @@
#ifndef _LINUX_FORTIFY_STRING_H_ #ifndef _LINUX_FORTIFY_STRING_H_
#define _LINUX_FORTIFY_STRING_H_ #define _LINUX_FORTIFY_STRING_H_
#define __FORTIFY_INLINE extern __always_inline __attribute__((gnu_inline)) #include <linux/const.h>
#define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable
#define __RENAME(x) __asm__(#x) #define __RENAME(x) __asm__(#x)
void fortify_panic(const char *name) __noreturn __cold; void fortify_panic(const char *name) __noreturn __cold;
void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)"); void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)");
void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)"); void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)");
void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("detected read beyond size of field (2nd parameter); maybe use struct_group()?");
void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)"); void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)");
void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("detected write beyond size of field (1st parameter); maybe use struct_group()?");
#define __compiletime_strlen(p) \ #define __compiletime_strlen(p) \
({ \ ({ \
...@@ -48,7 +52,17 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) ...@@ -48,7 +52,17 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size)
#define __underlying_strncpy __builtin_strncpy #define __underlying_strncpy __builtin_strncpy
#endif #endif
__FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size) /*
* Clang's use of __builtin_object_size() within inlines needs hinting via
* __pass_object_size(). The preference is to only ever use type 1 (member
* size, rather than struct size), but there remain some stragglers using
* type 0 that will be converted in the future.
*/
#define POS __pass_object_size(1)
#define POS0 __pass_object_size(0)
__FORTIFY_INLINE __diagnose_as(__builtin_strncpy, 1, 2, 3)
char *strncpy(char * const POS p, const char *q, __kernel_size_t size)
{ {
size_t p_size = __builtin_object_size(p, 1); size_t p_size = __builtin_object_size(p, 1);
...@@ -59,7 +73,8 @@ __FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size) ...@@ -59,7 +73,8 @@ __FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size)
return __underlying_strncpy(p, q, size); return __underlying_strncpy(p, q, size);
} }
__FORTIFY_INLINE char *strcat(char *p, const char *q) __FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2)
char *strcat(char * const POS p, const char *q)
{ {
size_t p_size = __builtin_object_size(p, 1); size_t p_size = __builtin_object_size(p, 1);
...@@ -71,7 +86,7 @@ __FORTIFY_INLINE char *strcat(char *p, const char *q) ...@@ -71,7 +86,7 @@ __FORTIFY_INLINE char *strcat(char *p, const char *q)
} }
extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen); extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen);
__FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size_t maxlen)
{ {
size_t p_size = __builtin_object_size(p, 1); size_t p_size = __builtin_object_size(p, 1);
size_t p_len = __compiletime_strlen(p); size_t p_len = __compiletime_strlen(p);
...@@ -91,8 +106,16 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) ...@@ -91,8 +106,16 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen)
return ret; return ret;
} }
/* defined after fortified strnlen to reuse it. */ /*
__FORTIFY_INLINE __kernel_size_t strlen(const char *p) * Defined after fortified strnlen to reuse it. However, it must still be
* possible for strlen() to be used on compile-time strings for use in
* static initializers (i.e. as a constant expression).
*/
#define strlen(p) \
__builtin_choose_expr(__is_constexpr(__builtin_strlen(p)), \
__builtin_strlen(p), __fortify_strlen(p))
__FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1)
__kernel_size_t __fortify_strlen(const char * const POS p)
{ {
__kernel_size_t ret; __kernel_size_t ret;
size_t p_size = __builtin_object_size(p, 1); size_t p_size = __builtin_object_size(p, 1);
...@@ -108,7 +131,7 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p) ...@@ -108,7 +131,7 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p)
/* defined after fortified strlen to reuse it */ /* defined after fortified strlen to reuse it */
extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy); extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy);
__FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) __FORTIFY_INLINE size_t strlcpy(char * const POS p, const char * const POS q, size_t size)
{ {
size_t p_size = __builtin_object_size(p, 1); size_t p_size = __builtin_object_size(p, 1);
size_t q_size = __builtin_object_size(q, 1); size_t q_size = __builtin_object_size(q, 1);
...@@ -135,7 +158,7 @@ __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) ...@@ -135,7 +158,7 @@ __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size)
/* defined after fortified strnlen to reuse it */ /* defined after fortified strnlen to reuse it */
extern ssize_t __real_strscpy(char *, const char *, size_t) __RENAME(strscpy); extern ssize_t __real_strscpy(char *, const char *, size_t) __RENAME(strscpy);
__FORTIFY_INLINE ssize_t strscpy(char *p, const char *q, size_t size) __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, size_t size)
{ {
size_t len; size_t len;
/* Use string size rather than possible enclosing struct size. */ /* Use string size rather than possible enclosing struct size. */
...@@ -181,7 +204,8 @@ __FORTIFY_INLINE ssize_t strscpy(char *p, const char *q, size_t size) ...@@ -181,7 +204,8 @@ __FORTIFY_INLINE ssize_t strscpy(char *p, const char *q, size_t size)
} }
/* defined after fortified strlen and strnlen to reuse them */ /* defined after fortified strlen and strnlen to reuse them */
__FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count) __FORTIFY_INLINE __diagnose_as(__builtin_strncat, 1, 2, 3)
char *strncat(char * const POS p, const char * const POS q, __kernel_size_t count)
{ {
size_t p_len, copy_len; size_t p_len, copy_len;
size_t p_size = __builtin_object_size(p, 1); size_t p_size = __builtin_object_size(p, 1);
...@@ -198,51 +222,161 @@ __FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count) ...@@ -198,51 +222,161 @@ __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__);
return __underlying_memset(p, c, size);
}
__FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size)
{
size_t p_size = __builtin_object_size(p, 0);
size_t q_size = __builtin_object_size(q, 0);
if (__builtin_constant_p(size)) { /* Warn when write size is larger than dest field. */
if (p_size < size) if (p_size_field < size)
__write_overflow(); __write_overflow_field(p_size_field, size);
if (q_size < size)
__read_overflow2();
} }
if (p_size < size || q_size < size) /*
fortify_panic(__func__); * At this point, length argument may not be a constant expression,
return __underlying_memcpy(p, q, size); * 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");
} }
__FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size) #define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({ \
{ size_t __fortify_size = (size_t)(size); \
size_t p_size = __builtin_object_size(p, 0); fortify_memset_chk(__fortify_size, p_size, p_size_field), \
size_t q_size = __builtin_object_size(q, 0); __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
* struct members. If you need to copy across multiple members, please use
* struct_group() to create a named mirror of an anonymous struct union.
* (e.g. see struct sk_buff.) Read overflow checking is currently only
* done when a write overflow is also present, or when building with W=1.
*
* Mitigation coverage matrix
* Bounds checking at:
* +-------+-------+-------+-------+
* | Compile time | Run time |
* memcpy() argument sizes: | write | read | write | read |
* dest source length +-------+-------+-------+-------+
* memcpy(known, known, constant) | y | y | n/a | n/a |
* memcpy(known, unknown, constant) | y | n | n/a | V |
* memcpy(known, known, dynamic) | n | n | B | B |
* memcpy(known, unknown, dynamic) | n | n | B | V |
* memcpy(unknown, known, constant) | n | y | V | n/a |
* memcpy(unknown, unknown, constant) | n | n | V | V |
* memcpy(unknown, known, dynamic) | n | n | V | B |
* memcpy(unknown, unknown, dynamic) | n | n | V | V |
* +-------+-------+-------+-------+
*
* y = perform deterministic compile-time bounds checking
* n = cannot perform deterministic compile-time bounds checking
* n/a = no run-time bounds checking needed since compile-time deterministic
* B = can perform run-time bounds checking (currently unimplemented)
* V = vulnerable to run-time overflow (will need refactoring to solve)
*
*/
__FORTIFY_INLINE void fortify_memcpy_chk(__kernel_size_t size,
const size_t p_size,
const size_t q_size,
const size_t p_size_field,
const size_t q_size_field,
const char *func)
{
if (__builtin_constant_p(size)) { if (__builtin_constant_p(size)) {
if (p_size < size) /*
* Length argument is a constant expression, so we
* can perform compile-time bounds checking where
* buffer sizes are known.
*/
/* Error when size is larger than enclosing struct. */
if (p_size > p_size_field && p_size < size)
__write_overflow(); __write_overflow();
if (q_size < size) if (q_size > q_size_field && q_size < size)
__read_overflow2(); __read_overflow2();
/* Warn when write size argument larger than dest field. */
if (p_size_field < size)
__write_overflow_field(p_size_field, size);
/*
* Warn for source field over-read when building with W=1
* or when an over-write happened, so both can be fixed at
* the same time.
*/
if ((IS_ENABLED(KBUILD_EXTRA_WARN1) || p_size_field < size) &&
q_size_field < size)
__read_overflow2_field(q_size_field, size);
} }
if (p_size < size || q_size < size) /*
fortify_panic(__func__); * At this point, length argument may not be a constant expression,
return __underlying_memmove(p, q, size); * 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) ||
(q_size != (size_t)(-1) && q_size < size))
fortify_panic(func);
} }
#define __fortify_memcpy_chk(p, q, size, p_size, q_size, \
p_size_field, q_size_field, op) ({ \
size_t __fortify_size = (size_t)(size); \
fortify_memcpy_chk(__fortify_size, p_size, q_size, \
p_size_field, q_size_field, #op); \
__underlying_##op(p, q, __fortify_size); \
})
/*
* __builtin_object_size() must be captured here to avoid evaluating argument
* side-effects further into the macro layers.
*/
#define memcpy(p, q, s) __fortify_memcpy_chk(p, q, s, \
__builtin_object_size(p, 0), __builtin_object_size(q, 0), \
__builtin_object_size(p, 1), __builtin_object_size(q, 1), \
memcpy)
#define memmove(p, q, s) __fortify_memcpy_chk(p, q, s, \
__builtin_object_size(p, 0), __builtin_object_size(q, 0), \
__builtin_object_size(p, 1), __builtin_object_size(q, 1), \
memmove)
extern void *__real_memscan(void *, int, __kernel_size_t) __RENAME(memscan); extern void *__real_memscan(void *, int, __kernel_size_t) __RENAME(memscan);
__FORTIFY_INLINE void *memscan(void *p, int c, __kernel_size_t size) __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size)
{ {
size_t p_size = __builtin_object_size(p, 0); size_t p_size = __builtin_object_size(p, 0);
...@@ -253,7 +387,8 @@ __FORTIFY_INLINE void *memscan(void *p, int c, __kernel_size_t size) ...@@ -253,7 +387,8 @@ __FORTIFY_INLINE void *memscan(void *p, int c, __kernel_size_t size)
return __real_memscan(p, c, size); return __real_memscan(p, c, size);
} }
__FORTIFY_INLINE int memcmp(const void *p, const void *q, __kernel_size_t size) __FORTIFY_INLINE __diagnose_as(__builtin_memcmp, 1, 2, 3)
int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t size)
{ {
size_t p_size = __builtin_object_size(p, 0); size_t p_size = __builtin_object_size(p, 0);
size_t q_size = __builtin_object_size(q, 0); size_t q_size = __builtin_object_size(q, 0);
...@@ -269,7 +404,8 @@ __FORTIFY_INLINE int memcmp(const void *p, const void *q, __kernel_size_t size) ...@@ -269,7 +404,8 @@ __FORTIFY_INLINE int memcmp(const void *p, const void *q, __kernel_size_t size)
return __underlying_memcmp(p, q, size); return __underlying_memcmp(p, q, size);
} }
__FORTIFY_INLINE void *memchr(const void *p, int c, __kernel_size_t size) __FORTIFY_INLINE __diagnose_as(__builtin_memchr, 1, 2, 3)
void *memchr(const void * const POS0 p, int c, __kernel_size_t size)
{ {
size_t p_size = __builtin_object_size(p, 0); size_t p_size = __builtin_object_size(p, 0);
...@@ -281,7 +417,7 @@ __FORTIFY_INLINE void *memchr(const void *p, int c, __kernel_size_t size) ...@@ -281,7 +417,7 @@ __FORTIFY_INLINE void *memchr(const void *p, int c, __kernel_size_t size)
} }
void *__real_memchr_inv(const void *s, int c, size_t n) __RENAME(memchr_inv); void *__real_memchr_inv(const void *s, int c, size_t n) __RENAME(memchr_inv);
__FORTIFY_INLINE void *memchr_inv(const void *p, int c, size_t size) __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size)
{ {
size_t p_size = __builtin_object_size(p, 0); size_t p_size = __builtin_object_size(p, 0);
...@@ -293,7 +429,7 @@ __FORTIFY_INLINE void *memchr_inv(const void *p, int c, size_t size) ...@@ -293,7 +429,7 @@ __FORTIFY_INLINE void *memchr_inv(const void *p, int c, size_t size)
} }
extern void *__real_kmemdup(const void *src, size_t len, gfp_t gfp) __RENAME(kmemdup); extern void *__real_kmemdup(const void *src, size_t len, gfp_t gfp) __RENAME(kmemdup);
__FORTIFY_INLINE void *kmemdup(const void *p, size_t size, gfp_t gfp) __FORTIFY_INLINE void *kmemdup(const void * const POS0 p, size_t size, gfp_t gfp)
{ {
size_t p_size = __builtin_object_size(p, 0); size_t p_size = __builtin_object_size(p, 0);
...@@ -304,13 +440,15 @@ __FORTIFY_INLINE void *kmemdup(const void *p, size_t size, gfp_t gfp) ...@@ -304,13 +440,15 @@ __FORTIFY_INLINE void *kmemdup(const void *p, size_t size, gfp_t gfp)
return __real_kmemdup(p, size, gfp); return __real_kmemdup(p, size, gfp);
} }
/* defined after fortified strlen and memcpy to reuse them */ /* Defined after fortified strlen to reuse it. */
__FORTIFY_INLINE char *strcpy(char *p, const char *q) __FORTIFY_INLINE __diagnose_as(__builtin_strcpy, 1, 2)
char *strcpy(char * const POS p, const char * const POS q)
{ {
size_t p_size = __builtin_object_size(p, 1); size_t p_size = __builtin_object_size(p, 1);
size_t q_size = __builtin_object_size(q, 1); size_t q_size = __builtin_object_size(q, 1);
size_t size; size_t size;
/* If neither buffer size is known, immediately give up. */
if (p_size == (size_t)-1 && q_size == (size_t)-1) if (p_size == (size_t)-1 && q_size == (size_t)-1)
return __underlying_strcpy(p, q); return __underlying_strcpy(p, q);
size = strlen(q) + 1; size = strlen(q) + 1;
...@@ -320,20 +458,20 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q) ...@@ -320,20 +458,20 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q)
/* Run-time check for dynamic size overflow. */ /* Run-time check for dynamic size overflow. */
if (p_size < size) if (p_size < size)
fortify_panic(__func__); fortify_panic(__func__);
memcpy(p, q, size); __underlying_memcpy(p, q, size);
return p; return p;
} }
/* 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_memcpy
#undef __underlying_memmove
#undef __underlying_memset
#undef __underlying_strcat #undef __underlying_strcat
#undef __underlying_strcpy #undef __underlying_strcpy
#undef __underlying_strlen #undef __underlying_strlen
#undef __underlying_strncat #undef __underlying_strncat
#undef __underlying_strncpy #undef __underlying_strncpy
#undef POS
#undef POS0
#endif /* _LINUX_FORTIFY_STRING_H_ */ #endif /* _LINUX_FORTIFY_STRING_H_ */
...@@ -377,7 +377,8 @@ TEST_FORTIFY_LOG = test_fortify.log ...@@ -377,7 +377,8 @@ TEST_FORTIFY_LOG = test_fortify.log
quiet_cmd_test_fortify = TEST $@ quiet_cmd_test_fortify = TEST $@
cmd_test_fortify = $(CONFIG_SHELL) $(srctree)/scripts/test_fortify.sh \ cmd_test_fortify = $(CONFIG_SHELL) $(srctree)/scripts/test_fortify.sh \
$< $@ "$(NM)" $(CC) $(c_flags) \ $< $@ "$(NM)" $(CC) $(c_flags) \
$(call cc-disable-warning,fortify-source) $(call cc-disable-warning,fortify-source) \
-DKBUILD_EXTRA_WARN1
targets += $(TEST_FORTIFY_LOGS) targets += $(TEST_FORTIFY_LOGS)
clean-files += $(TEST_FORTIFY_LOGS) clean-files += $(TEST_FORTIFY_LOGS)
......
...@@ -968,6 +968,12 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, ...@@ -968,6 +968,12 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
EXPORT_SYMBOL(memcpy_and_pad); EXPORT_SYMBOL(memcpy_and_pad);
#ifdef CONFIG_FORTIFY_SOURCE #ifdef CONFIG_FORTIFY_SOURCE
/* These are placeholders for fortify compile-time warnings. */
void __read_overflow2_field(size_t avail, size_t wanted) { }
EXPORT_SYMBOL(__read_overflow2_field);
void __write_overflow_field(size_t avail, size_t wanted) { }
EXPORT_SYMBOL(__write_overflow_field);
void fortify_panic(const char *name) void fortify_panic(const char *name)
{ {
pr_emerg("detected buffer overflow in %s\n", name); pr_emerg("detected buffer overflow in %s\n", name);
......
// SPDX-License-Identifier: GPL-2.0-only
#define TEST \
memcpy(large, instance.buf, sizeof(instance.buf) + 1)
#include "test_fortify.h"
// SPDX-License-Identifier: GPL-2.0-only
#define TEST \
memmove(large, instance.buf, sizeof(instance.buf) + 1)
#include "test_fortify.h"
// SPDX-License-Identifier: GPL-2.0-only
#define TEST \
memcpy(instance.buf, large, sizeof(instance.buf) + 1)
#include "test_fortify.h"
// SPDX-License-Identifier: GPL-2.0-only
#define TEST \
memmove(instance.buf, large, sizeof(instance.buf) + 1)
#include "test_fortify.h"
// SPDX-License-Identifier: GPL-2.0-only
#define TEST \
memset(instance.buf, 0x42, sizeof(instance.buf) + 1)
#include "test_fortify.h"
...@@ -46,8 +46,12 @@ if "$@" -Werror -c "$IN" -o "$OUT".o 2> "$TMP" ; then ...@@ -46,8 +46,12 @@ if "$@" -Werror -c "$IN" -o "$OUT".o 2> "$TMP" ; then
status="warning: unsafe ${FUNC}() usage lacked '$WANT' symbol in $IN" status="warning: unsafe ${FUNC}() usage lacked '$WANT' symbol in $IN"
fi fi
else else
# If the build failed, check for the warning in the stderr (gcc). # If the build failed, check for the warning in the stderr.
if ! grep -q -m1 "error: call to .\b${WANT}\b." "$TMP" ; then # GCC:
# ./include/linux/fortify-string.h:316:25: error: call to '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror=attribute-warning]
# Clang 14:
# ./include/linux/fortify-string.h:316:4: error: call to __write_overflow_field declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror,-Wattribute-warning]
if ! grep -Eq -m1 "error: call to .?\b${WANT}\b.?" "$TMP" ; then
status="warning: unsafe ${FUNC}() usage lacked '$WANT' warning in $IN" status="warning: unsafe ${FUNC}() usage lacked '$WANT' warning in $IN"
fi fi
fi fi
......
...@@ -177,9 +177,10 @@ config HARDENED_USERCOPY_PAGESPAN ...@@ -177,9 +177,10 @@ config HARDENED_USERCOPY_PAGESPAN
config FORTIFY_SOURCE config FORTIFY_SOURCE
bool "Harden common str/mem functions against buffer overflows" bool "Harden common str/mem functions against buffer overflows"
depends on ARCH_HAS_FORTIFY_SOURCE depends on ARCH_HAS_FORTIFY_SOURCE
# https://bugs.llvm.org/show_bug.cgi?id=50322
# https://bugs.llvm.org/show_bug.cgi?id=41459 # https://bugs.llvm.org/show_bug.cgi?id=41459
depends on !CC_IS_CLANG depends on !CC_IS_CLANG || CLANG_VERSION >= 120001
# https://github.com/llvm/llvm-project/issues/53645
depends on !CC_IS_CLANG || !X86_32
help help
Detect overflows of buffers in common string and memory functions Detect overflows of buffers in common string and memory functions
where the compiler can determine and validate the buffer sizes. where the compiler can determine and validate the buffer sizes.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment