mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-24 17:23:25 -05:00
memcpy updates for v5.18-rc1
- Enable strict FORTIFY_SOURCE compile-time validation of memcpy buffers - Add Clang features needed for FORTIFY_SOURCE support - Enable FORTIFY_SOURCE for Clang where possible -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmI+NxwWHGtlZXNjb29r QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJhnPEACI1AUB9OHzL+VbLhX6zzvPuFRm 7MC11PWyPTa4tkhKGTlVvYbHKwrfcJyAG85rKpz5euWVlzVFkifouT4YAG959CYK OGUj9WXPRpQ3IIPXXazZOtds4T5sP/m6dSts2NaRIX4w0NKOo3p2mlxUaYoagH1Z j178epRJ+lbUwPdBmGsSGceb5qDKqubz/sXh51lY3YoLdMZGiom6FLva4STenzZq SBEJqD2AM0tPWSkrue4OCRig7IsiLhzLvP8jC303suLLHn3eVTvoIT+RRBvwFqXo MX9B6i3DdCjbWoOg9gA0Jhc6+2+kP7MU1MO6WfWP6IVZh2V1pk4Avmgxy6ypxfwU fMNqH7CrFmojKOWqF55/1zfrQNNLqnHD3HiDAHpCtATN8kpcZGZXMUb3kT4FIij1 2Mcf6mBQOSqZTg4OvgKzPWGZYJe3KJp5lup5zhWmcOSV0o2gNhFCwXHEmhlNRLzw idnbghjqBE74UcThQQjyWNBldzdPWVAjgaD696CnziRDCtHiTsrQaIrRsjx9P8NX 3GpoIp0vqDFG4SjFkuGishmlyMWXb3B2Ij7s2WCCSYRHLgOUJQgkhkw5wNZ7F2zD qjEXaRZXecG5W/gwA4Ak9I2o6oKaK5HPMhNxYp7mlbceYcnuw9gSqeqRAgqX9LJA kg7orn733jgfMrGhHw== =8qRJ -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
4be240b18a
13 changed files with 272 additions and 56 deletions
|
@ -37,10 +37,11 @@
|
|||
* try to define their own functions if these are not defined as macros.
|
||||
*/
|
||||
#define memzero(s, n) memset((s), 0, (n))
|
||||
#ifndef memmove
|
||||
#define memmove memmove
|
||||
|
||||
/* Functions used by the included decompressor code below. */
|
||||
void *memmove(void *dest, const void *src, size_t n);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is set up by the setup-routine at boot-time
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#undef memcpy
|
||||
#undef memset
|
||||
#undef memmove
|
||||
|
||||
__visible void *memcpy(void *to, const void *from, size_t n)
|
||||
{
|
||||
|
|
|
@ -100,6 +100,19 @@
|
|||
# define __copy(symbol)
|
||||
#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'
|
||||
* attribute warnings entirely and for good") for more information.
|
||||
|
@ -257,12 +270,38 @@
|
|||
*/
|
||||
#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
|
||||
* clang: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-packed-variable-attribute
|
||||
*/
|
||||
#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
|
||||
*/
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
#ifndef _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)
|
||||
|
||||
void fortify_panic(const char *name) __noreturn __cold;
|
||||
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_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_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) \
|
||||
({ \
|
||||
|
@ -48,7 +52,17 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size)
|
|||
#define __underlying_strncpy __builtin_strncpy
|
||||
#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);
|
||||
|
||||
|
@ -59,7 +73,8 @@ __FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t 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);
|
||||
|
||||
|
@ -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);
|
||||
__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_len = __compiletime_strlen(p);
|
||||
|
@ -91,8 +106,16 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen)
|
|||
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;
|
||||
size_t p_size = __builtin_object_size(p, 1);
|
||||
|
@ -108,7 +131,7 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p)
|
|||
|
||||
/* defined after fortified strlen to reuse it */
|
||||
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 q_size = __builtin_object_size(q, 1);
|
||||
|
@ -135,7 +158,7 @@ __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size)
|
|||
|
||||
/* defined after fortified strnlen to reuse it */
|
||||
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;
|
||||
/* 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)
|
|||
}
|
||||
|
||||
/* 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_size = __builtin_object_size(p, 1);
|
||||
|
@ -198,51 +222,161 @@ __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) && p_size < size)
|
||||
__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)) {
|
||||
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();
|
||||
if (q_size < size)
|
||||
__read_overflow2();
|
||||
|
||||
/* Warn when write size is larger than dest field. */
|
||||
if (p_size_field < size)
|
||||
__write_overflow_field(p_size_field, size);
|
||||
}
|
||||
if (p_size < size || q_size < size)
|
||||
fortify_panic(__func__);
|
||||
return __underlying_memcpy(p, q, 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");
|
||||
}
|
||||
|
||||
__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); \
|
||||
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
|
||||
* 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)
|
||||
{
|
||||
size_t p_size = __builtin_object_size(p, 0);
|
||||
size_t q_size = __builtin_object_size(q, 0);
|
||||
|
||||
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();
|
||||
if (q_size < size)
|
||||
if (q_size > q_size_field && q_size < size)
|
||||
__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__);
|
||||
return __underlying_memmove(p, q, 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) ||
|
||||
(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);
|
||||
__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);
|
||||
|
||||
|
@ -253,7 +387,8 @@ __FORTIFY_INLINE void *memscan(void *p, int c, __kernel_size_t 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 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)
|
|||
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);
|
||||
|
||||
|
@ -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);
|
||||
__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);
|
||||
|
||||
|
@ -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);
|
||||
__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);
|
||||
|
||||
|
@ -304,13 +440,15 @@ __FORTIFY_INLINE void *kmemdup(const void *p, size_t size, gfp_t gfp)
|
|||
return __real_kmemdup(p, size, gfp);
|
||||
}
|
||||
|
||||
/* defined after fortified strlen and memcpy to reuse them */
|
||||
__FORTIFY_INLINE char *strcpy(char *p, const char *q)
|
||||
/* Defined after fortified strlen to reuse it. */
|
||||
__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 q_size = __builtin_object_size(q, 1);
|
||||
size_t size;
|
||||
|
||||
/* If neither buffer size is known, immediately give up. */
|
||||
if (p_size == (size_t)-1 && q_size == (size_t)-1)
|
||||
return __underlying_strcpy(p, q);
|
||||
size = strlen(q) + 1;
|
||||
|
@ -320,20 +458,20 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q)
|
|||
/* Run-time check for dynamic size overflow. */
|
||||
if (p_size < size)
|
||||
fortify_panic(__func__);
|
||||
memcpy(p, q, size);
|
||||
__underlying_memcpy(p, q, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Don't use these outside the FORITFY_SOURCE implementation */
|
||||
#undef __underlying_memchr
|
||||
#undef __underlying_memcmp
|
||||
#undef __underlying_memcpy
|
||||
#undef __underlying_memmove
|
||||
#undef __underlying_memset
|
||||
#undef __underlying_strcat
|
||||
#undef __underlying_strcpy
|
||||
#undef __underlying_strlen
|
||||
#undef __underlying_strncat
|
||||
#undef __underlying_strncpy
|
||||
|
||||
#undef POS
|
||||
#undef POS0
|
||||
|
||||
#endif /* _LINUX_FORTIFY_STRING_H_ */
|
||||
|
|
|
@ -377,7 +377,8 @@ TEST_FORTIFY_LOG = test_fortify.log
|
|||
quiet_cmd_test_fortify = TEST $@
|
||||
cmd_test_fortify = $(CONFIG_SHELL) $(srctree)/scripts/test_fortify.sh \
|
||||
$< $@ "$(NM)" $(CC) $(c_flags) \
|
||||
$(call cc-disable-warning,fortify-source)
|
||||
$(call cc-disable-warning,fortify-source) \
|
||||
-DKBUILD_EXTRA_WARN1
|
||||
|
||||
targets += $(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,
|
|||
EXPORT_SYMBOL(memcpy_and_pad);
|
||||
|
||||
#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)
|
||||
{
|
||||
pr_emerg("detected buffer overflow in %s\n", name);
|
||||
|
|
5
lib/test_fortify/read_overflow2_field-memcpy.c
Normal file
5
lib/test_fortify/read_overflow2_field-memcpy.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define TEST \
|
||||
memcpy(large, instance.buf, sizeof(instance.buf) + 1)
|
||||
|
||||
#include "test_fortify.h"
|
5
lib/test_fortify/read_overflow2_field-memmove.c
Normal file
5
lib/test_fortify/read_overflow2_field-memmove.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define TEST \
|
||||
memmove(large, instance.buf, sizeof(instance.buf) + 1)
|
||||
|
||||
#include "test_fortify.h"
|
5
lib/test_fortify/write_overflow_field-memcpy.c
Normal file
5
lib/test_fortify/write_overflow_field-memcpy.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define TEST \
|
||||
memcpy(instance.buf, large, sizeof(instance.buf) + 1)
|
||||
|
||||
#include "test_fortify.h"
|
5
lib/test_fortify/write_overflow_field-memmove.c
Normal file
5
lib/test_fortify/write_overflow_field-memmove.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define TEST \
|
||||
memmove(instance.buf, large, sizeof(instance.buf) + 1)
|
||||
|
||||
#include "test_fortify.h"
|
5
lib/test_fortify/write_overflow_field-memset.c
Normal file
5
lib/test_fortify/write_overflow_field-memset.c
Normal file
|
@ -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"
|
|
@ -46,8 +46,12 @@ if "$@" -Werror -c "$IN" -o "$OUT".o 2> "$TMP" ; then
|
|||
status="warning: unsafe ${FUNC}() usage lacked '$WANT' symbol in $IN"
|
||||
fi
|
||||
else
|
||||
# If the build failed, check for the warning in the stderr (gcc).
|
||||
if ! grep -q -m1 "error: call to .\b${WANT}\b." "$TMP" ; then
|
||||
# If the build failed, check for the warning in the stderr.
|
||||
# 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"
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -177,9 +177,10 @@ config HARDENED_USERCOPY_PAGESPAN
|
|||
config FORTIFY_SOURCE
|
||||
bool "Harden common str/mem functions against buffer overflows"
|
||||
depends on ARCH_HAS_FORTIFY_SOURCE
|
||||
# https://bugs.llvm.org/show_bug.cgi?id=50322
|
||||
# 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
|
||||
Detect overflows of buffers in common string and memory functions
|
||||
where the compiler can determine and validate the buffer sizes.
|
||||
|
|
Loading…
Add table
Reference in a new issue