summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-05-28 07:47:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2025-05-28 07:47:10 -0700
commit48cfc5791d83b630fd90a1b64a15a6d09c186f99 (patch)
tree5eafeeb38203c56efad2678ac37f751fe355031a
parent96d40793abc65341c4274169a4444fcfe5f0e6da (diff)
parentf0cd6012c40da99b45f8f63052b97ec89d5f307b (diff)
Merge tag 'hardening-v6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull hardening updates from Kees Cook: - Update overflow helpers to ease refactoring of on-stack flex array instances (Gustavo A. R. Silva, Kees Cook) - lkdtm: Use SLAB_NO_MERGE instead of constructors (Harry Yoo) - Simplify CONFIG_CC_HAS_COUNTED_BY (Jan Hendrik Farr) - Disable u64 usercopy KUnit test on 32-bit SPARC (Thomas Weißschuh) - Add missed designated initializers now exposed by fixed randstruct (Nathan Chancellor, Kees Cook) - Document compilers versions for __builtin_dynamic_object_size - Remove ARM_SSP_PER_TASK GCC plugin - Fix GCC plugin randstruct, add selftests, and restore COMPILE_TEST builds - Kbuild: induce full rebuilds when dependencies change with GCC plugins, the Clang sanitizer .scl file, or the randstruct seed. - Kbuild: Switch from -Wvla to -Wvla-larger-than=1 - Correct several __nonstring uses for -Wunterminated-string-initialization * tag 'hardening-v6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (23 commits) Revert "hardening: Disable GCC randstruct for COMPILE_TEST" lib/tests: randstruct: Add deep function pointer layout test lib/tests: Add randstruct KUnit test randstruct: gcc-plugin: Remove bogus void member net: qede: Initialize qede_ll_ops with designated initializer scsi: qedf: Use designated initializer for struct qed_fcoe_cb_ops md/bcache: Mark __nonstring look-up table integer-wrap: Force full rebuild when .scl file changes randstruct: Force full rebuild when seed changes gcc-plugins: Force full rebuild when plugins change kbuild: Switch from -Wvla to -Wvla-larger-than=1 hardening: simplify CONFIG_CC_HAS_COUNTED_BY overflow: Fix direct struct member initialization in _DEFINE_FLEX() kunit/overflow: Add tests for STACK_FLEX_ARRAY_SIZE() helper overflow: Add STACK_FLEX_ARRAY_SIZE() helper input/joystick: magellan: Mark __nonstring look-up table const watchdog: exar: Shorten identity name to fit correctly mod_devicetable: Enlarge the maximum platform_device_id name length overflow: Clarify expectations for getting DEFINE_FLEX variable sizes compiler_types: Identify compiler versions for __builtin_dynamic_object_size ...
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/arm/Kconfig3
-rw-r--r--arch/arm/boot/compressed/Makefile2
-rw-r--r--drivers/md/bcache/super.c3
-rw-r--r--drivers/misc/lkdtm/heap.c17
-rw-r--r--drivers/scsi/qedf/qedf_main.c2
-rw-r--r--drivers/watchdog/exar_wdt.c2
-rw-r--r--include/linux/compiler-version.h30
-rw-r--r--include/linux/compiler_types.h5
-rw-r--r--include/linux/mod_devicetable.h2
-rw-r--r--include/linux/overflow.h23
-rw-r--r--include/linux/vermagic.h1
-rw-r--r--init/Kconfig9
-rw-r--r--lib/Kconfig.debug8
-rw-r--r--lib/Makefile1
-rw-r--r--lib/tests/Makefile1
-rw-r--r--lib/tests/overflow_kunit.c4
-rw-r--r--lib/tests/randstruct_kunit.c334
-rw-r--r--lib/tests/usercopy_kunit.c1
-rw-r--r--mm/kasan/Makefile3
-rw-r--r--scripts/Makefile.extrawarn9
-rw-r--r--scripts/Makefile.gcc-plugins8
-rw-r--r--scripts/Makefile.lib18
-rw-r--r--scripts/Makefile.ubsan1
-rw-r--r--scripts/basic/Makefile5
-rw-r--r--scripts/gcc-plugins/Kconfig4
-rw-r--r--scripts/gcc-plugins/Makefile4
-rw-r--r--scripts/gcc-plugins/arm_ssp_per_task_plugin.c107
-rw-r--r--scripts/gcc-plugins/randomize_layout_plugin.c18
-rw-r--r--security/Kconfig.hardening2
30 files changed, 459 insertions, 169 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 76926087ee0a..996af81424de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12935,6 +12935,7 @@ F: include/linux/overflow.h
F: include/linux/randomize_kstack.h
F: include/linux/ucopysize.h
F: kernel/configs/hardening.config
+F: lib/tests/randstruct_kunit.c
F: lib/tests/usercopy_kunit.c
F: mm/usercopy.c
F: security/Kconfig.hardening
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 25ed6f1a7c7a..3072731fe09c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1380,8 +1380,7 @@ config CC_HAVE_STACKPROTECTOR_TLS
config STACKPROTECTOR_PER_TASK
bool "Use a unique stack canary value for each task"
depends on STACKPROTECTOR && CURRENT_POINTER_IN_TPIDRURO && !XIP_DEFLATED_DATA
- depends on GCC_PLUGINS || CC_HAVE_STACKPROTECTOR_TLS
- select GCC_PLUGIN_ARM_SSP_PER_TASK if !CC_HAVE_STACKPROTECTOR_TLS
+ depends on CC_HAVE_STACKPROTECTOR_TLS
default y
help
Due to the fact that GCC uses an ordinary symbol reference from
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 945b5975fce2..d61369b1eabe 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -96,7 +96,7 @@ KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
ccflags-y := -fpic $(call cc-option,-mno-single-pic-base,) -fno-builtin \
-I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \
- -I$(obj) $(DISABLE_ARM_SSP_PER_TASK_PLUGIN)
+ -I$(obj)
ccflags-remove-$(CONFIG_FUNCTION_TRACER) += -pg
asflags-y := -DZIMAGE
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index c40db9c161c1..1a2ce1a4b456 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -545,7 +545,8 @@ static struct uuid_entry *uuid_find(struct cache_set *c, const char *uuid)
static struct uuid_entry *uuid_find_empty(struct cache_set *c)
{
- static const char zero_uuid[16] = { 0 };
+ static const char zero_uuid[16] __nonstring =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
return uuid_find(c, zero_uuid);
}
diff --git a/drivers/misc/lkdtm/heap.c b/drivers/misc/lkdtm/heap.c
index b1b316f99703..c1a05b935894 100644
--- a/drivers/misc/lkdtm/heap.c
+++ b/drivers/misc/lkdtm/heap.c
@@ -355,23 +355,12 @@ static void lkdtm_SLAB_FREE_PAGE(void)
free_page(p);
}
-/*
- * We have constructors to keep the caches distinctly separated without
- * needing to boot with "slab_nomerge".
- */
-static void ctor_double_free(void *region)
-{ }
-static void ctor_a(void *region)
-{ }
-static void ctor_b(void *region)
-{ }
-
void __init lkdtm_heap_init(void)
{
double_free_cache = kmem_cache_create("lkdtm-heap-double_free",
- 64, 0, 0, ctor_double_free);
- a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a);
- b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b);
+ 64, 0, SLAB_NO_MERGE, NULL);
+ a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, SLAB_NO_MERGE, NULL);
+ b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, SLAB_NO_MERGE, NULL);
}
void __exit lkdtm_heap_exit(void)
diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c
index 436bd29d5eba..6b1ebab36fa3 100644
--- a/drivers/scsi/qedf/qedf_main.c
+++ b/drivers/scsi/qedf/qedf_main.c
@@ -699,7 +699,7 @@ static u32 qedf_get_login_failures(void *cookie)
}
static struct qed_fcoe_cb_ops qedf_cb_ops = {
- {
+ .common = {
.link_update = qedf_link_update,
.bw_update = qedf_bw_update,
.schedule_recovery_handler = qedf_schedule_recovery_handler,
diff --git a/drivers/watchdog/exar_wdt.c b/drivers/watchdog/exar_wdt.c
index 7c61ff343271..c2e3bb08df89 100644
--- a/drivers/watchdog/exar_wdt.c
+++ b/drivers/watchdog/exar_wdt.c
@@ -221,7 +221,7 @@ static const struct watchdog_info exar_wdt_info = {
.options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE,
- .identity = "Exar/MaxLinear XR28V38x Watchdog",
+ .identity = "Exar XR28V38x Watchdog",
};
static const struct watchdog_ops exar_wdt_ops = {
diff --git a/include/linux/compiler-version.h b/include/linux/compiler-version.h
index 573fa85b6c0c..ac1665a98a15 100644
--- a/include/linux/compiler-version.h
+++ b/include/linux/compiler-version.h
@@ -12,3 +12,33 @@
* and add dependency on include/config/CC_VERSION_TEXT, which is touched
* by Kconfig when the version string from the compiler changes.
*/
+
+/* Additional tree-wide dependencies start here. */
+
+/*
+ * If any of the GCC plugins change, we need to rebuild everything that
+ * was built with them, as they may have changed their behavior and those
+ * behaviors may need to be synchronized across all translation units.
+ */
+#ifdef GCC_PLUGINS
+#include <generated/gcc-plugins.h>
+#endif
+
+/*
+ * If the randstruct seed itself changes (whether for GCC plugins or
+ * Clang), the entire tree needs to be rebuilt since the randomization of
+ * structures may change between compilation units if not.
+ */
+#ifdef RANDSTRUCT
+#include <generated/randstruct_hash.h>
+#endif
+
+/*
+ * If any external changes affect Clang's integer wrapping sanitizer
+ * behavior, a full rebuild is needed as the coverage for wrapping types
+ * may have changed, which may impact the expected behaviors that should
+ * not differ between compilation units.
+ */
+#ifdef INTEGER_WRAP
+#include <generated/integer-wrap.h>
+#endif
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index 501cffddc2f4..20881cc761fa 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -449,6 +449,11 @@ struct ftrace_likely_data {
/*
* When the size of an allocated object is needed, use the best available
* mechanism to find it. (For cases where sizeof() cannot be used.)
+ *
+ * Optional: only supported since gcc >= 12
+ *
+ * gcc: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
+ * clang: https://clang.llvm.org/docs/LanguageExtensions.html#evaluating-object-size
*/
#if __has_builtin(__builtin_dynamic_object_size)
#define __struct_size(p) __builtin_dynamic_object_size(p, 0)
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index bd7e60c0b72f..ebcee9328168 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -601,7 +601,7 @@ struct dmi_system_id {
#define DMI_MATCH(a, b) { .slot = a, .substr = b }
#define DMI_EXACT_MATCH(a, b) { .slot = a, .substr = b, .exact_match = 1 }
-#define PLATFORM_NAME_SIZE 20
+#define PLATFORM_NAME_SIZE 24
#define PLATFORM_MODULE_PREFIX "platform:"
struct platform_device_id {
diff --git a/include/linux/overflow.h b/include/linux/overflow.h
index 0c7e3dcfe867..7b7be27ca113 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.h
@@ -396,7 +396,7 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
* @name: Name for a variable to define.
* @member: Name of the array member.
* @count: Number of elements in the array; must be compile-time const.
- * @initializer: initializer expression (could be empty for no init).
+ * @initializer: Initializer expression (e.g., pass `= { }` at minimum).
*/
#define _DEFINE_FLEX(type, name, member, count, initializer...) \
_Static_assert(__builtin_constant_p(count), \
@@ -404,7 +404,7 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
union { \
u8 bytes[struct_size_t(type, member, count)]; \
type obj; \
- } name##_u initializer; \
+ } name##_u = { .obj initializer }; \
type *name = (type *)&name##_u
/**
@@ -419,6 +419,9 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
* Define a zeroed, on-stack, instance of @type structure with a trailing
* flexible array member.
* Use __struct_size(@name) to get compile-time size of it afterwards.
+ * Use __member_size(@name->member) to get compile-time size of @name members.
+ * Use STACK_FLEX_ARRAY_SIZE(@name, @member) to get compile-time number of
+ * elements in array @member.
*/
#define DEFINE_RAW_FLEX(type, name, member, count) \
_DEFINE_FLEX(type, name, member, count, = {})
@@ -436,8 +439,22 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
* Define a zeroed, on-stack, instance of @TYPE structure with a trailing
* flexible array member.
* Use __struct_size(@NAME) to get compile-time size of it afterwards.
+ * Use __member_size(@NAME->member) to get compile-time size of @NAME members.
+ * Use STACK_FLEX_ARRAY_SIZE(@name, @member) to get compile-time number of
+ * elements in array @member.
*/
#define DEFINE_FLEX(TYPE, NAME, MEMBER, COUNTER, COUNT) \
- _DEFINE_FLEX(TYPE, NAME, MEMBER, COUNT, = { .obj.COUNTER = COUNT, })
+ _DEFINE_FLEX(TYPE, NAME, MEMBER, COUNT, = { .COUNTER = COUNT, })
+
+/**
+ * STACK_FLEX_ARRAY_SIZE() - helper macro for DEFINE_FLEX() family.
+ * Returns the number of elements in @array.
+ *
+ * @name: Name for a variable defined in DEFINE_RAW_FLEX()/DEFINE_FLEX().
+ * @array: Name of the array member.
+ */
+#define STACK_FLEX_ARRAY_SIZE(name, array) \
+ (__member_size((name)->array) / sizeof(*(name)->array) + \
+ __must_be_array((name)->array))
#endif /* __LINUX_OVERFLOW_H */
diff --git a/include/linux/vermagic.h b/include/linux/vermagic.h
index 939ceabcaf06..335c360d4f9b 100644
--- a/include/linux/vermagic.h
+++ b/include/linux/vermagic.h
@@ -33,7 +33,6 @@
#define MODULE_VERMAGIC_MODVERSIONS ""
#endif
#ifdef RANDSTRUCT
-#include <generated/randstruct_hash.h>
#define MODULE_RANDSTRUCT "RANDSTRUCT_" RANDSTRUCT_HASHED_SEED
#else
#define MODULE_RANDSTRUCT
diff --git a/init/Kconfig b/init/Kconfig
index ae5d00baeafb..20716189fe68 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -116,13 +116,14 @@ config CC_HAS_NO_PROFILE_FN_ATTR
def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
config CC_HAS_COUNTED_BY
- # TODO: when gcc 15 is released remove the build test and add
- # a gcc version check
- def_bool $(success,echo 'struct flex { int count; int array[] __attribute__((__counted_by__(count))); };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
+ bool
# clang needs to be at least 19.1.3 to avoid __bdos miscalculations
# https://github.com/llvm/llvm-project/pull/110497
# https://github.com/llvm/llvm-project/pull/112636
- depends on !(CC_IS_CLANG && CLANG_VERSION < 190103)
+ default y if CC_IS_CLANG && CLANG_VERSION >= 190103
+ # supported since gcc 15.1.0
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
+ default y if CC_IS_GCC && GCC_VERSION >= 150100
config CC_HAS_MULTIDIMENSIONAL_NONSTRING
def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index f9051ab610d5..6479cec900c7 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2863,6 +2863,14 @@ config OVERFLOW_KUNIT_TEST
If unsure, say N.
+config RANDSTRUCT_KUNIT_TEST
+ tristate "Test randstruct structure layout randomization at runtime" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Builds unit tests for the checking CONFIG_RANDSTRUCT=y, which
+ randomizes structure layouts.
+
config STACKINIT_KUNIT_TEST
tristate "Test level of stack variable initialization" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/Makefile b/lib/Makefile
index f07b24ce1b3f..c38582f187dd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -71,7 +71,6 @@ CFLAGS_test_bitops.o += -Werror
obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o
obj-$(CONFIG_TEST_IDA) += test_ida.o
obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
-CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla)
CFLAGS_test_ubsan.o += $(call cc-disable-warning, unused-but-set-variable)
UBSAN_SANITIZE_test_ubsan.o := y
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index 5a4794c1826e..56d645014482 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o
CFLAGS_overflow_kunit.o = $(call cc-disable-warning, tautological-constant-out-of-range-compare)
obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o
obj-$(CONFIG_PRINTF_KUNIT_TEST) += printf_kunit.o
+obj-$(CONFIG_RANDSTRUCT_KUNIT_TEST) += randstruct_kunit.o
obj-$(CONFIG_SCANF_KUNIT_TEST) += scanf_kunit.o
obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o
diff --git a/lib/tests/overflow_kunit.c b/lib/tests/overflow_kunit.c
index 894691b4411a..19cb03b25dc5 100644
--- a/lib/tests/overflow_kunit.c
+++ b/lib/tests/overflow_kunit.c
@@ -1210,6 +1210,10 @@ static void DEFINE_FLEX_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, __struct_size(empty->array), 0);
KUNIT_EXPECT_EQ(test, __member_size(empty->array), 0);
+ KUNIT_EXPECT_EQ(test, STACK_FLEX_ARRAY_SIZE(two, array), 2);
+ KUNIT_EXPECT_EQ(test, STACK_FLEX_ARRAY_SIZE(eight, array), 8);
+ KUNIT_EXPECT_EQ(test, STACK_FLEX_ARRAY_SIZE(empty, array), 0);
+
/* If __counted_by is not being used, array size will have the on-stack size. */
if (!IS_ENABLED(CONFIG_CC_HAS_COUNTED_BY))
array_size_override = 2 * sizeof(s16);
diff --git a/lib/tests/randstruct_kunit.c b/lib/tests/randstruct_kunit.c
new file mode 100644
index 000000000000..f3a2d63c4cfb
--- /dev/null
+++ b/lib/tests/randstruct_kunit.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test cases for struct randomization, i.e. CONFIG_RANDSTRUCT=y.
+ *
+ * For example, see:
+ * "Running tests with kunit_tool" at Documentation/dev-tools/kunit/start.rst
+ * ./tools/testing/kunit/kunit.py run randstruct [--raw_output] \
+ * [--make_option LLVM=1] \
+ * --kconfig_add CONFIG_RANDSTRUCT_FULL=y
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <kunit/test.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define DO_MANY_MEMBERS(macro, args...) \
+ macro(a, args) \
+ macro(b, args) \
+ macro(c, args) \
+ macro(d, args) \
+ macro(e, args) \
+ macro(f, args) \
+ macro(g, args) \
+ macro(h, args)
+
+#define do_enum(x, ignored) MEMBER_NAME_ ## x,
+enum randstruct_member_names {
+ DO_MANY_MEMBERS(do_enum)
+ MEMBER_NAME_MAX,
+};
+/* Make sure the macros are working: want 8 test members. */
+_Static_assert(MEMBER_NAME_MAX == 8, "Number of test members changed?!");
+
+/* This is an unsigned long member to match the function pointer size */
+#define unsigned_long_member(x, ignored) unsigned long x;
+struct randstruct_untouched {
+ DO_MANY_MEMBERS(unsigned_long_member)
+};
+
+/* Struct explicitly marked with __randomize_layout. */
+struct randstruct_shuffled {
+ DO_MANY_MEMBERS(unsigned_long_member)
+} __randomize_layout;
+#undef unsigned_long_member
+
+/* Struct implicitly randomized from being all func ptrs. */
+#define func_member(x, ignored) size_t (*x)(int);
+struct randstruct_funcs_untouched {
+ DO_MANY_MEMBERS(func_member)
+} __no_randomize_layout;
+
+struct randstruct_funcs_shuffled {
+ DO_MANY_MEMBERS(func_member)
+};
+
+#define func_body(x, ignored) \
+static noinline size_t func_##x(int arg) \
+{ \
+ return offsetof(struct randstruct_funcs_untouched, x); \
+}
+DO_MANY_MEMBERS(func_body)
+
+/* Various mixed types. */
+#define mixed_members \
+ bool a; \
+ short b; \
+ unsigned int c __aligned(16); \
+ size_t d; \
+ char e; \
+ u64 f; \
+ union { \
+ struct randstruct_shuffled shuffled; \
+ uintptr_t g; \
+ }; \
+ union { \
+ void *ptr; \
+ char h; \
+ };
+
+struct randstruct_mixed_untouched {
+ mixed_members
+};
+
+struct randstruct_mixed_shuffled {
+ mixed_members
+} __randomize_layout;
+#undef mixed_members
+
+struct contains_randstruct_untouched {
+ int before;
+ struct randstruct_untouched untouched;
+ int after;
+};
+
+struct contains_randstruct_shuffled {
+ int before;
+ struct randstruct_shuffled shuffled;
+ int after;
+};
+
+struct contains_func_untouched {
+ struct randstruct_funcs_shuffled inner;
+ DO_MANY_MEMBERS(func_member)
+} __no_randomize_layout;
+
+struct contains_func_shuffled {
+ struct randstruct_funcs_shuffled inner;
+ DO_MANY_MEMBERS(func_member)
+};
+#undef func_member
+
+#define check_mismatch(x, untouched, shuffled) \
+ if (offsetof(untouched, x) != offsetof(shuffled, x)) \
+ mismatches++; \
+ kunit_info(test, #shuffled "::" #x " @ %zu (vs %zu)\n", \
+ offsetof(shuffled, x), \
+ offsetof(untouched, x)); \
+
+#define check_pair(outcome, untouched, shuffled, checker...) \
+ mismatches = 0; \
+ DO_MANY_MEMBERS(checker, untouched, shuffled) \
+ kunit_info(test, "Differing " #untouched " vs " #shuffled " member positions: %d\n", \
+ mismatches); \
+ KUNIT_##outcome##_MSG(test, mismatches, 0, \
+ #untouched " vs " #shuffled " layouts: unlucky or broken?\n");
+
+static void randstruct_layout_same(struct kunit *test)
+{
+ int mismatches;
+
+ check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched,
+ check_mismatch)
+ check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled,
+ check_mismatch)
+}
+
+static void randstruct_layout_mixed(struct kunit *test)
+{
+ int mismatches;
+
+ check_pair(EXPECT_EQ, struct randstruct_mixed_untouched, struct randstruct_mixed_untouched,
+ check_mismatch)
+ check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled,
+ check_mismatch)
+}
+
+static void randstruct_layout_fptr(struct kunit *test)
+{
+ int mismatches;
+
+ check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched,
+ check_mismatch)
+ check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled,
+ check_mismatch)
+ check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled,
+ check_mismatch)
+}
+
+#define check_mismatch_prefixed(x, prefix, untouched, shuffled) \
+ check_mismatch(prefix.x, untouched, shuffled)
+
+static void randstruct_layout_fptr_deep(struct kunit *test)
+{
+ int mismatches;
+
+ if (IS_ENABLED(CONFIG_CC_IS_CLANG))
+ kunit_skip(test, "Clang randstruct misses inner functions: https://github.com/llvm/llvm-project/issues/138355");
+
+ check_pair(EXPECT_EQ, struct contains_func_untouched, struct contains_func_untouched,
+ check_mismatch_prefixed, inner)
+
+ check_pair(EXPECT_GT, struct contains_func_untouched, struct contains_func_shuffled,
+ check_mismatch_prefixed, inner)
+}
+
+#undef check_pair
+#undef check_mismatch
+
+#define check_mismatch(x, ignore) \
+ KUNIT_EXPECT_EQ_MSG(test, untouched->x, shuffled->x, \
+ "Mismatched member value in %s initializer\n", \
+ name);
+
+static void test_check_init(struct kunit *test, const char *name,
+ struct randstruct_untouched *untouched,
+ struct randstruct_shuffled *shuffled)
+{
+ DO_MANY_MEMBERS(check_mismatch)
+}
+
+static void test_check_mixed_init(struct kunit *test, const char *name,
+ struct randstruct_mixed_untouched *untouched,
+ struct randstruct_mixed_shuffled *shuffled)
+{
+ DO_MANY_MEMBERS(check_mismatch)
+}
+#undef check_mismatch
+
+#define check_mismatch(x, ignore) \
+ KUNIT_EXPECT_EQ_MSG(test, untouched->untouched.x, \
+ shuffled->shuffled.x, \
+ "Mismatched member value in %s initializer\n", \
+ name);
+static void test_check_contained_init(struct kunit *test, const char *name,
+ struct contains_randstruct_untouched *untouched,
+ struct contains_randstruct_shuffled *shuffled)
+{
+ DO_MANY_MEMBERS(check_mismatch)
+}
+#undef check_mismatch
+
+#define check_mismatch(x, ignore) \
+ KUNIT_EXPECT_PTR_EQ_MSG(test, untouched->x, shuffled->x, \
+ "Mismatched member value in %s initializer\n", \
+ name);
+
+static void test_check_funcs_init(struct kunit *test, const char *name,
+ struct randstruct_funcs_untouched *untouched,
+ struct randstruct_funcs_shuffled *shuffled)
+{
+ DO_MANY_MEMBERS(check_mismatch)
+}
+#undef check_mismatch
+
+static void randstruct_initializers(struct kunit *test)
+{
+#define init_members \
+ .a = 1, \
+ .b = 3, \
+ .c = 5, \
+ .d = 7, \
+ .e = 11, \
+ .f = 13, \
+ .g = 17, \
+ .h = 19,
+ struct randstruct_untouched untouched = {
+ init_members
+ };
+ struct randstruct_shuffled shuffled = {
+ init_members
+ };
+ struct randstruct_mixed_untouched mixed_untouched = {
+ init_members
+ };
+ struct randstruct_mixed_shuffled mixed_shuffled = {
+ init_members
+ };
+ struct contains_randstruct_untouched contains_untouched = {
+ .untouched = {
+ init_members
+ },
+ };
+ struct contains_randstruct_shuffled contains_shuffled = {
+ .shuffled = {
+ init_members
+ },
+ };
+#define func_member(x, ignored) \
+ .x = func_##x,
+ struct randstruct_funcs_untouched funcs_untouched = {
+ DO_MANY_MEMBERS(func_member)
+ };
+ struct randstruct_funcs_shuffled funcs_shuffled = {
+ DO_MANY_MEMBERS(func_member)
+ };
+
+ test_check_init(test, "named", &untouched, &shuffled);
+ test_check_init(test, "unnamed", &untouched,
+ &(struct randstruct_shuffled){
+ init_members
+ });
+
+ test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled);
+ test_check_contained_init(test, "unnamed", &contains_untouched,
+ &(struct contains_randstruct_shuffled){
+ .shuffled = (struct randstruct_shuffled){
+ init_members
+ },
+ });
+
+ test_check_contained_init(test, "named", &contains_untouched, &contains_shuffled);
+ test_check_contained_init(test, "unnamed copy", &contains_untouched,
+ &(struct contains_randstruct_shuffled){
+ /* full struct copy initializer */
+ .shuffled = shuffled,
+ });
+
+ test_check_mixed_init(test, "named", &mixed_untouched, &mixed_shuffled);
+ test_check_mixed_init(test, "unnamed", &mixed_untouched,
+ &(struct randstruct_mixed_shuffled){
+ init_members
+ });
+
+ test_check_funcs_init(test, "named", &funcs_untouched, &funcs_shuffled);
+ test_check_funcs_init(test, "unnamed", &funcs_untouched,
+ &(struct randstruct_funcs_shuffled){
+ DO_MANY_MEMBERS(func_member)
+ });
+
+#undef func_member
+#undef init_members
+}
+
+static int randstruct_test_init(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_RANDSTRUCT))
+ kunit_skip(test, "Not built with CONFIG_RANDSTRUCT=y");
+
+ return 0;
+}
+
+static struct kunit_case randstruct_test_cases[] = {
+ KUNIT_CASE(randstruct_layout_same),
+ KUNIT_CASE(randstruct_layout_mixed),
+ KUNIT_CASE(randstruct_layout_fptr),
+ KUNIT_CASE(randstruct_layout_fptr_deep),
+ KUNIT_CASE(randstruct_initializers),
+ {}
+};
+
+static struct kunit_suite randstruct_test_suite = {
+ .name = "randstruct",
+ .init = randstruct_test_init,
+ .test_cases = randstruct_test_cases,
+};
+
+kunit_test_suites(&randstruct_test_suite);
+
+MODULE_DESCRIPTION("Test cases for struct randomization");
+MODULE_LICENSE("GPL");
diff --git a/lib/tests/usercopy_kunit.c b/lib/tests/usercopy_kunit.c
index 77fa00a13df7..80f8abe10968 100644
--- a/lib/tests/usercopy_kunit.c
+++ b/lib/tests/usercopy_kunit.c
@@ -27,6 +27,7 @@
!defined(CONFIG_MICROBLAZE) && \
!defined(CONFIG_NIOS2) && \
!defined(CONFIG_PPC32) && \
+ !defined(CONFIG_SPARC32) && \
!defined(CONFIG_SUPERH))
# define TEST_U64
#endif
diff --git a/mm/kasan/Makefile b/mm/kasan/Makefile
index 1a958e7c8a46..dd93ae8a6beb 100644
--- a/mm/kasan/Makefile
+++ b/mm/kasan/Makefile
@@ -35,7 +35,7 @@ CFLAGS_shadow.o := $(CC_FLAGS_KASAN_RUNTIME)
CFLAGS_hw_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
CFLAGS_sw_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
-CFLAGS_KASAN_TEST := $(CFLAGS_KASAN) $(call cc-disable-warning, vla)
+CFLAGS_KASAN_TEST := $(CFLAGS_KASAN)
ifndef CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX
# If compiler instruments memintrinsics by prefixing them with __asan/__hwasan,
# we need to treat them normally (as builtins), otherwise the compiler won't
@@ -44,6 +44,7 @@ ifndef CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX
CFLAGS_KASAN_TEST += -fno-builtin
endif
+CFLAGS_REMOVE_kasan_test_c.o += $(call cc-option, -Wvla-larger-than=1)
CFLAGS_kasan_test_c.o := $(CFLAGS_KASAN_TEST)
RUSTFLAGS_kasan_test_rust.o := $(RUSTFLAGS_KASAN)
diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn
index 540f3db5cd86..dca175fffcab 100644
--- a/scripts/Makefile.extrawarn
+++ b/scripts/Makefile.extrawarn
@@ -58,8 +58,13 @@ endif
# These result in bogus false positives
KBUILD_CFLAGS += $(call cc-option, -Wno-dangling-pointer)
-# Variable Length Arrays (VLAs) should not be used anywhere in the kernel
-KBUILD_CFLAGS += -Wvla
+# Stack Variable Length Arrays (VLAs) must not be used in the kernel.
+# Function array parameters should, however, be usable, but -Wvla will
+# warn for those. Clang has no way yet to distinguish between the VLA
+# types, so depend on GCC for now to keep stack VLAs out of the tree.
+# https://github.com/llvm/llvm-project/issues/57098
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98217
+KBUILD_CFLAGS += $(call cc-option,-Wvla-larger-than=1)
# disable pointer signed / unsigned warnings in gcc 4.0
KBUILD_CFLAGS += -Wno-pointer-sign
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index e4deaf5fa571..e50dc931be49 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -36,15 +36,9 @@ ifdef CONFIG_GCC_PLUGIN_STACKLEAK
endif
export DISABLE_STACKLEAK_PLUGIN
-gcc-plugin-$(CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK) += arm_ssp_per_task_plugin.so
-ifdef CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK
- DISABLE_ARM_SSP_PER_TASK_PLUGIN += -fplugin-arg-arm_ssp_per_task_plugin-disable
-endif
-export DISABLE_ARM_SSP_PER_TASK_PLUGIN
-
# All the plugin CFLAGS are collected here in case a build target needs to
# filter them out of the KBUILD_CFLAGS.
-GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
+GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) -DGCC_PLUGINS
export GCC_PLUGINS_CFLAGS
# Add the flags to the build!
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 2fe73cda0bdd..6fc2a82ee3bb 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -296,6 +296,19 @@ $(foreach m, $1, \
$(addprefix $(obj)/, $(call suffix-search, $(patsubst $(obj)/%,%,$m), $2, $3))))
endef
+# Remove ".." and "." from a path, without using "realpath"
+# Usage:
+# $(call normalize_path,path/to/../file)
+define normalize_path
+$(strip $(eval elements :=) \
+$(foreach elem,$(subst /, ,$1), \
+ $(if $(filter-out .,$(elem)), \
+ $(if $(filter ..,$(elem)), \
+ $(eval elements := $(wordlist 2,$(words $(elements)),x $(elements))), \
+ $(eval elements := $(elements) $(elem))))) \
+$(subst $(space),/,$(elements)))
+endef
+
# Build commands
# ===========================================================================
# These are shared by some Makefile.* files.
@@ -343,6 +356,11 @@ quiet_cmd_copy = COPY $@
$(obj)/%: $(src)/%_shipped
$(call cmd,copy)
+# Touch a file
+# ===========================================================================
+quiet_cmd_touch = TOUCH $(call normalize_path,$@)
+ cmd_touch = touch $@
+
# Commands useful for building a boot image
# ===========================================================================
#
diff --git a/scripts/Makefile.ubsan b/scripts/Makefile.ubsan
index 9e35198edbf0..653f7117819c 100644
--- a/scripts/Makefile.ubsan
+++ b/scripts/Makefile.ubsan
@@ -15,6 +15,7 @@ ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(call cc-option,-fsanitize-trap=undefined
export CFLAGS_UBSAN := $(ubsan-cflags-y)
ubsan-integer-wrap-cflags-$(CONFIG_UBSAN_INTEGER_WRAP) += \
+ -DINTEGER_WRAP \
-fsanitize-undefined-ignore-overflow-pattern=all \
-fsanitize=signed-integer-overflow \
-fsanitize=unsigned-integer-overflow \
diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile
index dd289a6725ac..fb8e2c38fbc7 100644
--- a/scripts/basic/Makefile
+++ b/scripts/basic/Makefile
@@ -14,3 +14,8 @@ cmd_create_randstruct_seed = \
$(obj)/randstruct.seed: $(gen-randstruct-seed) FORCE
$(call if_changed,create_randstruct_seed)
always-$(CONFIG_RANDSTRUCT) += randstruct.seed
+
+# integer-wrap: if the .scl file changes, we need to do a full rebuild.
+$(obj)/../../include/generated/integer-wrap.h: $(srctree)/scripts/integer-wrap-ignore.scl FORCE
+ $(call if_changed,touch)
+always-$(CONFIG_UBSAN_INTEGER_WRAP) += ../../include/generated/integer-wrap.h
diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig
index e383cda05367..231f4a20d617 100644
--- a/scripts/gcc-plugins/Kconfig
+++ b/scripts/gcc-plugins/Kconfig
@@ -46,8 +46,4 @@ config GCC_PLUGIN_LATENT_ENTROPY
* https://grsecurity.net/
* https://pax.grsecurity.net/
-config GCC_PLUGIN_ARM_SSP_PER_TASK
- bool
- depends on GCC_PLUGINS && ARM
-
endif
diff --git a/scripts/gcc-plugins/Makefile b/scripts/gcc-plugins/Makefile
index 320afd3cf8e8..05b14aba41ef 100644
--- a/scripts/gcc-plugins/Makefile
+++ b/scripts/gcc-plugins/Makefile
@@ -66,3 +66,7 @@ quiet_cmd_plugin_cxx_o_c = HOSTCXX $@
$(plugin-objs): $(obj)/%.o: $(src)/%.c FORCE
$(call if_changed_dep,plugin_cxx_o_c)
+
+$(obj)/../../include/generated/gcc-plugins.h: $(plugin-single) $(plugin-multi) FORCE
+ $(call if_changed,touch)
+always-y += ../../include/generated/gcc-plugins.h
diff --git a/scripts/gcc-plugins/arm_ssp_per_task_plugin.c b/scripts/gcc-plugins/arm_ssp_per_task_plugin.c
deleted file mode 100644
index 7328d037f975..000000000000
--- a/scripts/gcc-plugins/arm_ssp_per_task_plugin.c
+++ /dev/null
@@ -1,107 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include "gcc-common.h"
-
-__visible int plugin_is_GPL_compatible;
-
-static unsigned int canary_offset;
-
-static unsigned int arm_pertask_ssp_rtl_execute(void)
-{
- rtx_insn *insn;
-
- for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) {
- const char *sym;
- rtx body;
- rtx current;
-
- /*
- * Find a SET insn involving a SYMBOL_REF to __stack_chk_guard
- */
- if (!INSN_P(insn))
- continue;
- body = PATTERN(insn);
- if (GET_CODE(body) != SET ||
- GET_CODE(SET_SRC(body)) != SYMBOL_REF)
- continue;
- sym = XSTR(SET_SRC(body), 0);
- if (strcmp(sym, "__stack_chk_guard"))
- continue;
-
- /*
- * Replace the source of the SET insn with an expression that
- * produces the address of the current task's stack canary value
- */
- current = gen_reg_rtx(Pmode);
-
- emit_insn_before(gen_load_tp_hard(current), insn);
-
- SET_SRC(body) = gen_rtx_PLUS(Pmode, current,
- GEN_INT(canary_offset));
- }
- return 0;
-}
-
-#define PASS_NAME arm_pertask_ssp_rtl
-
-#define NO_GATE
-#include "gcc-generate-rtl-pass.h"
-
-#if BUILDING_GCC_VERSION >= 9000
-static bool no(void)
-{
- return false;
-}
-
-static void arm_pertask_ssp_start_unit(void *gcc_data, void *user_data)
-{
- targetm.have_stack_protect_combined_set = no;
- targetm.have_stack_protect_combined_test = no;
-}
-#endif
-
-__visible int plugin_init(struct plugin_name_args *plugin_info,
- struct plugin_gcc_version *version)
-{
- const char * const plugin_name = plugin_info->base_name;
- const int argc = plugin_info->argc;
- const struct plugin_argument *argv = plugin_info->argv;
- int i;
-
- if (!plugin_default_version_check(version, &gcc_version)) {
- error(G_("incompatible gcc/plugin versions"));
- return 1;
- }
-
- for (i = 0; i < argc; ++i) {
- if (!strcmp(argv[i].key, "disable"))
- return 0;
-
- /* all remaining options require a value */
- if (!argv[i].value) {
- error(G_("no value supplied for option '-fplugin-arg-%s-%s'"),
- plugin_name, argv[i].key);
- return 1;
- }
-
- if (!strcmp(argv[i].key, "offset")) {
- canary_offset = atoi(argv[i].value);
- continue;
- }
- error(G_("unknown option '-fplugin-arg-%s-%s'"),
- plugin_name, argv[i].key);
- return 1;
- }
-
- PASS_INFO(arm_pertask_ssp_rtl, "expand", 1, PASS_POS_INSERT_AFTER);
-
- register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP,
- NULL, &arm_pertask_ssp_rtl_pass_info);
-
-#if BUILDING_GCC_VERSION >= 9000
- register_callback(plugin_info->base_name, PLUGIN_START_UNIT,
- arm_pertask_ssp_start_unit, NULL);
-#endif
-
- return 0;
-}
diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c
index 5694df3da2e9..971a1908a8cc 100644
--- a/scripts/gcc-plugins/randomize_layout_plugin.c
+++ b/scripts/gcc-plugins/randomize_layout_plugin.c
@@ -344,29 +344,13 @@ static int relayout_struct(tree type)
shuffle(type, (tree *)newtree, shuffle_length);
- /*
- * set up a bogus anonymous struct field designed to error out on unnamed struct initializers
- * as gcc provides no other way to detect such code
- */
- list = make_node(FIELD_DECL);
- TREE_CHAIN(list) = newtree[0];
- TREE_TYPE(list) = void_type_node;
- DECL_SIZE(list) = bitsize_zero_node;
- DECL_NONADDRESSABLE_P(list) = 1;
- DECL_FIELD_BIT_OFFSET(list) = bitsize_zero_node;
- DECL_SIZE_UNIT(list) = size_zero_node;
- DECL_FIELD_OFFSET(list) = size_zero_node;
- DECL_CONTEXT(list) = type;
- // to satisfy the constify plugin
- TREE_READONLY(list) = 1;
-
for (i = 0; i < num_fields - 1; i++)
TREE_CHAIN(newtree[i]) = newtree[i+1];
TREE_CHAIN(newtree[num_fields - 1]) = NULL_TREE;
main_variant = TYPE_MAIN_VARIANT(type);
for (variant = main_variant; variant; variant = TYPE_NEXT_VARIANT(variant)) {
- TYPE_FIELDS(variant) = list;
+ TYPE_FIELDS(variant) = newtree[0];
TYPE_ATTRIBUTES(variant) = copy_list(TYPE_ATTRIBUTES(variant));
TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("randomize_performed"), NULL_TREE, TYPE_ATTRIBUTES(variant));
TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("designated_init"), NULL_TREE, TYPE_ATTRIBUTES(variant));
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening
index 3fe9d7b945c4..c17366ce8224 100644
--- a/security/Kconfig.hardening
+++ b/security/Kconfig.hardening
@@ -344,7 +344,7 @@ config CC_HAS_RANDSTRUCT
choice
prompt "Randomize layout of sensitive kernel structures"
- default RANDSTRUCT_FULL if COMPILE_TEST && CC_HAS_RANDSTRUCT
+ default RANDSTRUCT_FULL if COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT)
default RANDSTRUCT_NONE
help
If you enable this, the layouts of structures that are entirely