kunit/stackinit: Use fill byte different from Clang i386 pattern
Kees Cook
maintainerPATCH 1/1 kunit/stackinit: Use fill byte different from Clang i386 pattern
The byte initialization values used with -ftrivial-auto-var-init=pattern (CONFIG_INIT_STACK_ALL_PATTERN=y) depends on the compiler, architecture, and byte position relative to struct member types. On i386 with Clang, this includes the 0xFF value, which means it looks like nothing changes between the leaf byte filling pass and the expected "stack wiping" pass of the stackinit test.
Use the byte fill value of 0x99 instead, fixing the test for i386 Clang builds.
---
Cc: Nathan Chancellor <[email protected]>
Cc: Bill Wendling <[email protected]>
Cc: Justin Stitt <[email protected]>
Cc: [email protected]
--- lib/tests/stackinit_kunit.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-)
lib/tests/stackinit_kunit.c +20 -10
@@ -184,6 +184,15 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,#define INIT_UNION_assigned_copy (var_type) \ INIT_STRUCT_assigned_copy(var_type) +/* +* The "did we actually fill the stack?" check value needs +* to be neither 0 nor any of the "pattern" bytes. The +* pattern bytes are compiler, architecture, and type based, +* so we have to pick a value that never appears for those +* combinations. Use 0x99 which is not 0xFF, 0xFE, nor 0xAA. +*/ +#define FILL_BYTE 0x99 +/* * @name: unique string name for the test * @var_type: type to be tested for zeroing initialization
@@ -206,12 +215,12 @@ static noinline void test_ ## name (struct kunit *test) \ ZERO_CLONE_ ## which(zero); \ /* Clear entire check buffer for 0xFF overlap test. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \ -/* Fill stack with 0xFF. */ \ +/* Fill stack with FILL_BYTE. */ \ ignored = leaf_ ##name((unsigned long)&ignored, 1, \ FETCH_ARG_ ## which(zero)); \ -/* Verify all bytes overwritten with 0xFF. */ \ +/* Verify all bytes overwritten with FILL_BYTE. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] !=0xFF ); \ + sum += (check_buf[i] !=FILL_BYTE ); \/* Clear entire check buffer for later bit tests. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \/* Extract stack-defined variable contents. */ \
@@ -222,7 +231,8 @@ static noinline void test_ ## name (struct kunit *test) \* possible between the two leaf function calls. \ */ \KUNIT_ASSERT_EQ_MSG (test, sum,0 , \ -"leaf fill was not 0xFF!?\n" ); \ +"leaf fill was not 0x%02X!?\n" , \ +FILL_BYTE ); \ \/* Validate that compiler lined up fill and target. */ \ KUNIT_ASSERT_TRUE_MSG(test, \
@@ -234,9 +244,9 @@ static noinline void test_ ## name (struct kunit *test) \ (int )((ssize_t )(uintptr_t )fill_start - \ (ssize_t)(uintptr_t)target_start)); \ \ -/* Look for any bytes still 0xFF in check region. */ \ +/* Validate check region has no FILL_BYTE bytes. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] ==0xFF ); \ + sum += (check_buf[i] ==FILL_BYTE ); \ \if (sum !=0 && xfail) \ kunit_skip(test, \
@@ -271,12 +281,12 @@ static noinline int leaf_ ## name(unsigned long sp, bool fill, \* stack frame of SOME kind... \ */ \memset (buf, (char )(sp &0xff ),sizeof (buf)); \ -/* Fill variable with 0xFF. */ \ +/* Fill variable with FILL_BYTE. */ \if (fill) { \ fill_start = &var; \ fill_size =sizeof (var); \memset (fill_start, \ - (char )((sp &0xff ) | forced_mask), \ +FILL_BYTE & forced_mask, \ fill_size); \ } \ \
@@ -469,7 +479,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var); -memset (fill_start, forced_mask |0x55 , fill_size); +memset (fill_start, (forced_mask |0x55 ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;
@@ -480,7 +490,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var); -memset (fill_start, forced_mask |0xaa , fill_size); +memset (fill_start, (forced_mask |0xaa ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;
Signature · 1 line
2.34.1
Hi,
Quoted 14 lines · Kees Cook
The byte initialization values used with -ftrivial-auto-var-init=pattern (
CONFIG_INIT_STACK_ALL_PATTERN=y) depends on the compiler, architecture, and byte position relative to struct member types. On i386 with Clang, this includes the0xFFvalue, which means it looks like nothing changes between the leaf byte filling pass and the expected "stack wiping" pass of the stackinit test.Use the byte fill value of
0x99instead, fixing the test for i386 Clang builds.Reported-by ernsteiswuerfelFixes 8c30d32b1a32 ("lib/test_stackinit: Handle Clang auto-initialization pattern")Signed-off-by Kees Cook <[email protected]>
Quoted 7 lines
---
Cc: Nathan Chancellor <[email protected]>
Cc: Bill Wendling <[email protected]>
Cc: Justin Stitt <[email protected]>
Cc: [email protected]--- lib/tests/stackinit_kunit.c | 30 ++++++++++++++++++++----------
Ah, couldn't find this file at first. Depends on [1].
Quoted 39 lines
1 file changed, 20 insertions(+), 10 deletions(-)lib/tests/stackinit_kunit.c +12
-3@@ -184,6 +184,15 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,#define INIT_UNION_assigned_copy (var_type) \ INIT_STRUCT_assigned_copy(var_type) +/* +* The "did we actually fill the stack?" check value needs +* to be neither 0 nor any of the "pattern" bytes. The +* pattern bytes are compiler, architecture, and type based, +* so we have to pick a value that never appears for those +* combinations. Use 0x99 which is not 0xFF, 0xFE, nor 0xAA. +*/ +#define FILL_BYTE 0x99 +/* * @name: unique string name for the test * @var_type: type to be tested for zeroing initialization @@ -206,12 +215,12 @@ static noinline void test_ ## name (struct kunit *test) \ ZERO_CLONE_ ## which(zero); \ /* Clear entire check buffer for 0xFF overlap test. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \ -/* Fill stack with 0xFF. */ \ +/* Fill stack with FILL_BYTE. */ \ ignored = leaf_ ##name((unsigned long)&ignored, 1, \ FETCH_ARG_ ## which(zero)); \ -/* Verify all bytes overwritten with 0xFF. */ \ +/* Verify all bytes overwritten with FILL_BYTE. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] !=0xFF ); \ + sum += (check_buf[i] !=FILL_BYTE ); \/* Clear entire check buffer for later bit tests. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \/* Extract stack-defined variable contents. */ \
lib/tests/stackinit_kunit.c static noinline void test_ ## name (struct kunit *test) \ · line 235
Quoted 6 lines
lib/tests/stackinit_kunit.c +2
-1@@ -222,7 +231,8 @@ static noinline void test_ ## name (struct kunit *test) \* possible between the two leaf function calls. \ */ \KUNIT_ASSERT_EQ_MSG (test, sum,0 , \ -"leaf fill was not 0xFF!?\n" ); \ +"leaf fill was not 0x%02X!?\n" , \ +FILL_BYTE ); \
KUNIT_ASSERT_EQ_MSG(test, sum, 0, \
- "leaf fill was not 0xFF!?\n"); \ + "leaf fill was not 0x%02X!??!?!?!?!?!?!?!\n", \ + FILL_BYTE); \
Quoted 52 lines
lib/tests/stackinit_kunit.c +6
-6\/* Validate that compiler lined up fill and target. */ \ KUNIT_ASSERT_TRUE_MSG(test, \@@ -234,9 +244,9 @@ static noinline void test_ ## name (struct kunit *test) \ (int )((ssize_t )(uintptr_t )fill_start - \ (ssize_t)(uintptr_t)target_start)); \ \ -/* Look for any bytes still 0xFF in check region. */ \ +/* Validate check region has no FILL_BYTE bytes. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] ==0xFF ); \ + sum += (check_buf[i] ==FILL_BYTE ); \ \if (sum !=0 && xfail) \ kunit_skip(test, \@@ -271,12 +281,12 @@ static noinline int leaf_ ## name(unsigned long sp, bool fill, \* stack frame of SOME kind... \ */ \memset (buf, (char )(sp &0xff ),sizeof (buf)); \ -/* Fill variable with 0xFF. */ \ +/* Fill variable with FILL_BYTE. */ \if (fill) { \ fill_start = &var; \ fill_size =sizeof (var); \memset (fill_start, \ - (char )((sp &0xff ) | forced_mask), \ +FILL_BYTE & forced_mask, \ fill_size); \ } \ \@@ -469,7 +479,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var); -memset (fill_start, forced_mask |0x55 , fill_size); +memset (fill_start, (forced_mask |0x55 ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;@@ -480,7 +490,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var); -memset (fill_start, forced_mask |0xaa , fill_size); +memset (fill_start, (forced_mask |0xaa ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;Signature · 1 line
2.34.1
I don't understand the stack init pattern tendencies enough to give a RB but I looked at your patch and it seems to fully replace the fill test values from 0xff to 0x99, so if 0x99 is OK then OK.
[1]: https://lore.kernel.org/all/[email protected]/
Justin
Kees Cook
maintainerQuoted 74 lines · Justin Stitt
Hi,
Quoted 14 lines · Kees Cook
The byte initialization values used with -ftrivial-auto-var-init=pattern (
CONFIG_INIT_STACK_ALL_PATTERN=y) depends on the compiler, architecture, and byte position relative to struct member types. On i386 with Clang, this includes the0xFFvalue, which means it looks like nothing changes between the leaf byte filling pass and the expected "stack wiping" pass of the stackinit test.Use the byte fill value of
0x99instead, fixing the test for i386 Clang builds.Reported-by ernsteiswuerfelFixes 8c30d32b1a32 ("lib/test_stackinit: Handle Clang auto-initialization pattern")Signed-off-by Kees Cook <[email protected]>Quoted 7 lines
---
Cc: Nathan Chancellor <[email protected]>
Cc: Bill Wendling <[email protected]>
Cc: Justin Stitt <[email protected]>
Cc: [email protected]--- lib/tests/stackinit_kunit.c | 30 ++++++++++++++++++++----------Ah, couldn't find this file at first. Depends on [1].
Quoted 39 lines
1 file changed, 20 insertions(+), 10 deletions(-)lib/tests/stackinit_kunit.c +12
-3@@ -184,6 +184,15 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,#define INIT_UNION_assigned_copy (var_type) \ INIT_STRUCT_assigned_copy(var_type) +/* +* The "did we actually fill the stack?" check value needs +* to be neither 0 nor any of the "pattern" bytes. The +* pattern bytes are compiler, architecture, and type based, +* so we have to pick a value that never appears for those +* combinations. Use 0x99 which is not 0xFF, 0xFE, nor 0xAA. +*/ +#define FILL_BYTE 0x99 +/* * @name: unique string name for the test * @var_type: type to be tested for zeroing initialization @@ -206,12 +215,12 @@ static noinline void test_ ## name (struct kunit *test) \ ZERO_CLONE_ ## which(zero); \ /* Clear entire check buffer for 0xFF overlap test. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \ -/* Fill stack with 0xFF. */ \ +/* Fill stack with FILL_BYTE. */ \ ignored = leaf_ ##name((unsigned long)&ignored, 1, \ FETCH_ARG_ ## which(zero)); \ -/* Verify all bytes overwritten with 0xFF. */ \ +/* Verify all bytes overwritten with FILL_BYTE. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] !=0xFF ); \ + sum += (check_buf[i] !=FILL_BYTE ); \/* Clear entire check buffer for later bit tests. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \/* Extract stack-defined variable contents. */ \lib/tests/stackinit_kunit.c static noinline void test_ ## name (struct kunit *test) \ · line 235
Quoted 6 lines
lib/tests/stackinit_kunit.c +2
-1@@ -222,7 +231,8 @@ static noinline void test_ ## name (struct kunit *test) \* possible between the two leaf function calls. \ */ \KUNIT_ASSERT_EQ_MSG (test, sum,0 , \ -"leaf fill was not 0xFF!?\n" ); \ +"leaf fill was not 0x%02X!?\n" , \ +FILL_BYTE ); \KUNIT_ASSERT_EQ_MSG(test, sum, 0, \
Quoted 3 lines
- "leaf fill was not 0xFF!?\n"); \ + "leaf fill was not 0x%02X!??!?!?!?!?!?!?!\n", \ + FILL_BYTE); \
OMGWTFBBQ!!
Quoted 44 lines
Quoted 44 lines
lib/tests/stackinit_kunit.c +5
-5\/* Validate that compiler lined up fill and target. */ \ KUNIT_ASSERT_TRUE_MSG(test, \@@ -234,9 +244,9 @@ static noinline void test_ ## name (struct kunit *test) \ (int )((ssize_t )(uintptr_t )fill_start - \ (ssize_t)(uintptr_t)target_start)); \ \ -/* Look for any bytes still 0xFF in check region. */ \ +/* Validate check region has no FILL_BYTE bytes. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] ==0xFF ); \ + sum += (check_buf[i] ==FILL_BYTE ); \ \if (sum !=0 && xfail) \ kunit_skip(test, \@@ -271,12 +281,12 @@ static noinline int leaf_ ## name(unsigned long sp, bool fill, \* stack frame of SOME kind... \ */ \memset (buf, (char )(sp &0xff ),sizeof (buf)); \ -/* Fill variable with 0xFF. */ \ +/* Fill variable with FILL_BYTE. */ \if (fill) { \ fill_start = &var; \ fill_size =sizeof (var); \memset (fill_start, \ - (char )((sp &0xff ) | forced_mask), \ +FILL_BYTE & forced_mask, \ fill_size); \ } \ \@@ -469,7 +479,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var); -memset (fill_start, forced_mask |0x55 , fill_size); +memset (fill_start, (forced_mask |0x55 ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;@@ -480,7 +490,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var);
Quoted 13 lines
Quoted 9 lines
lib/tests/stackinit_kunit.c +1
-1-memset (fill_start, forced_mask |0xaa , fill_size); +memset (fill_start, (forced_mask |0xaa ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;Signature · 1 line
2.34.1I don't understand the stack init pattern tendencies enough to give a RB but I looked at your patch and it seems to fully replace the fill test values from
0xffto0x99, so if0x99is OK then OK.
Yeah, I had to go do some godbolting to remind myself what value were being used. :)
Thanks for the double-check!
-Kees
Quoted 4 lines
[1]: https://lore.kernel.org/all/[email protected]/
Justin
Signature · 1 line
Kees Cook
Quoted 7 lines · Kees Cook
The byte initialization values used with -ftrivial-auto-var-init=pattern (
CONFIG_INIT_STACK_ALL_PATTERN=y) depends on the compiler, architecture, and byte position relative to struct member types. On i386 with Clang, this includes the0xFFvalue, which means it looks like nothing changes between the leaf byte filling pass and the expected "stack wiping" pass of the stackinit test.
Quoted 7 lines
Use the byte fill value of
0x99instead, fixing the test for i386 Clang builds.Reported-by ernsteiswuerfelFixes 8c30d32b1a32 ("lib/test_stackinit: Handle Clang auto-initialization pattern")Signed-off-by Kees Cook <[email protected]>
stackinit passes with arm, arm64, i386, and x86_64 when using LLVM 20.1.0-rc3. Hopefully they do not change the init pattern :)
Quoted 103 lines
---
Cc: Nathan Chancellor <[email protected]>
Cc: Bill Wendling <[email protected]>
Cc: Justin Stitt <[email protected]>
Cc: [email protected]--- lib/tests/stackinit_kunit.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-)lib/tests/stackinit_kunit.c +20
-10@@ -184,6 +184,15 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,#define INIT_UNION_assigned_copy (var_type) \ INIT_STRUCT_assigned_copy(var_type) +/* +* The "did we actually fill the stack?" check value needs +* to be neither 0 nor any of the "pattern" bytes. The +* pattern bytes are compiler, architecture, and type based, +* so we have to pick a value that never appears for those +* combinations. Use 0x99 which is not 0xFF, 0xFE, nor 0xAA. +*/ +#define FILL_BYTE 0x99 +/* * @name: unique string name for the test * @var_type: type to be tested for zeroing initialization @@ -206,12 +215,12 @@ static noinline void test_ ## name (struct kunit *test) \ ZERO_CLONE_ ## which(zero); \ /* Clear entire check buffer for 0xFF overlap test. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \ -/* Fill stack with 0xFF. */ \ +/* Fill stack with FILL_BYTE. */ \ ignored = leaf_ ##name((unsigned long)&ignored, 1, \ FETCH_ARG_ ## which(zero)); \ -/* Verify all bytes overwritten with 0xFF. */ \ +/* Verify all bytes overwritten with FILL_BYTE. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] !=0xFF ); \ + sum += (check_buf[i] !=FILL_BYTE ); \/* Clear entire check buffer for later bit tests. */ \memset (check_buf,0x00 ,sizeof (check_buf)); \/* Extract stack-defined variable contents. */ \@@ -222,7 +231,8 @@ static noinline void test_ ## name (struct kunit *test) \* possible between the two leaf function calls. \ */ \KUNIT_ASSERT_EQ_MSG (test, sum,0 , \ -"leaf fill was not 0xFF!?\n" ); \ +"leaf fill was not 0x%02X!?\n" , \ +FILL_BYTE ); \ \/* Validate that compiler lined up fill and target. */ \ KUNIT_ASSERT_TRUE_MSG(test, \@@ -234,9 +244,9 @@ static noinline void test_ ## name (struct kunit *test) \ (int )((ssize_t )(uintptr_t )fill_start - \ (ssize_t)(uintptr_t)target_start)); \ \ -/* Look for any bytes still 0xFF in check region. */ \ +/* Validate check region has no FILL_BYTE bytes. */ \for (sum =0 , i =0 ; i < target_size; i++) \ - sum += (check_buf[i] ==0xFF ); \ + sum += (check_buf[i] ==FILL_BYTE ); \ \if (sum !=0 && xfail) \ kunit_skip(test, \@@ -271,12 +281,12 @@ static noinline int leaf_ ## name(unsigned long sp, bool fill, \* stack frame of SOME kind... \ */ \memset (buf, (char )(sp &0xff ),sizeof (buf)); \ -/* Fill variable with 0xFF. */ \ +/* Fill variable with FILL_BYTE. */ \if (fill) { \ fill_start = &var; \ fill_size =sizeof (var); \memset (fill_start, \ - (char )((sp &0xff ) | forced_mask), \ +FILL_BYTE & forced_mask, \ fill_size); \ } \ \@@ -469,7 +479,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var); -memset (fill_start, forced_mask |0x55 , fill_size); +memset (fill_start, (forced_mask |0x55 ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;@@ -480,7 +490,7 @@ static int noinline __leaf_switch_none(int path, bool fill) fill_start = &var; fill_size =sizeof (var); -memset (fill_start, forced_mask |0xaa , fill_size); +memset (fill_start, (forced_mask |0xaa ) &FILL_BYTE , fill_size); }memcpy (check_buf, target_start, target_size);break ;Signature · 1 line
2.34.1
Kees Cook
maintainerQuoted 8 lines · Kees Cook
The byte initialization values used with -ftrivial-auto-var-init=pattern (
CONFIG_INIT_STACK_ALL_PATTERN=y) depends on the compiler, architecture, and byte position relative to struct member types. On i386 with Clang, this includes the0xFFvalue, which means it looks like nothing changes between the leaf byte filling pass and the expected "stack wiping" pass of the stackinit test.[...]
Applied to for-next/move-kunit-tests.
[1/1] kunit/stackinit: Use fill byte different from Clang i386 pattern
https://git.kernel.org/kees/c/d985e4399adf
Signature · 1 line
Kees Cook