diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Kconfig.debug | 8 | ||||
-rw-r--r-- | lib/Makefile | 1 | ||||
-rw-r--r-- | lib/crc32.c | 6 | ||||
-rw-r--r-- | lib/find_bit.c | 11 | ||||
-rw-r--r-- | lib/pldmfw/pldmfw.c | 6 | ||||
-rw-r--r-- | lib/ratelimit.c | 75 | ||||
-rw-r--r-- | lib/string_helpers.c | 39 | ||||
-rw-r--r-- | lib/test_sysctl.c | 131 | ||||
-rw-r--r-- | lib/tests/Makefile | 1 | ||||
-rw-r--r-- | lib/tests/crc_kunit.c | 6 | ||||
-rw-r--r-- | lib/tests/overflow_kunit.c | 4 | ||||
-rw-r--r-- | lib/tests/printf_kunit.c | 39 | ||||
-rw-r--r-- | lib/tests/randstruct_kunit.c | 334 | ||||
-rw-r--r-- | lib/tests/usercopy_kunit.c | 1 | ||||
-rw-r--r-- | lib/vsprintf.c | 50 |
15 files changed, 597 insertions, 115 deletions
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/crc32.c b/lib/crc32.c index e690026f44f7..95429861d3ac 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -117,12 +117,6 @@ u32 crc32_le_shift(u32 crc, size_t len) } EXPORT_SYMBOL(crc32_le_shift); -u32 crc32c_shift(u32 crc, size_t len) -{ - return crc32_generic_shift(crc, len, CRC32C_POLY_LE); -} -EXPORT_SYMBOL(crc32c_shift); - u32 crc32_be_base(u32 crc, const u8 *p, size_t len) { while (len--) diff --git a/lib/find_bit.c b/lib/find_bit.c index 0836bb3d76c5..06b6342aa3ae 100644 --- a/lib/find_bit.c +++ b/lib/find_bit.c @@ -117,6 +117,17 @@ EXPORT_SYMBOL(_find_first_and_bit); #endif /* + * Find the first bit set in 1st memory region and unset in 2nd. + */ +unsigned long _find_first_andnot_bit(const unsigned long *addr1, + const unsigned long *addr2, + unsigned long size) +{ + return FIND_FIRST_BIT(addr1[idx] & ~addr2[idx], /* nop */, size); +} +EXPORT_SYMBOL(_find_first_andnot_bit); + +/* * Find the first set bit in three memory regions. */ unsigned long _find_first_and_and_bit(const unsigned long *addr1, diff --git a/lib/pldmfw/pldmfw.c b/lib/pldmfw/pldmfw.c index 6264e2013f25..b45ceb725780 100644 --- a/lib/pldmfw/pldmfw.c +++ b/lib/pldmfw/pldmfw.c @@ -728,6 +728,9 @@ pldm_send_package_data(struct pldmfw_priv *data) struct pldmfw_record *record = data->matching_record; const struct pldmfw_ops *ops = data->context->ops; + if (!ops->send_package_data) + return 0; + return ops->send_package_data(data->context, record->package_data, record->package_data_len); } @@ -755,6 +758,9 @@ pldm_send_component_tables(struct pldmfw_priv *data) if (!test_bit(index, bitmap)) continue; + if (!data->context->ops->send_component_table) + continue; + /* determine whether this is the start, middle, end, or both * the start and end of the component tables */ diff --git a/lib/ratelimit.c b/lib/ratelimit.c index ce945c17980b..859c251b23ce 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c @@ -33,44 +33,73 @@ int ___ratelimit(struct ratelimit_state *rs, const char *func) int interval = READ_ONCE(rs->interval); int burst = READ_ONCE(rs->burst); unsigned long flags; - int ret; + int ret = 0; - if (!interval) - return 1; + /* + * Zero interval says never limit, otherwise, non-positive burst + * says always limit. + */ + if (interval <= 0 || burst <= 0) { + WARN_ONCE(interval < 0 || burst < 0, "Negative interval (%d) or burst (%d): Uninitialized ratelimit_state structure?\n", interval, burst); + ret = interval == 0 || burst > 0; + if (!(READ_ONCE(rs->flags) & RATELIMIT_INITIALIZED) || (!interval && !burst) || + !raw_spin_trylock_irqsave(&rs->lock, flags)) + goto nolock_ret; + + /* Force re-initialization once re-enabled. */ + rs->flags &= ~RATELIMIT_INITIALIZED; + goto unlock_ret; + } /* - * If we contend on this state's lock then almost - * by definition we are too busy to print a message, - * in addition to the one that will be printed by - * the entity that is holding the lock already: + * If we contend on this state's lock then just check if + * the current burst is used or not. It might cause + * false positive when we are past the interval and + * the current lock owner is just about to reset it. */ - if (!raw_spin_trylock_irqsave(&rs->lock, flags)) - return 0; + if (!raw_spin_trylock_irqsave(&rs->lock, flags)) { + if (READ_ONCE(rs->flags) & RATELIMIT_INITIALIZED && + atomic_read(&rs->rs_n_left) > 0 && atomic_dec_return(&rs->rs_n_left) >= 0) + ret = 1; + goto nolock_ret; + } - if (!rs->begin) + if (!(rs->flags & RATELIMIT_INITIALIZED)) { rs->begin = jiffies; + rs->flags |= RATELIMIT_INITIALIZED; + atomic_set(&rs->rs_n_left, rs->burst); + } if (time_is_before_jiffies(rs->begin + interval)) { - if (rs->missed) { - if (!(rs->flags & RATELIMIT_MSG_ON_RELEASE)) { + int m; + + /* + * Reset rs_n_left ASAP to reduce false positives + * in parallel calls, see above. + */ + atomic_set(&rs->rs_n_left, rs->burst); + rs->begin = jiffies; + + if (!(rs->flags & RATELIMIT_MSG_ON_RELEASE)) { + m = ratelimit_state_reset_miss(rs); + if (m) { printk_deferred(KERN_WARNING - "%s: %d callbacks suppressed\n", - func, rs->missed); - rs->missed = 0; + "%s: %d callbacks suppressed\n", func, m); } } - rs->begin = jiffies; - rs->printed = 0; } - if (burst && burst > rs->printed) { - rs->printed++; + + /* Note that the burst might be taken by a parallel call. */ + if (atomic_read(&rs->rs_n_left) > 0 && atomic_dec_return(&rs->rs_n_left) >= 0) ret = 1; - } else { - rs->missed++; - ret = 0; - } + +unlock_ret: raw_spin_unlock_irqrestore(&rs->lock, flags); +nolock_ret: + if (!ret) + ratelimit_state_inc_miss(rs); + return ret; } EXPORT_SYMBOL(___ratelimit); diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 91fa37b5c510..ffb8ead6d4cd 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -138,6 +138,25 @@ int string_get_size(u64 size, u64 blk_size, const enum string_size_units units, } EXPORT_SYMBOL(string_get_size); +int parse_int_array(const char *buf, size_t count, int **array) +{ + int *ints, nints; + + get_options(buf, 0, &nints); + if (!nints) + return -ENOENT; + + ints = kcalloc(nints + 1, sizeof(*ints), GFP_KERNEL); + if (!ints) + return -ENOMEM; + + get_options(buf, nints + 1, ints); + *array = ints; + + return 0; +} +EXPORT_SYMBOL(parse_int_array); + /** * parse_int_array_user - Split string into a sequence of integers * @from: The user space buffer to read from @@ -153,30 +172,14 @@ EXPORT_SYMBOL(string_get_size); */ int parse_int_array_user(const char __user *from, size_t count, int **array) { - int *ints, nints; char *buf; - int ret = 0; + int ret; buf = memdup_user_nul(from, count); if (IS_ERR(buf)) return PTR_ERR(buf); - get_options(buf, 0, &nints); - if (!nints) { - ret = -ENOENT; - goto free_buf; - } - - ints = kcalloc(nints + 1, sizeof(*ints), GFP_KERNEL); - if (!ints) { - ret = -ENOMEM; - goto free_buf; - } - - get_options(buf, nints + 1, ints); - *array = ints; - -free_buf: + ret = parse_int_array(buf, count, array); kfree(buf); return ret; } diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c index 4249e0cc8aaf..c02aa9c868f2 100644 --- a/lib/test_sysctl.c +++ b/lib/test_sysctl.c @@ -30,15 +30,17 @@ static int i_zero; static int i_one_hundred = 100; static int match_int_ok = 1; +enum { + TEST_H_SETUP_NODE, + TEST_H_MNT, + TEST_H_MNTERROR, + TEST_H_EMPTY_ADD, + TEST_H_EMPTY, + TEST_H_U8, + TEST_H_SIZE /* Always at the end */ +}; -static struct { - struct ctl_table_header *test_h_setup_node; - struct ctl_table_header *test_h_mnt; - struct ctl_table_header *test_h_mnterror; - struct ctl_table_header *empty_add; - struct ctl_table_header *empty; -} sysctl_test_headers; - +static struct ctl_table_header *ctl_headers[TEST_H_SIZE] = {}; struct test_sysctl_data { int int_0001; int int_0002; @@ -167,8 +169,8 @@ static int test_sysctl_setup_node_tests(void) test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL); if (!test_data.bitmap_0001) return -ENOMEM; - sysctl_test_headers.test_h_setup_node = register_sysctl("debug/test_sysctl", test_table); - if (!sysctl_test_headers.test_h_setup_node) { + ctl_headers[TEST_H_SETUP_NODE] = register_sysctl("debug/test_sysctl", test_table); + if (!ctl_headers[TEST_H_SETUP_NODE]) { kfree(test_data.bitmap_0001); return -ENOMEM; } @@ -202,12 +204,12 @@ static int test_sysctl_run_unregister_nested(void) static int test_sysctl_run_register_mount_point(void) { - sysctl_test_headers.test_h_mnt + ctl_headers[TEST_H_MNT] = register_sysctl_mount_point("debug/test_sysctl/mnt"); - if (!sysctl_test_headers.test_h_mnt) + if (!ctl_headers[TEST_H_MNT]) return -ENOMEM; - sysctl_test_headers.test_h_mnterror + ctl_headers[TEST_H_MNTERROR] = register_sysctl("debug/test_sysctl/mnt/mnt_error", test_table_unregister); /* @@ -225,39 +227,94 @@ static const struct ctl_table test_table_empty[] = { }; static int test_sysctl_run_register_empty(void) { /* Tets that an empty dir can be created */ - sysctl_test_headers.empty_add + ctl_headers[TEST_H_EMPTY_ADD] = register_sysctl("debug/test_sysctl/empty_add", test_table_empty); - if (!sysctl_test_headers.empty_add) + if (!ctl_headers[TEST_H_EMPTY_ADD]) return -ENOMEM; /* Test that register on top of an empty dir works */ - sysctl_test_headers.empty + ctl_headers[TEST_H_EMPTY] = register_sysctl("debug/test_sysctl/empty_add/empty", test_table_empty); - if (!sysctl_test_headers.empty) + if (!ctl_headers[TEST_H_EMPTY]) return -ENOMEM; return 0; } -static int __init test_sysctl_init(void) +static const struct ctl_table table_u8_over[] = { + { + .procname = "u8_over", + .data = &test_data.uint_0001, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_FOUR, + .extra2 = SYSCTL_ONE_THOUSAND, + }, +}; + +static const struct ctl_table table_u8_under[] = { + { + .procname = "u8_under", + .data = &test_data.uint_0001, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_NEG_ONE, + .extra2 = SYSCTL_ONE_HUNDRED, + }, +}; + +static const struct ctl_table table_u8_valid[] = { + { + .procname = "u8_valid", + .data = &test_data.uint_0001, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_TWO_HUNDRED, + }, +}; + +static int test_sysctl_register_u8_extra(void) { - int err; + /* should fail because it's over */ + ctl_headers[TEST_H_U8] + = register_sysctl("debug/test_sysctl", table_u8_over); + if (ctl_headers[TEST_H_U8]) + return -ENOMEM; + + /* should fail because it's under */ + ctl_headers[TEST_H_U8] + = register_sysctl("debug/test_sysctl", table_u8_under); + if (ctl_headers[TEST_H_U8]) + return -ENOMEM; - err = test_sysctl_setup_node_tests(); - if (err) - goto out; + /* should not fail because it's valid */ + ctl_headers[TEST_H_U8] + = register_sysctl("debug/test_sysctl", table_u8_valid); + if (!ctl_headers[TEST_H_U8]) + return -ENOMEM; - err = test_sysctl_run_unregister_nested(); - if (err) - goto out; + return 0; +} - err = test_sysctl_run_register_mount_point(); - if (err) - goto out; +static int __init test_sysctl_init(void) +{ + int err = 0; + + int (*func_array[])(void) = { + test_sysctl_setup_node_tests, + test_sysctl_run_unregister_nested, + test_sysctl_run_register_mount_point, + test_sysctl_run_register_empty, + test_sysctl_register_u8_extra + }; - err = test_sysctl_run_register_empty(); + for (int i = 0; !err && i < ARRAY_SIZE(func_array); i++) + err = func_array[i](); -out: return err; } module_init(test_sysctl_init); @@ -265,16 +322,10 @@ module_init(test_sysctl_init); static void __exit test_sysctl_exit(void) { kfree(test_data.bitmap_0001); - if (sysctl_test_headers.test_h_setup_node) - unregister_sysctl_table(sysctl_test_headers.test_h_setup_node); - if (sysctl_test_headers.test_h_mnt) - unregister_sysctl_table(sysctl_test_headers.test_h_mnt); - if (sysctl_test_headers.test_h_mnterror) - unregister_sysctl_table(sysctl_test_headers.test_h_mnterror); - if (sysctl_test_headers.empty) - unregister_sysctl_table(sysctl_test_headers.empty); - if (sysctl_test_headers.empty_add) - unregister_sysctl_table(sysctl_test_headers.empty_add); + for (int i = 0; i < TEST_H_SIZE; i++) { + if (ctl_headers[i]) + unregister_sysctl_table(ctl_headers[i]); + } } module_exit(test_sysctl_exit); 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/crc_kunit.c b/lib/tests/crc_kunit.c index 585c48b65cef..064c2d581557 100644 --- a/lib/tests/crc_kunit.c +++ b/lib/tests/crc_kunit.c @@ -391,17 +391,11 @@ static u64 crc32c_wrapper(u64 crc, const u8 *p, size_t len) return crc32c(crc, p, len); } -static u64 crc32c_combine_wrapper(u64 crc1, u64 crc2, size_t len2) -{ - return crc32c_combine(crc1, crc2, len2); -} - static const struct crc_variant crc_variant_crc32c = { .bits = 32, .le = true, .poly = 0x82f63b78, .func = crc32c_wrapper, - .combine_func = crc32c_combine_wrapper, }; static void crc32c_test(struct kunit *test) 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/printf_kunit.c b/lib/tests/printf_kunit.c index 2c9f6170bacd..bc54cca2d7a6 100644 --- a/lib/tests/printf_kunit.c +++ b/lib/tests/printf_kunit.c @@ -701,21 +701,46 @@ static void fwnode_pointer(struct kunit *kunittest) software_node_unregister_node_group(group); } +struct fourcc_struct { + u32 code; + const char *str; +}; + +static void fourcc_pointer_test(struct kunit *kunittest, const struct fourcc_struct *fc, + size_t n, const char *fmt) +{ + size_t i; + + for (i = 0; i < n; i++) + test(fc[i].str, fmt, &fc[i].code); +} + static void fourcc_pointer(struct kunit *kunittest) { - struct { - u32 code; - char *str; - } const try[] = { + static const struct fourcc_struct try_cc[] = { { 0x3231564e, "NV12 little-endian (0x3231564e)", }, { 0xb231564e, "NV12 big-endian (0xb231564e)", }, { 0x10111213, ".... little-endian (0x10111213)", }, { 0x20303159, "Y10 little-endian (0x20303159)", }, }; - unsigned int i; + static const struct fourcc_struct try_ch[] = { + { 0x41424344, "ABCD (0x41424344)", }, + }; + static const struct fourcc_struct try_chR[] = { + { 0x41424344, "DCBA (0x44434241)", }, + }; + static const struct fourcc_struct try_cl[] = { + { (__force u32)cpu_to_le32(0x41424344), "ABCD (0x41424344)", }, + }; + static const struct fourcc_struct try_cb[] = { + { (__force u32)cpu_to_be32(0x41424344), "ABCD (0x41424344)", }, + }; - for (i = 0; i < ARRAY_SIZE(try); i++) - test(try[i].str, "%p4cc", &try[i].code); + fourcc_pointer_test(kunittest, try_cc, ARRAY_SIZE(try_cc), "%p4cc"); + fourcc_pointer_test(kunittest, try_ch, ARRAY_SIZE(try_ch), "%p4ch"); + fourcc_pointer_test(kunittest, try_chR, ARRAY_SIZE(try_chR), "%p4chR"); + fourcc_pointer_test(kunittest, try_cl, ARRAY_SIZE(try_cl), "%p4cl"); + fourcc_pointer_test(kunittest, try_cb, ARRAY_SIZE(try_cb), "%p4cb"); } static void 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/lib/vsprintf.c b/lib/vsprintf.c index 01699852f30c..3d85800757aa 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1793,27 +1793,49 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc, char output[sizeof("0123 little-endian (0x01234567)")]; char *p = output; unsigned int i; + bool pixel_fmt = false; u32 orig, val; - if (fmt[1] != 'c' || fmt[2] != 'c') + if (fmt[1] != 'c') return error_string(buf, end, "(%p4?)", spec); if (check_pointer(&buf, end, fourcc, spec)) return buf; orig = get_unaligned(fourcc); - val = orig & ~BIT(31); + switch (fmt[2]) { + case 'h': + if (fmt[3] == 'R') + orig = swab32(orig); + break; + case 'l': + orig = (__force u32)cpu_to_le32(orig); + break; + case 'b': + orig = (__force u32)cpu_to_be32(orig); + break; + case 'c': + /* Pixel formats are printed LSB-first */ + pixel_fmt = true; + break; + default: + return error_string(buf, end, "(%p4?)", spec); + } + + val = pixel_fmt ? swab32(orig & ~BIT(31)) : orig; for (i = 0; i < sizeof(u32); i++) { - unsigned char c = val >> (i * 8); + unsigned char c = val >> ((3 - i) * 8); /* Print non-control ASCII characters as-is, dot otherwise */ *p++ = isascii(c) && isprint(c) ? c : '.'; } - *p++ = ' '; - strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian"); - p += strlen(p); + if (pixel_fmt) { + *p++ = ' '; + strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian"); + p += strlen(p); + } *p++ = ' '; *p++ = '('; @@ -1981,15 +2003,11 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, if (check_pointer(&buf, end, clk, spec)) return buf; - switch (fmt[1]) { - case 'n': - default: #ifdef CONFIG_COMMON_CLK - return string(buf, end, __clk_get_name(clk), spec); + return string(buf, end, __clk_get_name(clk), spec); #else - return ptr_to_id(buf, end, clk, spec); + return ptr_to_id(buf, end, clk, spec); #endif - } } static @@ -2374,6 +2392,12 @@ early_param("no_hash_pointers", no_hash_pointers_enable); * read the documentation (path below) first. * - 'NF' For a netdev_features_t * - '4cc' V4L2 or DRM FourCC code, with endianness and raw numerical value. + * - '4c[h[R]lb]' For generic FourCC code with raw numerical value. Both are + * displayed in the big-endian format. This is the opposite of V4L2 or + * DRM FourCCs. + * The additional specifiers define what endianness is used to load + * the stored bytes. The data might be interpreted using the host, + * reversed host byte order, little-endian, or big-endian. * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with * a certain separator (' ' by default): * C colon @@ -2391,8 +2415,6 @@ early_param("no_hash_pointers", no_hash_pointers_enable); * T time64_t * - 'C' For a clock, it prints the name (Common Clock Framework) or address * (legacy clock framework) of the clock - * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address - * (legacy clock framework) of the clock * - 'G' For flags to be printed as a collection of symbolic strings that would * construct the specific value. Supported flags given by option: * p page flags (see struct page) given as pointer to unsigned long |