mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
synced 2025-09-29 12:19:07 +10:00
lib/tests: Add randstruct KUnit test
Perform basic validation about layout randomization and initialization tracking when using CONFIG_RANDSTRUCT=y. Tested using: $ ./tools/testing/kunit/kunit.py run \ --kconfig_add CONFIG_RANDSTRUCT_FULL=y \ randstruct [17:22:30] ================= randstruct (2 subtests) ================== [17:22:30] [PASSED] randstruct_layout [17:22:30] [PASSED] randstruct_initializers [17:22:30] =================== [PASSED] randstruct ==================== [17:22:30] ============================================================ [17:22:30] Testing complete. Ran 2 tests: passed: 2 [17:22:30] Elapsed time: 5.091s total, 0.001s configuring, 4.974s building, 0.086s running Adding "--make_option LLVM=1" can be used to test Clang, which also passes. Acked-by: David Gow <davidgow@google.com> Signed-off-by: Kees Cook <kees@kernel.org>
This commit is contained in:
parent
e136a40621
commit
b370f7eacd
@ -12892,6 +12892,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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
283
lib/tests/randstruct_kunit.c
Normal file
283
lib/tests/randstruct_kunit.c
Normal file
@ -0,0 +1,283 @@
|
||||
// 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)
|
||||
};
|
||||
#undef 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;
|
||||
};
|
||||
|
||||
static void randstruct_layout(struct kunit *test)
|
||||
{
|
||||
int mismatches;
|
||||
|
||||
#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) \
|
||||
mismatches = 0; \
|
||||
DO_MANY_MEMBERS(check_mismatch, 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");
|
||||
|
||||
check_pair(EXPECT_EQ, struct randstruct_untouched, struct randstruct_untouched)
|
||||
check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_shuffled)
|
||||
check_pair(EXPECT_GT, struct randstruct_untouched, struct randstruct_funcs_shuffled)
|
||||
check_pair(EXPECT_GT, struct randstruct_funcs_untouched, struct randstruct_funcs_shuffled)
|
||||
check_pair(EXPECT_GT, struct randstruct_mixed_untouched, struct randstruct_mixed_shuffled)
|
||||
#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),
|
||||
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");
|
Loading…
Reference in New Issue
Block a user