hardening updates for v6.16-rc1

- 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
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRSPkdeREjth1dHnSE2KwveOeQkuwUCaDUq9gAKCRA2KwveOeQk
 u+ZCAQDhqpOE/yn5gfjyplIvaTtzj9CaW6g11AmPYrimJCuj3QD9G+0o35kzlXOw
 f0ZIj2U7LFNgbLos+20hQwhMFf1Zhgg=
 =OYzD
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2025-05-28 07:47:10 -07:00
commit 48cfc5791d
30 changed files with 459 additions and 169 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@
!defined(CONFIG_MICROBLAZE) && \
!defined(CONFIG_NIOS2) && \
!defined(CONFIG_PPC32) && \
!defined(CONFIG_SPARC32) && \
!defined(CONFIG_SUPERH))
# define TEST_U64
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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