Skip to content

Commit 73512f2

Browse files
committed
Merge tag 'hardening-v6.14-rc1-fix1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull hardening fixes from Kees Cook: "This is a fix for the soon to be released GCC 15 which has regressed its initialization of unions when performing explicit initialization (i.e. a general problem, not specifically a hardening problem; we're just carrying the fix). Details in the final patch, Acked by Masahiro, with updated selftests to validate the fix" * tag 'hardening-v6.14-rc1-fix1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: kbuild: Use -fzero-init-padding-bits=all stackinit: Add union initialization to selftests stackinit: Add old-style zero-init syntax to struct tests
2 parents 851faa8 + dce4aab commit 73512f2

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

lib/stackinit_kunit.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,26 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,
4747
#define DO_NOTHING_TYPE_SCALAR(var_type) var_type
4848
#define DO_NOTHING_TYPE_STRING(var_type) void
4949
#define DO_NOTHING_TYPE_STRUCT(var_type) void
50+
#define DO_NOTHING_TYPE_UNION(var_type) void
5051

5152
#define DO_NOTHING_RETURN_SCALAR(ptr) *(ptr)
5253
#define DO_NOTHING_RETURN_STRING(ptr) /**/
5354
#define DO_NOTHING_RETURN_STRUCT(ptr) /**/
55+
#define DO_NOTHING_RETURN_UNION(ptr) /**/
5456

5557
#define DO_NOTHING_CALL_SCALAR(var, name) \
5658
(var) = do_nothing_ ## name(&(var))
5759
#define DO_NOTHING_CALL_STRING(var, name) \
5860
do_nothing_ ## name(var)
5961
#define DO_NOTHING_CALL_STRUCT(var, name) \
6062
do_nothing_ ## name(&(var))
63+
#define DO_NOTHING_CALL_UNION(var, name) \
64+
do_nothing_ ## name(&(var))
6165

6266
#define FETCH_ARG_SCALAR(var) &var
6367
#define FETCH_ARG_STRING(var) var
6468
#define FETCH_ARG_STRUCT(var) &var
69+
#define FETCH_ARG_UNION(var) &var
6570

6671
/*
6772
* On m68k, if the leaf function test variable is longer than 8 bytes,
@@ -77,6 +82,7 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,
7782
#define INIT_CLONE_SCALAR /**/
7883
#define INIT_CLONE_STRING [FILL_SIZE_STRING]
7984
#define INIT_CLONE_STRUCT /**/
85+
#define INIT_CLONE_UNION /**/
8086

8187
#define ZERO_CLONE_SCALAR(zero) memset(&(zero), 0x00, sizeof(zero))
8288
#define ZERO_CLONE_STRING(zero) memset(&(zero), 0x00, sizeof(zero))
@@ -92,6 +98,7 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,
9298
zero.three = 0; \
9399
zero.four = 0; \
94100
} while (0)
101+
#define ZERO_CLONE_UNION(zero) ZERO_CLONE_STRUCT(zero)
95102

96103
#define INIT_SCALAR_none(var_type) /**/
97104
#define INIT_SCALAR_zero(var_type) = 0
@@ -101,6 +108,7 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,
101108

102109
#define INIT_STRUCT_none(var_type) /**/
103110
#define INIT_STRUCT_zero(var_type) = { }
111+
#define INIT_STRUCT_old_zero(var_type) = { 0 }
104112

105113

106114
#define __static_partial { .two = 0, }
@@ -146,6 +154,34 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,
146154
#define INIT_STRUCT_assigned_copy(var_type) \
147155
; var = *(arg)
148156

157+
/* Union initialization is the same as structs. */
158+
#define INIT_UNION_none(var_type) INIT_STRUCT_none(var_type)
159+
#define INIT_UNION_zero(var_type) INIT_STRUCT_zero(var_type)
160+
#define INIT_UNION_old_zero(var_type) INIT_STRUCT_old_zero(var_type)
161+
162+
#define INIT_UNION_static_partial(var_type) \
163+
INIT_STRUCT_static_partial(var_type)
164+
#define INIT_UNION_static_all(var_type) \
165+
INIT_STRUCT_static_all(var_type)
166+
#define INIT_UNION_dynamic_partial(var_type) \
167+
INIT_STRUCT_dynamic_partial(var_type)
168+
#define INIT_UNION_dynamic_all(var_type) \
169+
INIT_STRUCT_dynamic_all(var_type)
170+
#define INIT_UNION_runtime_partial(var_type) \
171+
INIT_STRUCT_runtime_partial(var_type)
172+
#define INIT_UNION_runtime_all(var_type) \
173+
INIT_STRUCT_runtime_all(var_type)
174+
#define INIT_UNION_assigned_static_partial(var_type) \
175+
INIT_STRUCT_assigned_static_partial(var_type)
176+
#define INIT_UNION_assigned_static_all(var_type) \
177+
INIT_STRUCT_assigned_static_all(var_type)
178+
#define INIT_UNION_assigned_dynamic_partial(var_type) \
179+
INIT_STRUCT_assigned_dynamic_partial(var_type)
180+
#define INIT_UNION_assigned_dynamic_all(var_type) \
181+
INIT_STRUCT_assigned_dynamic_all(var_type)
182+
#define INIT_UNION_assigned_copy(var_type) \
183+
INIT_STRUCT_assigned_copy(var_type)
184+
149185
/*
150186
* @name: unique string name for the test
151187
* @var_type: type to be tested for zeroing initialization
@@ -294,6 +330,33 @@ struct test_user {
294330
unsigned long four;
295331
};
296332

333+
/* No padding: all members are the same size. */
334+
union test_same_sizes {
335+
unsigned long one;
336+
unsigned long two;
337+
unsigned long three;
338+
unsigned long four;
339+
};
340+
341+
/* Mismatched sizes, with one and two being small */
342+
union test_small_start {
343+
char one:1;
344+
char two;
345+
short three;
346+
unsigned long four;
347+
struct big_struct {
348+
unsigned long array[8];
349+
} big;
350+
};
351+
352+
/* Mismatched sizes, with one and two being small */
353+
union test_small_end {
354+
short one;
355+
unsigned long two;
356+
char three:1;
357+
char four;
358+
};
359+
297360
#define ALWAYS_PASS WANT_SUCCESS
298361
#define ALWAYS_FAIL XFAIL
299362

@@ -332,6 +395,11 @@ struct test_user {
332395
struct test_ ## name, STRUCT, init, \
333396
xfail)
334397

398+
#define DEFINE_UNION_TEST(name, init, xfail) \
399+
DEFINE_TEST(name ## _ ## init, \
400+
union test_ ## name, STRUCT, init, \
401+
xfail)
402+
335403
#define DEFINE_STRUCT_TESTS(init, xfail) \
336404
DEFINE_STRUCT_TEST(small_hole, init, xfail); \
337405
DEFINE_STRUCT_TEST(big_hole, init, xfail); \
@@ -343,16 +411,35 @@ struct test_user {
343411
xfail); \
344412
DEFINE_STRUCT_TESTS(base ## _ ## all, xfail)
345413

414+
#define DEFINE_UNION_INITIALIZER_TESTS(base, xfail) \
415+
DEFINE_UNION_TESTS(base ## _ ## partial, \
416+
xfail); \
417+
DEFINE_UNION_TESTS(base ## _ ## all, xfail)
418+
419+
#define DEFINE_UNION_TESTS(init, xfail) \
420+
DEFINE_UNION_TEST(same_sizes, init, xfail); \
421+
DEFINE_UNION_TEST(small_start, init, xfail); \
422+
DEFINE_UNION_TEST(small_end, init, xfail);
423+
346424
/* These should be fully initialized all the time! */
347425
DEFINE_SCALAR_TESTS(zero, ALWAYS_PASS);
348426
DEFINE_STRUCT_TESTS(zero, ALWAYS_PASS);
427+
DEFINE_STRUCT_TESTS(old_zero, ALWAYS_PASS);
428+
DEFINE_UNION_TESTS(zero, ALWAYS_PASS);
429+
DEFINE_UNION_TESTS(old_zero, ALWAYS_PASS);
349430
/* Struct initializers: padding may be left uninitialized. */
350431
DEFINE_STRUCT_INITIALIZER_TESTS(static, STRONG_PASS);
351432
DEFINE_STRUCT_INITIALIZER_TESTS(dynamic, STRONG_PASS);
352433
DEFINE_STRUCT_INITIALIZER_TESTS(runtime, STRONG_PASS);
353434
DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static, STRONG_PASS);
354435
DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS);
355436
DEFINE_STRUCT_TESTS(assigned_copy, ALWAYS_FAIL);
437+
DEFINE_UNION_INITIALIZER_TESTS(static, STRONG_PASS);
438+
DEFINE_UNION_INITIALIZER_TESTS(dynamic, STRONG_PASS);
439+
DEFINE_UNION_INITIALIZER_TESTS(runtime, STRONG_PASS);
440+
DEFINE_UNION_INITIALIZER_TESTS(assigned_static, STRONG_PASS);
441+
DEFINE_UNION_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS);
442+
DEFINE_UNION_TESTS(assigned_copy, ALWAYS_FAIL);
356443
/* No initialization without compiler instrumentation. */
357444
DEFINE_SCALAR_TESTS(none, STRONG_PASS);
358445
DEFINE_STRUCT_TESTS(none, BYREF_PASS);
@@ -436,13 +523,23 @@ DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, ALWAYS_FAIL);
436523
KUNIT_CASE(test_trailing_hole_ ## init),\
437524
KUNIT_CASE(test_packed_ ## init) \
438525

526+
#define KUNIT_test_unions(init) \
527+
KUNIT_CASE(test_same_sizes_ ## init), \
528+
KUNIT_CASE(test_small_start_ ## init), \
529+
KUNIT_CASE(test_small_end_ ## init) \
530+
439531
static struct kunit_case stackinit_test_cases[] = {
440532
/* These are explicitly initialized and should always pass. */
441533
KUNIT_test_scalars(zero),
442534
KUNIT_test_structs(zero),
535+
KUNIT_test_structs(old_zero),
536+
KUNIT_test_unions(zero),
537+
KUNIT_test_unions(old_zero),
443538
/* Padding here appears to be accidentally always initialized? */
444539
KUNIT_test_structs(dynamic_partial),
445540
KUNIT_test_structs(assigned_dynamic_partial),
541+
KUNIT_test_unions(dynamic_partial),
542+
KUNIT_test_unions(assigned_dynamic_partial),
446543
/* Padding initialization depends on compiler behaviors. */
447544
KUNIT_test_structs(static_partial),
448545
KUNIT_test_structs(static_all),
@@ -452,8 +549,17 @@ static struct kunit_case stackinit_test_cases[] = {
452549
KUNIT_test_structs(assigned_static_partial),
453550
KUNIT_test_structs(assigned_static_all),
454551
KUNIT_test_structs(assigned_dynamic_all),
552+
KUNIT_test_unions(static_partial),
553+
KUNIT_test_unions(static_all),
554+
KUNIT_test_unions(dynamic_all),
555+
KUNIT_test_unions(runtime_partial),
556+
KUNIT_test_unions(runtime_all),
557+
KUNIT_test_unions(assigned_static_partial),
558+
KUNIT_test_unions(assigned_static_all),
559+
KUNIT_test_unions(assigned_dynamic_all),
455560
/* Everything fails this since it effectively performs a memcpy(). */
456561
KUNIT_test_structs(assigned_copy),
562+
KUNIT_test_unions(assigned_copy),
457563
/* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
458564
KUNIT_test_scalars(none),
459565
KUNIT_CASE(test_switch_1_none),

scripts/Makefile.extrawarn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init)
7777
# Warn if there is an enum types mismatch
7878
KBUILD_CFLAGS += $(call cc-option,-Wenum-conversion)
7979

80+
# Explicitly clear padding bits during variable initialization
81+
KBUILD_CFLAGS += $(call cc-option,-fzero-init-padding-bits=all)
82+
8083
KBUILD_CFLAGS += -Wextra
8184
KBUILD_CFLAGS += -Wunused
8285

0 commit comments

Comments
 (0)