fortify: Add Clang support

Enable FORTIFY_SOURCE support for Clang:

Use the new __pass_object_size and __overloadable attributes so that
Clang will have appropriate visibility into argument sizes such that
__builtin_object_size(p, 1) will behave correctly. Additional details
available here:
    https://github.com/llvm/llvm-project/issues/53516
    https://github.com/ClangBuiltLinux/linux/issues/1401

A bug with __builtin_constant_p() of globally defined variables was
fixed in Clang 13 (and backported to 12.0.1), so FORTIFY support must
depend on that version or later. Additional details here:
    https://bugs.llvm.org/show_bug.cgi?id=41459
    commit a52f8a59ae ("fortify: Explicitly disable Clang support")

A bug with Clang's -mregparm=3 and -m32 makes some builtins unusable,
so removing -ffreestanding (to gain the needed libcall optimizations
with Clang) cannot be done. Without the libcall optimizations, Clang
cannot provide appropriate FORTIFY coverage, so it must be disabled
for CONFIG_X86_32. Additional details here;
    https://github.com/llvm/llvm-project/issues/53645

Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: George Burgess IV <gbiv@google.com>
Cc: llvm@lists.linux.dev
Signed-off-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
Link: https://lore.kernel.org/r/20220208225350.1331628-9-keescook@chromium.org
This commit is contained in:
Kees Cook 2022-02-08 14:53:50 -08:00
parent 67ebc3ab44
commit 281d0c9627
2 changed files with 29 additions and 16 deletions

View File

@ -4,7 +4,7 @@
#include <linux/const.h> #include <linux/const.h>
#define __FORTIFY_INLINE extern __always_inline __gnu_inline #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;
@ -52,8 +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
/*
* 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) __FORTIFY_INLINE __diagnose_as(__builtin_strncpy, 1, 2, 3)
char *strncpy(char * const p, const char *q, __kernel_size_t size) 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);
@ -65,7 +74,7 @@ char *strncpy(char * const p, const char *q, __kernel_size_t size)
} }
__FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2) __FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2)
char *strcat(char * const p, const char *q) 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);
@ -77,7 +86,7 @@ char *strcat(char * const 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 * const 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);
@ -106,7 +115,7 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const p, __kernel_size_t m
__builtin_choose_expr(__is_constexpr(__builtin_strlen(p)), \ __builtin_choose_expr(__is_constexpr(__builtin_strlen(p)), \
__builtin_strlen(p), __fortify_strlen(p)) __builtin_strlen(p), __fortify_strlen(p))
__FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1) __FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1)
__kernel_size_t __fortify_strlen(const char * const p) __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);
@ -122,7 +131,7 @@ __kernel_size_t __fortify_strlen(const char * const 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 * const p, const char * const 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);
@ -149,7 +158,7 @@ __FORTIFY_INLINE size_t strlcpy(char * const p, const char * const q, size_t siz
/* 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 * const p, const char * const 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. */
@ -196,7 +205,7 @@ __FORTIFY_INLINE ssize_t strscpy(char * const p, const char * const q, size_t si
/* defined after fortified strlen and strnlen to reuse them */ /* defined after fortified strlen and strnlen to reuse them */
__FORTIFY_INLINE __diagnose_as(__builtin_strncat, 1, 2, 3) __FORTIFY_INLINE __diagnose_as(__builtin_strncat, 1, 2, 3)
char *strncat(char * const p, const char * const q, __kernel_size_t count) 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);
@ -367,7 +376,7 @@ __FORTIFY_INLINE void fortify_memcpy_chk(__kernel_size_t size,
memmove) 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 * const 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);
@ -379,7 +388,7 @@ __FORTIFY_INLINE void *memscan(void * const p, int c, __kernel_size_t size)
} }
__FORTIFY_INLINE __diagnose_as(__builtin_memcmp, 1, 2, 3) __FORTIFY_INLINE __diagnose_as(__builtin_memcmp, 1, 2, 3)
int memcmp(const void * const p, const void * const q, __kernel_size_t size) 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);
@ -396,7 +405,7 @@ int memcmp(const void * const p, const void * const q, __kernel_size_t size)
} }
__FORTIFY_INLINE __diagnose_as(__builtin_memchr, 1, 2, 3) __FORTIFY_INLINE __diagnose_as(__builtin_memchr, 1, 2, 3)
void *memchr(const void * const p, int c, __kernel_size_t size) 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);
@ -408,7 +417,7 @@ void *memchr(const void * const 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 * const 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);
@ -420,7 +429,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const 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 * const 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);
@ -433,7 +442,7 @@ __FORTIFY_INLINE void *kmemdup(const void * const p, size_t size, gfp_t gfp)
/* Defined after fortified strlen to reuse it. */ /* Defined after fortified strlen to reuse it. */
__FORTIFY_INLINE __diagnose_as(__builtin_strcpy, 1, 2) __FORTIFY_INLINE __diagnose_as(__builtin_strcpy, 1, 2)
char *strcpy(char * const p, const char * const q) 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);
@ -462,4 +471,7 @@ char *strcpy(char * const p, const char * const q)
#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_ */

View File

@ -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.