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:
Linus Torvalds 2025-03-24 15:18:08 -07:00
commit fc13a78e1f
36 changed files with 284 additions and 152 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 */

View File

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

View File

@ -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];

View File

@ -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__ */

View File

@ -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
/*

View File

@ -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); \
\

View File

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

View File

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

View File

@ -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
View 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__ */

View File

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

View File

@ -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; /*

View File

@ -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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
[{unsigned-integer-overflow,signed-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation}]
type:*
type:size_t=sanitize

View File

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

View File

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

View File

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

View File

@ -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;
}