mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00
hardening updates for v6.15-rc1
- loadpin: remove unsupported MODULE_COMPRESS_NONE (Arulpandiyan Vadivel) - samples/check-exec: Fix script name (Mickaël Salaün) - yama: remove needless locking in yama_task_prctl() (Oleg Nesterov) - lib/string_choices: Sort by function name (R Sundar) - hardening: Allow default HARDENED_USERCOPY to be set at compile time (Mel Gorman) - uaccess: Split out compile-time checks into ucopysize.h - kbuild: clang: Support building UM with SUBARCH=i386 - x86: Enable i386 FORTIFY_SOURCE on Clang 16+ - ubsan/overflow: Rework integer overflow sanitizer option - Add missing __nonstring annotations for callers of memtostr*()/strtomem*() - Add __must_be_noncstr() and have memtostr*()/strtomem*() check for it - Introduce __nonstring_array for silencing future GCC 15 warnings -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRSPkdeREjth1dHnSE2KwveOeQkuwUCZ9hGrgAKCRA2KwveOeQk u1WvAQC3ZxFu3b0Omfmht2pPqCltf2UOQNvUx3egjoeXpUaNSgD+Lxr/T4xksy7E jHh7rCYDkruOWs3DHA5JjRQcf0BBLQo= =FTQp -----END PGP SIGNATURE----- Merge tag 'hardening-v6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux Pull hardening updates from Kees Cook: "As usual, it's scattered changes all over. Patches touching things outside of our traditional areas in the tree have been Acked by maintainers or were trivial changes: - loadpin: remove unsupported MODULE_COMPRESS_NONE (Arulpandiyan Vadivel) - samples/check-exec: Fix script name (Mickaël Salaün) - yama: remove needless locking in yama_task_prctl() (Oleg Nesterov) - lib/string_choices: Sort by function name (R Sundar) - hardening: Allow default HARDENED_USERCOPY to be set at compile time (Mel Gorman) - uaccess: Split out compile-time checks into ucopysize.h - kbuild: clang: Support building UM with SUBARCH=i386 - x86: Enable i386 FORTIFY_SOURCE on Clang 16+ - ubsan/overflow: Rework integer overflow sanitizer option - Add missing __nonstring annotations for callers of memtostr*()/strtomem*() - Add __must_be_noncstr() and have memtostr*()/strtomem*() check for it - Introduce __nonstring_array for silencing future GCC 15 warnings" * tag 'hardening-v6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (26 commits) compiler_types: Introduce __nonstring_array hardening: Enable i386 FORTIFY_SOURCE on Clang 16+ x86/build: Remove -ffreestanding on i386 with GCC ubsan/overflow: Enable ignorelist parsing and add type filter ubsan/overflow: Enable pattern exclusions ubsan/overflow: Rework integer overflow sanitizer option to turn on everything samples/check-exec: Fix script name yama: don't abuse rcu_read_lock/get_task_struct in yama_task_prctl() kbuild: clang: Support building UM with SUBARCH=i386 loadpin: remove MODULE_COMPRESS_NONE as it is no longer supported lib/string_choices: Rearrange functions in sorted order string.h: Validate memtostr*()/strtomem*() arguments more carefully compiler.h: Introduce __must_be_noncstr() nilfs2: Mark on-disk strings as nonstring uapi: stddef.h: Introduce __kernel_nonstring x86/tdx: Mark message.bytes as nonstring string: kunit: Mark nonstring test strings as __nonstring scsi: qla2xxx: Mark device strings as nonstring scsi: mpt3sas: Mark device strings as nonstring scsi: mpi3mr: Mark device strings as nonstring ...
This commit is contained in:
commit
fc13a78e1f
@ -1785,7 +1785,9 @@
|
||||
allocation boundaries as a proactive defense
|
||||
against bounds-checking flaws in the kernel's
|
||||
copy_to_user()/copy_from_user() interface.
|
||||
on Perform hardened usercopy checks (default).
|
||||
The default is determined by
|
||||
CONFIG_HARDENED_USERCOPY_DEFAULT_ON.
|
||||
on Perform hardened usercopy checks.
|
||||
off Disable hardened usercopy checks.
|
||||
|
||||
hardlockup_all_cpu_backtrace=
|
||||
|
@ -12591,6 +12591,7 @@ F: Documentation/ABI/testing/sysfs-kernel-warn_count
|
||||
F: arch/*/configs/hardening.config
|
||||
F: include/linux/overflow.h
|
||||
F: include/linux/randomize_kstack.h
|
||||
F: include/linux/ucopysize.h
|
||||
F: kernel/configs/hardening.config
|
||||
F: lib/tests/usercopy_kunit.c
|
||||
F: mm/usercopy.c
|
||||
|
@ -137,8 +137,10 @@ ifeq ($(CONFIG_X86_32),y)
|
||||
include $(srctree)/arch/x86/Makefile_32.cpu
|
||||
KBUILD_CFLAGS += $(cflags-y)
|
||||
|
||||
# temporary until string.h is fixed
|
||||
ifneq ($(call clang-min-version, 160000),y)
|
||||
# https://github.com/llvm/llvm-project/issues/53645
|
||||
KBUILD_CFLAGS += -ffreestanding
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_STACKPROTECTOR),y)
|
||||
ifeq ($(CONFIG_SMP),y)
|
||||
|
@ -167,11 +167,11 @@ static void __noreturn tdx_panic(const char *msg)
|
||||
/* Define register order according to the GHCI */
|
||||
struct { u64 r14, r15, rbx, rdi, rsi, r8, r9, rdx; };
|
||||
|
||||
char str[64];
|
||||
char bytes[64] __nonstring;
|
||||
} message;
|
||||
|
||||
/* VMM assumes '\0' in byte 65, if the message took all 64 bytes */
|
||||
strtomem_pad(message.str, msg, '\0');
|
||||
strtomem_pad(message.bytes, msg, '\0');
|
||||
|
||||
args.r8 = message.r8;
|
||||
args.r9 = message.r9;
|
||||
|
@ -2834,10 +2834,10 @@ struct rep_manu_reply{
|
||||
u8 sas_format:1;
|
||||
u8 reserved1:7;
|
||||
u8 reserved2[3];
|
||||
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
|
||||
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
|
||||
u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
|
||||
u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
|
||||
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] __nonstring;
|
||||
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN] __nonstring;
|
||||
u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] __nonstring;
|
||||
u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] __nonstring;
|
||||
u16 component_id;
|
||||
u8 component_revision_id;
|
||||
u8 reserved3;
|
||||
|
@ -105,10 +105,10 @@ struct rep_manu_reply {
|
||||
u8 reserved0[2];
|
||||
u8 sas_format;
|
||||
u8 reserved2[3];
|
||||
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
|
||||
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
|
||||
u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
|
||||
u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
|
||||
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] __nonstring;
|
||||
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN] __nonstring;
|
||||
u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] __nonstring;
|
||||
u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] __nonstring;
|
||||
u16 component_id;
|
||||
u8 component_revision_id;
|
||||
u8 reserved3;
|
||||
|
@ -606,7 +606,7 @@ typedef struct _MPI2_CONFIG_REPLY {
|
||||
|
||||
typedef struct _MPI2_CONFIG_PAGE_MAN_0 {
|
||||
MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
|
||||
U8 ChipName[16]; /*0x04 */
|
||||
U8 ChipName[16] __nonstring; /*0x04 */
|
||||
U8 ChipRevision[8]; /*0x14 */
|
||||
U8 BoardName[16]; /*0x1C */
|
||||
U8 BoardAssembly[16]; /*0x2C */
|
||||
|
@ -328,10 +328,10 @@ struct rep_manu_reply {
|
||||
u8 reserved0[2];
|
||||
u8 sas_format;
|
||||
u8 reserved2[3];
|
||||
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
|
||||
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
|
||||
u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
|
||||
u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
|
||||
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] __nonstring;
|
||||
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN] __nonstring;
|
||||
u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] __nonstring;
|
||||
u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] __nonstring;
|
||||
u16 component_id;
|
||||
u8 component_revision_id;
|
||||
u8 reserved3;
|
||||
|
@ -282,8 +282,8 @@ struct register_host_info {
|
||||
#define QLAFX00_TGT_NODE_LIST_SIZE (sizeof(uint32_t) * 32)
|
||||
|
||||
struct config_info_data {
|
||||
uint8_t model_num[16];
|
||||
uint8_t model_description[80];
|
||||
uint8_t model_num[16] __nonstring;
|
||||
uint8_t model_description[80] __nonstring;
|
||||
uint8_t reserved0[160];
|
||||
uint8_t symbolic_name[64];
|
||||
uint8_t serial_num[32];
|
||||
|
@ -206,9 +206,25 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
|
||||
#define __must_be_byte_array(a) __BUILD_BUG_ON_ZERO_MSG(!__is_byte_array(a), \
|
||||
"must be byte array")
|
||||
|
||||
/*
|
||||
* If the "nonstring" attribute isn't available, we have to return true
|
||||
* so the __must_*() checks pass when "nonstring" isn't supported.
|
||||
*/
|
||||
#if __has_attribute(__nonstring__) && defined(__annotated)
|
||||
#define __is_cstr(a) (!__annotated(a, nonstring))
|
||||
#define __is_noncstr(a) (__annotated(a, nonstring))
|
||||
#else
|
||||
#define __is_cstr(a) (true)
|
||||
#define __is_noncstr(a) (true)
|
||||
#endif
|
||||
|
||||
/* Require C Strings (i.e. NUL-terminated) lack the "nonstring" attribute. */
|
||||
#define __must_be_cstr(p) \
|
||||
__BUILD_BUG_ON_ZERO_MSG(__annotated(p, nonstring), "must be cstr (NUL-terminated)")
|
||||
__BUILD_BUG_ON_ZERO_MSG(!__is_cstr(p), \
|
||||
"must be C-string (NUL-terminated)")
|
||||
#define __must_be_noncstr(p) \
|
||||
__BUILD_BUG_ON_ZERO_MSG(!__is_noncstr(p), \
|
||||
"must be non-C-string (not NUL-terminated)")
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
|
@ -348,6 +348,18 @@ struct ftrace_likely_data {
|
||||
# define __counted_by(member)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Optional: only supported since gcc >= 15
|
||||
* Optional: not supported by Clang
|
||||
*
|
||||
* gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178
|
||||
*/
|
||||
#ifdef CONFIG_CC_HAS_MULTIDIMENSIONAL_NONSTRING
|
||||
# define __nonstring_array __attribute__((__nonstring__))
|
||||
#else
|
||||
# define __nonstring_array
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Apply __counted_by() when the Endianness matches to increase test coverage.
|
||||
*/
|
||||
@ -360,7 +372,7 @@ struct ftrace_likely_data {
|
||||
#endif
|
||||
|
||||
/* Do not trap wrapping arithmetic within an annotated function. */
|
||||
#ifdef CONFIG_UBSAN_SIGNED_WRAP
|
||||
#ifdef CONFIG_UBSAN_INTEGER_WRAP
|
||||
# define __signed_wrap __attribute__((no_sanitize("signed-integer-overflow")))
|
||||
#else
|
||||
# define __signed_wrap
|
||||
@ -446,11 +458,14 @@ struct ftrace_likely_data {
|
||||
#define __member_size(p) __builtin_object_size(p, 1)
|
||||
#endif
|
||||
|
||||
/* Determine if an attribute has been applied to a variable. */
|
||||
/*
|
||||
* Determine if an attribute has been applied to a variable.
|
||||
* Using __annotated needs to check for __annotated being available,
|
||||
* or negative tests may fail when annotation cannot be checked. For
|
||||
* example, see the definition of __is_cstr().
|
||||
*/
|
||||
#if __has_builtin(__builtin_has_attribute)
|
||||
#define __annotated(var, attr) __builtin_has_attribute(var, attr)
|
||||
#else
|
||||
#define __annotated(var, attr) (false)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -415,8 +415,10 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
|
||||
*/
|
||||
#define strtomem_pad(dest, src, pad) do { \
|
||||
const size_t _dest_len = __must_be_byte_array(dest) + \
|
||||
__must_be_noncstr(dest) + \
|
||||
ARRAY_SIZE(dest); \
|
||||
const size_t _src_len = __builtin_object_size(src, 1); \
|
||||
const size_t _src_len = __must_be_cstr(src) + \
|
||||
__builtin_object_size(src, 1); \
|
||||
\
|
||||
BUILD_BUG_ON(!__builtin_constant_p(_dest_len) || \
|
||||
_dest_len == (size_t)-1); \
|
||||
@ -439,8 +441,10 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
|
||||
*/
|
||||
#define strtomem(dest, src) do { \
|
||||
const size_t _dest_len = __must_be_byte_array(dest) + \
|
||||
__must_be_noncstr(dest) + \
|
||||
ARRAY_SIZE(dest); \
|
||||
const size_t _src_len = __builtin_object_size(src, 1); \
|
||||
const size_t _src_len = __must_be_cstr(src) + \
|
||||
__builtin_object_size(src, 1); \
|
||||
\
|
||||
BUILD_BUG_ON(!__builtin_constant_p(_dest_len) || \
|
||||
_dest_len == (size_t)-1); \
|
||||
@ -459,8 +463,10 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
|
||||
*/
|
||||
#define memtostr(dest, src) do { \
|
||||
const size_t _dest_len = __must_be_byte_array(dest) + \
|
||||
__must_be_cstr(dest) + \
|
||||
ARRAY_SIZE(dest); \
|
||||
const size_t _src_len = __builtin_object_size(src, 1); \
|
||||
const size_t _src_len = __must_be_noncstr(src) + \
|
||||
__builtin_object_size(src, 1); \
|
||||
const size_t _src_chars = strnlen(src, _src_len); \
|
||||
const size_t _copy_len = min(_dest_len - 1, _src_chars); \
|
||||
\
|
||||
@ -485,8 +491,10 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
|
||||
*/
|
||||
#define memtostr_pad(dest, src) do { \
|
||||
const size_t _dest_len = __must_be_byte_array(dest) + \
|
||||
__must_be_cstr(dest) + \
|
||||
ARRAY_SIZE(dest); \
|
||||
const size_t _src_len = __builtin_object_size(src, 1); \
|
||||
const size_t _src_len = __must_be_noncstr(src) + \
|
||||
__builtin_object_size(src, 1); \
|
||||
const size_t _src_chars = strnlen(src, _src_len); \
|
||||
const size_t _copy_len = min(_dest_len - 1, _src_chars); \
|
||||
\
|
||||
|
@ -41,23 +41,23 @@ static inline const char *str_high_low(bool v)
|
||||
}
|
||||
#define str_low_high(v) str_high_low(!(v))
|
||||
|
||||
static inline const char *str_read_write(bool v)
|
||||
{
|
||||
return v ? "read" : "write";
|
||||
}
|
||||
#define str_write_read(v) str_read_write(!(v))
|
||||
|
||||
static inline const char *str_on_off(bool v)
|
||||
{
|
||||
return v ? "on" : "off";
|
||||
}
|
||||
#define str_off_on(v) str_on_off(!(v))
|
||||
|
||||
static inline const char *str_yes_no(bool v)
|
||||
static inline const char *str_read_write(bool v)
|
||||
{
|
||||
return v ? "yes" : "no";
|
||||
return v ? "read" : "write";
|
||||
}
|
||||
#define str_no_yes(v) str_yes_no(!(v))
|
||||
#define str_write_read(v) str_read_write(!(v))
|
||||
|
||||
static inline const char *str_true_false(bool v)
|
||||
{
|
||||
return v ? "true" : "false";
|
||||
}
|
||||
#define str_false_true(v) str_true_false(!(v))
|
||||
|
||||
static inline const char *str_up_down(bool v)
|
||||
{
|
||||
@ -65,11 +65,11 @@ static inline const char *str_up_down(bool v)
|
||||
}
|
||||
#define str_down_up(v) str_up_down(!(v))
|
||||
|
||||
static inline const char *str_true_false(bool v)
|
||||
static inline const char *str_yes_no(bool v)
|
||||
{
|
||||
return v ? "true" : "false";
|
||||
return v ? "yes" : "no";
|
||||
}
|
||||
#define str_false_true(v) str_true_false(!(v))
|
||||
#define str_no_yes(v) str_yes_no(!(v))
|
||||
|
||||
/**
|
||||
* str_plural - Return the simple pluralization based on English counts
|
||||
|
@ -217,54 +217,6 @@ static inline int arch_within_stack_frames(const void * const stack,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HARDENED_USERCOPY
|
||||
extern void __check_object_size(const void *ptr, unsigned long n,
|
||||
bool to_user);
|
||||
|
||||
static __always_inline void check_object_size(const void *ptr, unsigned long n,
|
||||
bool to_user)
|
||||
{
|
||||
if (!__builtin_constant_p(n))
|
||||
__check_object_size(ptr, n, to_user);
|
||||
}
|
||||
#else
|
||||
static inline void check_object_size(const void *ptr, unsigned long n,
|
||||
bool to_user)
|
||||
{ }
|
||||
#endif /* CONFIG_HARDENED_USERCOPY */
|
||||
|
||||
extern void __compiletime_error("copy source size is too small")
|
||||
__bad_copy_from(void);
|
||||
extern void __compiletime_error("copy destination size is too small")
|
||||
__bad_copy_to(void);
|
||||
|
||||
void __copy_overflow(int size, unsigned long count);
|
||||
|
||||
static inline void copy_overflow(int size, unsigned long count)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_BUG))
|
||||
__copy_overflow(size, count);
|
||||
}
|
||||
|
||||
static __always_inline __must_check bool
|
||||
check_copy_size(const void *addr, size_t bytes, bool is_source)
|
||||
{
|
||||
int sz = __builtin_object_size(addr, 0);
|
||||
if (unlikely(sz >= 0 && sz < bytes)) {
|
||||
if (!__builtin_constant_p(bytes))
|
||||
copy_overflow(sz, bytes);
|
||||
else if (is_source)
|
||||
__bad_copy_from();
|
||||
else
|
||||
__bad_copy_to();
|
||||
return false;
|
||||
}
|
||||
if (WARN_ON_ONCE(bytes > INT_MAX))
|
||||
return false;
|
||||
check_object_size(addr, bytes, is_source);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef arch_setup_new_exec
|
||||
static inline void arch_setup_new_exec(void) { }
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/ucopysize.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
63
include/linux/ucopysize.h
Normal file
63
include/linux/ucopysize.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Perform sanity checking for object sizes for uaccess.h and uio.h. */
|
||||
#ifndef __LINUX_UCOPYSIZE_H__
|
||||
#define __LINUX_UCOPYSIZE_H__
|
||||
|
||||
#include <linux/bug.h>
|
||||
|
||||
#ifdef CONFIG_HARDENED_USERCOPY
|
||||
#include <linux/jump_label.h>
|
||||
extern void __check_object_size(const void *ptr, unsigned long n,
|
||||
bool to_user);
|
||||
|
||||
DECLARE_STATIC_KEY_MAYBE(CONFIG_HARDENED_USERCOPY_DEFAULT_ON,
|
||||
validate_usercopy_range);
|
||||
|
||||
static __always_inline void check_object_size(const void *ptr, unsigned long n,
|
||||
bool to_user)
|
||||
{
|
||||
if (!__builtin_constant_p(n) &&
|
||||
static_branch_maybe(CONFIG_HARDENED_USERCOPY_DEFAULT_ON,
|
||||
&validate_usercopy_range)) {
|
||||
__check_object_size(ptr, n, to_user);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void check_object_size(const void *ptr, unsigned long n,
|
||||
bool to_user)
|
||||
{ }
|
||||
#endif /* CONFIG_HARDENED_USERCOPY */
|
||||
|
||||
extern void __compiletime_error("copy source size is too small")
|
||||
__bad_copy_from(void);
|
||||
extern void __compiletime_error("copy destination size is too small")
|
||||
__bad_copy_to(void);
|
||||
|
||||
void __copy_overflow(int size, unsigned long count);
|
||||
|
||||
static inline void copy_overflow(int size, unsigned long count)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_BUG))
|
||||
__copy_overflow(size, count);
|
||||
}
|
||||
|
||||
static __always_inline __must_check bool
|
||||
check_copy_size(const void *addr, size_t bytes, bool is_source)
|
||||
{
|
||||
int sz = __builtin_object_size(addr, 0);
|
||||
if (unlikely(sz >= 0 && sz < bytes)) {
|
||||
if (!__builtin_constant_p(bytes))
|
||||
copy_overflow(sz, bytes);
|
||||
else if (is_source)
|
||||
__bad_copy_from();
|
||||
else
|
||||
__bad_copy_to();
|
||||
return false;
|
||||
}
|
||||
if (WARN_ON_ONCE(bytes > INT_MAX))
|
||||
return false;
|
||||
check_object_size(addr, bytes, is_source);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __LINUX_UCOPYSIZE_H__ */
|
@ -6,8 +6,8 @@
|
||||
#define __LINUX_UIO_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/ucopysize.h>
|
||||
#include <uapi/linux/uio.h>
|
||||
|
||||
struct page;
|
||||
|
@ -188,7 +188,8 @@ struct nilfs_super_block {
|
||||
__le16 s_segment_usage_size; /* Size of a segment usage */
|
||||
|
||||
/*98*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */
|
||||
/*A8*/ char s_volume_name[80]; /* volume name */
|
||||
/*A8*/ char s_volume_name[80] /* volume name */
|
||||
__kernel_nonstring;
|
||||
|
||||
/*F8*/ __le32 s_c_interval; /* Commit interval of segment */
|
||||
__le32 s_c_block_max; /*
|
||||
|
@ -70,4 +70,10 @@
|
||||
#define __counted_by_be(m)
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define __kernel_nonstring __nonstring
|
||||
#else
|
||||
#define __kernel_nonstring
|
||||
#endif
|
||||
|
||||
#endif /* _UAPI_LINUX_STDDEF_H */
|
||||
|
@ -129,6 +129,9 @@ config CC_HAS_COUNTED_BY
|
||||
# https://github.com/llvm/llvm-project/pull/112636
|
||||
depends on !(CC_IS_CLANG && CLANG_VERSION < 190103)
|
||||
|
||||
config CC_HAS_MULTIDIMENSIONAL_NONSTRING
|
||||
def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
|
||||
|
||||
config RUSTC_HAS_COERCE_POINTEE
|
||||
def_bool RUSTC_VERSION >= 108400
|
||||
|
||||
|
@ -46,7 +46,7 @@ CONFIG_UBSAN_BOUNDS=y
|
||||
# CONFIG_UBSAN_SHIFT is not set
|
||||
# CONFIG_UBSAN_DIV_ZERO is not set
|
||||
# CONFIG_UBSAN_UNREACHABLE is not set
|
||||
# CONFIG_UBSAN_SIGNED_WRAP is not set
|
||||
# CONFIG_UBSAN_INTEGER_WRAP is not set
|
||||
# CONFIG_UBSAN_BOOL is not set
|
||||
# CONFIG_UBSAN_ENUM is not set
|
||||
# CONFIG_UBSAN_ALIGNMENT is not set
|
||||
|
@ -116,21 +116,22 @@ config UBSAN_UNREACHABLE
|
||||
This option enables -fsanitize=unreachable which checks for control
|
||||
flow reaching an expected-to-be-unreachable position.
|
||||
|
||||
config UBSAN_SIGNED_WRAP
|
||||
bool "Perform checking for signed arithmetic wrap-around"
|
||||
config UBSAN_INTEGER_WRAP
|
||||
bool "Perform checking for integer arithmetic wrap-around"
|
||||
default UBSAN
|
||||
depends on !COMPILE_TEST
|
||||
# The no_sanitize attribute was introduced in GCC with version 8.
|
||||
depends on !CC_IS_GCC || GCC_VERSION >= 80000
|
||||
depends on $(cc-option,-fsanitize-undefined-ignore-overflow-pattern=all)
|
||||
depends on $(cc-option,-fsanitize=signed-integer-overflow)
|
||||
depends on $(cc-option,-fsanitize=unsigned-integer-overflow)
|
||||
depends on $(cc-option,-fsanitize=implicit-signed-integer-truncation)
|
||||
depends on $(cc-option,-fsanitize=implicit-unsigned-integer-truncation)
|
||||
depends on $(cc-option,-fsanitize-ignorelist=/dev/null)
|
||||
help
|
||||
This option enables -fsanitize=signed-integer-overflow which checks
|
||||
for wrap-around of any arithmetic operations with signed integers.
|
||||
This currently performs nearly no instrumentation due to the
|
||||
kernel's use of -fno-strict-overflow which converts all would-be
|
||||
arithmetic undefined behavior into wrap-around arithmetic. Future
|
||||
sanitizer versions will allow for wrap-around checking (rather than
|
||||
exclusively undefined behavior).
|
||||
This option enables all of the sanitizers involved in integer overflow
|
||||
(wrap-around) mitigation: signed-integer-overflow, unsigned-integer-overflow,
|
||||
implicit-signed-integer-truncation, and implicit-unsigned-integer-truncation.
|
||||
This is currently limited only to the size_t type while testing and
|
||||
compiler development continues.
|
||||
|
||||
config UBSAN_BOOL
|
||||
bool "Perform checking for non-boolean values used as boolean"
|
||||
|
@ -15,7 +15,7 @@ static void test_ubsan_add_overflow(void)
|
||||
{
|
||||
volatile int val = INT_MAX;
|
||||
|
||||
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
|
||||
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
|
||||
val += 2;
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ static void test_ubsan_sub_overflow(void)
|
||||
volatile int val = INT_MIN;
|
||||
volatile int val2 = 2;
|
||||
|
||||
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
|
||||
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
|
||||
val -= val2;
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ static void test_ubsan_mul_overflow(void)
|
||||
{
|
||||
volatile int val = INT_MAX / 2;
|
||||
|
||||
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
|
||||
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
|
||||
val *= 3;
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ static void test_ubsan_negate_overflow(void)
|
||||
{
|
||||
volatile int val = INT_MIN;
|
||||
|
||||
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
|
||||
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
|
||||
val = -val;
|
||||
}
|
||||
|
||||
@ -53,6 +53,15 @@ static void test_ubsan_divrem_overflow(void)
|
||||
val /= val2;
|
||||
}
|
||||
|
||||
static void test_ubsan_truncate_signed(void)
|
||||
{
|
||||
volatile long val = LONG_MAX;
|
||||
volatile int val2 = 0;
|
||||
|
||||
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
|
||||
val2 = val;
|
||||
}
|
||||
|
||||
static void test_ubsan_shift_out_of_bounds(void)
|
||||
{
|
||||
volatile int neg = -1, wrap = 4;
|
||||
@ -127,6 +136,7 @@ static const test_ubsan_fp test_ubsan_array[] = {
|
||||
test_ubsan_sub_overflow,
|
||||
test_ubsan_mul_overflow,
|
||||
test_ubsan_negate_overflow,
|
||||
test_ubsan_truncate_signed,
|
||||
test_ubsan_shift_out_of_bounds,
|
||||
test_ubsan_out_of_bounds,
|
||||
test_ubsan_load_invalid_value,
|
||||
|
@ -579,8 +579,8 @@ static void string_test_strtomem(struct kunit *test)
|
||||
|
||||
static void string_test_memtostr(struct kunit *test)
|
||||
{
|
||||
char nonstring[7] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
|
||||
char nonstring_small[3] = { 'a', 'b', 'c' };
|
||||
char nonstring[7] __nonstring = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
|
||||
char nonstring_small[3] __nonstring = { 'a', 'b', 'c' };
|
||||
char dest[sizeof(nonstring) + 1];
|
||||
|
||||
/* Copy in a non-NUL-terminated string into exactly right-sized dest. */
|
||||
|
28
lib/ubsan.c
28
lib/ubsan.c
@ -44,7 +44,7 @@ const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type)
|
||||
case ubsan_shift_out_of_bounds:
|
||||
return "UBSAN: shift out of bounds";
|
||||
#endif
|
||||
#if defined(CONFIG_UBSAN_DIV_ZERO) || defined(CONFIG_UBSAN_SIGNED_WRAP)
|
||||
#if defined(CONFIG_UBSAN_DIV_ZERO) || defined(CONFIG_UBSAN_INTEGER_WRAP)
|
||||
/*
|
||||
* SanitizerKind::IntegerDivideByZero and
|
||||
* SanitizerKind::SignedIntegerOverflow emit
|
||||
@ -79,7 +79,7 @@ const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type)
|
||||
case ubsan_type_mismatch:
|
||||
return "UBSAN: type mismatch";
|
||||
#endif
|
||||
#ifdef CONFIG_UBSAN_SIGNED_WRAP
|
||||
#ifdef CONFIG_UBSAN_INTEGER_WRAP
|
||||
/*
|
||||
* SanitizerKind::SignedIntegerOverflow emits
|
||||
* SanitizerHandler::AddOverflow, SanitizerHandler::SubOverflow,
|
||||
@ -303,6 +303,30 @@ void __ubsan_handle_negate_overflow(void *_data, void *old_val)
|
||||
}
|
||||
EXPORT_SYMBOL(__ubsan_handle_negate_overflow);
|
||||
|
||||
void __ubsan_handle_implicit_conversion(void *_data, void *from_val, void *to_val)
|
||||
{
|
||||
struct implicit_conversion_data *data = _data;
|
||||
char from_val_str[VALUE_LENGTH];
|
||||
char to_val_str[VALUE_LENGTH];
|
||||
|
||||
if (suppress_report(&data->location))
|
||||
return;
|
||||
|
||||
val_to_string(from_val_str, sizeof(from_val_str), data->from_type, from_val);
|
||||
val_to_string(to_val_str, sizeof(to_val_str), data->to_type, to_val);
|
||||
|
||||
ubsan_prologue(&data->location, "implicit-conversion");
|
||||
|
||||
pr_err("cannot represent %s value %s during %s %s, truncated to %s\n",
|
||||
data->from_type->type_name,
|
||||
from_val_str,
|
||||
type_check_kinds[data->type_check_kind],
|
||||
data->to_type->type_name,
|
||||
to_val_str);
|
||||
|
||||
ubsan_epilogue();
|
||||
}
|
||||
EXPORT_SYMBOL(__ubsan_handle_implicit_conversion);
|
||||
|
||||
void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs)
|
||||
{
|
||||
|
@ -62,6 +62,13 @@ struct overflow_data {
|
||||
struct type_descriptor *type;
|
||||
};
|
||||
|
||||
struct implicit_conversion_data {
|
||||
struct source_location location;
|
||||
struct type_descriptor *from_type;
|
||||
struct type_descriptor *to_type;
|
||||
unsigned char type_check_kind;
|
||||
};
|
||||
|
||||
struct type_mismatch_data {
|
||||
struct source_location location;
|
||||
struct type_descriptor *type;
|
||||
@ -142,6 +149,7 @@ void ubsan_linkage __ubsan_handle_sub_overflow(void *data, void *lhs, void *rhs)
|
||||
void ubsan_linkage __ubsan_handle_mul_overflow(void *data, void *lhs, void *rhs);
|
||||
void ubsan_linkage __ubsan_handle_negate_overflow(void *_data, void *old_val);
|
||||
void ubsan_linkage __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs);
|
||||
void ubsan_linkage __ubsan_handle_implicit_conversion(void *_data, void *lhs, void *rhs);
|
||||
void ubsan_linkage __ubsan_handle_type_mismatch(struct type_mismatch_data *data, void *ptr);
|
||||
void ubsan_linkage __ubsan_handle_type_mismatch_v1(void *_data, void *ptr);
|
||||
void ubsan_linkage __ubsan_handle_out_of_bounds(void *_data, void *index);
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/ucopysize.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/jump_label.h>
|
||||
@ -201,7 +201,9 @@ static inline void check_heap_object(const void *ptr, unsigned long n,
|
||||
}
|
||||
}
|
||||
|
||||
static DEFINE_STATIC_KEY_FALSE_RO(bypass_usercopy_checks);
|
||||
DEFINE_STATIC_KEY_MAYBE_RO(CONFIG_HARDENED_USERCOPY_DEFAULT_ON,
|
||||
validate_usercopy_range);
|
||||
EXPORT_SYMBOL(validate_usercopy_range);
|
||||
|
||||
/*
|
||||
* Validates that the given object is:
|
||||
@ -212,9 +214,6 @@ static DEFINE_STATIC_KEY_FALSE_RO(bypass_usercopy_checks);
|
||||
*/
|
||||
void __check_object_size(const void *ptr, unsigned long n, bool to_user)
|
||||
{
|
||||
if (static_branch_unlikely(&bypass_usercopy_checks))
|
||||
return;
|
||||
|
||||
/* Skip all tests if size is zero. */
|
||||
if (!n)
|
||||
return;
|
||||
@ -255,7 +254,8 @@ void __check_object_size(const void *ptr, unsigned long n, bool to_user)
|
||||
}
|
||||
EXPORT_SYMBOL(__check_object_size);
|
||||
|
||||
static bool enable_checks __initdata = true;
|
||||
static bool enable_checks __initdata =
|
||||
IS_ENABLED(CONFIG_HARDENED_USERCOPY_DEFAULT_ON);
|
||||
|
||||
static int __init parse_hardened_usercopy(char *str)
|
||||
{
|
||||
@ -269,8 +269,10 @@ __setup("hardened_usercopy=", parse_hardened_usercopy);
|
||||
|
||||
static int __init set_hardened_usercopy(void)
|
||||
{
|
||||
if (enable_checks == false)
|
||||
static_branch_enable(&bypass_usercopy_checks);
|
||||
if (enable_checks)
|
||||
static_branch_enable(&validate_usercopy_range);
|
||||
else
|
||||
static_branch_disable(&validate_usercopy_range);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@ CLANG_TARGET_FLAGS_riscv := riscv64-linux-gnu
|
||||
CLANG_TARGET_FLAGS_s390 := s390x-linux-gnu
|
||||
CLANG_TARGET_FLAGS_sparc := sparc64-linux-gnu
|
||||
CLANG_TARGET_FLAGS_x86 := x86_64-linux-gnu
|
||||
# This is only for i386 UM builds, which need the 32-bit target not -m32
|
||||
CLANG_TARGET_FLAGS_i386 := i386-linux-gnu
|
||||
CLANG_TARGET_FLAGS_um := $(CLANG_TARGET_FLAGS_$(SUBARCH))
|
||||
CLANG_TARGET_FLAGS := $(CLANG_TARGET_FLAGS_$(SRCARCH))
|
||||
|
||||
|
@ -166,8 +166,8 @@ _c_flags += $(if $(patsubst n%,, \
|
||||
$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SANITIZE)$(is-kernel-object)), \
|
||||
$(CFLAGS_UBSAN))
|
||||
_c_flags += $(if $(patsubst n%,, \
|
||||
$(UBSAN_SIGNED_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SIGNED_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \
|
||||
$(CFLAGS_UBSAN_SIGNED_WRAP))
|
||||
$(UBSAN_INTEGER_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_INTEGER_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \
|
||||
$(CFLAGS_UBSAN_INTEGER_WRAP))
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_KCOV),y)
|
||||
|
@ -14,5 +14,11 @@ ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(call cc-option,-fsanitize-trap=undefined
|
||||
|
||||
export CFLAGS_UBSAN := $(ubsan-cflags-y)
|
||||
|
||||
ubsan-signed-wrap-cflags-$(CONFIG_UBSAN_SIGNED_WRAP) += -fsanitize=signed-integer-overflow
|
||||
export CFLAGS_UBSAN_SIGNED_WRAP := $(ubsan-signed-wrap-cflags-y)
|
||||
ubsan-integer-wrap-cflags-$(CONFIG_UBSAN_INTEGER_WRAP) += \
|
||||
-fsanitize-undefined-ignore-overflow-pattern=all \
|
||||
-fsanitize=signed-integer-overflow \
|
||||
-fsanitize=unsigned-integer-overflow \
|
||||
-fsanitize=implicit-signed-integer-truncation \
|
||||
-fsanitize=implicit-unsigned-integer-truncation \
|
||||
-fsanitize-ignorelist=$(srctree)/scripts/integer-wrap-ignore.scl
|
||||
export CFLAGS_UBSAN_INTEGER_WRAP := $(ubsan-integer-wrap-cflags-y)
|
||||
|
3
scripts/integer-wrap-ignore.scl
Normal file
3
scripts/integer-wrap-ignore.scl
Normal file
@ -0,0 +1,3 @@
|
||||
[{unsigned-integer-overflow,signed-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation}]
|
||||
type:*
|
||||
type:size_t=sanitize
|
@ -164,27 +164,6 @@ config LSM_MMAP_MIN_ADDR
|
||||
this low address space will need the permission specific to the
|
||||
systems running LSM.
|
||||
|
||||
config HARDENED_USERCOPY
|
||||
bool "Harden memory copies between kernel and userspace"
|
||||
imply STRICT_DEVMEM
|
||||
help
|
||||
This option checks for obviously wrong memory regions when
|
||||
copying memory to/from the kernel (via copy_to_user() and
|
||||
copy_from_user() functions) by rejecting memory ranges that
|
||||
are larger than the specified heap object, span multiple
|
||||
separately allocated pages, are not on the process stack,
|
||||
or are part of the kernel text. This prevents entire classes
|
||||
of heap overflow exploits and similar kernel memory exposures.
|
||||
|
||||
config FORTIFY_SOURCE
|
||||
bool "Harden common str/mem functions against buffer overflows"
|
||||
depends on ARCH_HAS_FORTIFY_SOURCE
|
||||
# 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.
|
||||
|
||||
config STATIC_USERMODEHELPER
|
||||
bool "Force all usermode helper calls through a single binary"
|
||||
help
|
||||
|
@ -280,6 +280,39 @@ config ZERO_CALL_USED_REGS
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Bounds checking"
|
||||
|
||||
config FORTIFY_SOURCE
|
||||
bool "Harden common str/mem functions against buffer overflows"
|
||||
depends on ARCH_HAS_FORTIFY_SOURCE
|
||||
# https://github.com/llvm/llvm-project/issues/53645
|
||||
depends on !X86_32 || !CC_IS_CLANG || CLANG_VERSION >= 160000
|
||||
help
|
||||
Detect overflows of buffers in common string and memory functions
|
||||
where the compiler can determine and validate the buffer sizes.
|
||||
|
||||
config HARDENED_USERCOPY
|
||||
bool "Harden memory copies between kernel and userspace"
|
||||
imply STRICT_DEVMEM
|
||||
help
|
||||
This option checks for obviously wrong memory regions when
|
||||
copying memory to/from the kernel (via copy_to_user() and
|
||||
copy_from_user() functions) by rejecting memory ranges that
|
||||
are larger than the specified heap object, span multiple
|
||||
separately allocated pages, are not on the process stack,
|
||||
or are part of the kernel text. This prevents entire classes
|
||||
of heap overflow exploits and similar kernel memory exposures.
|
||||
|
||||
config HARDENED_USERCOPY_DEFAULT_ON
|
||||
bool "Harden memory copies by default"
|
||||
depends on HARDENED_USERCOPY
|
||||
default HARDENED_USERCOPY
|
||||
help
|
||||
This has the effect of setting "hardened_usercopy=on" on the kernel
|
||||
command line. This can be disabled with "hardened_usercopy=off".
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Hardening of kernel data structures"
|
||||
|
||||
config LIST_HARDENED
|
||||
|
@ -16,7 +16,7 @@ config SECURITY_LOADPIN_ENFORCE
|
||||
depends on SECURITY_LOADPIN
|
||||
# Module compression breaks LoadPin unless modules are decompressed in
|
||||
# the kernel.
|
||||
depends on !MODULES || (MODULE_COMPRESS_NONE || MODULE_DECOMPRESS)
|
||||
depends on !MODULE_COMPRESS || MODULE_DECOMPRESS
|
||||
help
|
||||
If selected, LoadPin will enforce pinning at boot. If not
|
||||
selected, it can be enabled at boot with the kernel parameter
|
||||
|
@ -222,7 +222,7 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
struct task_struct *myself = current;
|
||||
struct task_struct *myself;
|
||||
|
||||
switch (option) {
|
||||
case PR_SET_PTRACER:
|
||||
@ -232,11 +232,7 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
* leader checking is handled later when walking the ancestry
|
||||
* at the time of PTRACE_ATTACH check.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
if (!thread_group_leader(myself))
|
||||
myself = rcu_dereference(myself->group_leader);
|
||||
get_task_struct(myself);
|
||||
rcu_read_unlock();
|
||||
myself = current->group_leader;
|
||||
|
||||
if (arg2 == 0) {
|
||||
yama_ptracer_del(NULL, myself);
|
||||
@ -255,7 +251,6 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
}
|
||||
}
|
||||
|
||||
put_task_struct(myself);
|
||||
break;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user