From: Uriel Guajardo <urielguajardo@xxxxxxxxxx> UBSAN tests are ported to the KUnit testing framework using the tool failure expectation feature added to KUnit. I've commented out test_ubsan_divrem_overflow and test_ubsan_out_of_bounds since they both consistently crash the kernel. The former gives me a divide error and the latter gives a corrupted kernel stack error. If this is only on my end, let me know. Kconfig file is reformatted according the KUnit naming guidelines: https://lore.kernel.org/linux-kselftest/20200702071416.1780522-1-davidgow@xxxxxxxxxx/ Requires "kunit: UBSAN integration": https://lore.kernel.org/linux-kselftest/20200813205722.1384108-2-urielguajardojr@xxxxxxxxx/ Signed-off-by: Uriel Guajardo <urielguajardo@xxxxxxxxxx> --- lib/Kconfig.ubsan | 12 ++++-- lib/Makefile | 2 +- lib/test_ubsan.c | 96 ++++++++++++++++++++++++----------------------- lib/ubsan.c | 2 +- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/lib/Kconfig.ubsan b/lib/Kconfig.ubsan index 774315de555a..aab5fbecd19a 100644 --- a/lib/Kconfig.ubsan +++ b/lib/Kconfig.ubsan @@ -80,11 +80,15 @@ config UBSAN_ALIGNMENT Enabling this option on architectures that support unaligned accesses may produce a lot of false positives. -config TEST_UBSAN - tristate "Module for testing for undefined behavior detection" - depends on m +config UBSAN_KUNIT_TEST + tristate "Tests for undefined behavior detection" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS help - This is a test module for UBSAN. + This is a test suite for UBSAN using the KUnit testing framework. It triggers various undefined behavior, and detect it. + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit + endif # if UBSAN diff --git a/lib/Makefile b/lib/Makefile index b1c42c10073b..8b1a134f5bf1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,7 +67,7 @@ obj-$(CONFIG_TEST_IDA) += test_ida.o obj-$(CONFIG_TEST_KASAN) += test_kasan.o CFLAGS_test_kasan.o += -fno-builtin CFLAGS_test_kasan.o += $(call cc-disable-warning, vla) -obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o +obj-$(CONFIG_UBSAN_KUNIT_TEST) += test_ubsan.o CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla) UBSAN_SANITIZE_test_ubsan.o := y obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o diff --git a/lib/test_ubsan.c b/lib/test_ubsan.c index 9ea10adf7a66..ec724cddf005 100644 --- a/lib/test_ubsan.c +++ b/lib/test_ubsan.c @@ -2,63 +2,65 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <kunit/test.h> -typedef void(*test_ubsan_fp)(void); +#define KUNIT_EXPECT_UBSAN_FAIL(test, condition) \ + KUNIT_EXPECT_TOOL_FAIL(test, condition, UBSAN) -static void test_ubsan_add_overflow(void) +static void test_ubsan_add_overflow(struct kunit *test) { volatile int val = INT_MAX; - val += 2; + KUNIT_EXPECT_UBSAN_FAIL(test, val += 2); } -static void test_ubsan_sub_overflow(void) +static void test_ubsan_sub_overflow(struct kunit *test) { volatile int val = INT_MIN; volatile int val2 = 2; - val -= val2; + KUNIT_EXPECT_UBSAN_FAIL(test, val -= val2); } -static void test_ubsan_mul_overflow(void) +static void test_ubsan_mul_overflow(struct kunit *test) { volatile int val = INT_MAX / 2; - val *= 3; + KUNIT_EXPECT_UBSAN_FAIL(test, val *= 3); } -static void test_ubsan_negate_overflow(void) +static void test_ubsan_negate_overflow(struct kunit *test) { volatile int val = INT_MIN; - val = -val; + KUNIT_EXPECT_UBSAN_FAIL(test, val = -val); } -static void test_ubsan_divrem_overflow(void) +static void test_ubsan_divrem_overflow(struct kunit *test) { volatile int val = 16; volatile int val2 = 0; - val /= val2; + KUNIT_EXPECT_UBSAN_FAIL(test, val /= val2); } -static void test_ubsan_shift_out_of_bounds(void) +static void test_ubsan_shift_out_of_bounds(struct kunit *test) { volatile int val = -1; int val2 = 10; - val2 <<= val; + KUNIT_EXPECT_UBSAN_FAIL(test, val2 <<= val); } -static void test_ubsan_out_of_bounds(void) +static void test_ubsan_out_of_bounds(struct kunit *test) { volatile int i = 4, j = 5; volatile int arr[4]; - arr[j] = i; + KUNIT_EXPECT_UBSAN_FAIL(test, arr[j] = i); } -static void test_ubsan_load_invalid_value(void) +static void test_ubsan_load_invalid_value(struct kunit *test) { volatile char *dst, *src; bool val, val2, *ptr; @@ -69,10 +71,10 @@ static void test_ubsan_load_invalid_value(void) *dst = *src; ptr = &val2; - val2 = val; + KUNIT_EXPECT_UBSAN_FAIL(test, val2 = val); } -static void test_ubsan_null_ptr_deref(void) +static void test_ubsan_null_ptr_deref(struct kunit *test) { volatile int *ptr = NULL; int val; @@ -80,56 +82,56 @@ static void test_ubsan_null_ptr_deref(void) val = *ptr; } -static void test_ubsan_misaligned_access(void) +static void test_ubsan_misaligned_access(struct kunit *test) { volatile char arr[5] __aligned(4) = {1, 2, 3, 4, 5}; volatile int *ptr, val = 6; ptr = (int *)(arr + 1); - *ptr = val; + KUNIT_EXPECT_UBSAN_FAIL(test, *ptr = val); } -static void test_ubsan_object_size_mismatch(void) +static void test_ubsan_object_size_mismatch(struct kunit *test) { /* "((aligned(8)))" helps this not into be misaligned for ptr-access. */ volatile int val __aligned(8) = 4; volatile long long *ptr, val2; ptr = (long long *)&val; - val2 = *ptr; + KUNIT_EXPECT_UBSAN_FAIL(test, val2 = *ptr); } -static const test_ubsan_fp test_ubsan_array[] = { - test_ubsan_add_overflow, - test_ubsan_sub_overflow, - test_ubsan_mul_overflow, - test_ubsan_negate_overflow, - test_ubsan_divrem_overflow, - test_ubsan_shift_out_of_bounds, - test_ubsan_out_of_bounds, - test_ubsan_load_invalid_value, - //test_ubsan_null_ptr_deref, /* exclude it because there is a crash */ - test_ubsan_misaligned_access, - test_ubsan_object_size_mismatch, +static struct kunit_case ubsan_test_cases[] = { + KUNIT_CASE(test_ubsan_add_overflow), + KUNIT_CASE(test_ubsan_sub_overflow), + KUNIT_CASE(test_ubsan_mul_overflow), + KUNIT_CASE(test_ubsan_negate_overflow), + //KUNIT_CASE(test_ubsan_divrem_overflow), /* exclude because it crashes*/ + KUNIT_CASE(test_ubsan_shift_out_of_bounds), + //KUNIT_CASE(test_ubsan_out_of_bounds), /* exclude because it crashes */ + KUNIT_CASE(test_ubsan_load_invalid_value), + //KUNIT_CASE(test_ubsan_null_ptr_deref), /* exclude because it crashes */ + KUNIT_CASE(test_ubsan_misaligned_access), + KUNIT_CASE(test_ubsan_object_size_mismatch), + {} }; -static int __init test_ubsan_init(void) +static int test_ubsan_init(struct kunit *test) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(test_ubsan_array); i++) - test_ubsan_array[i](); - - (void)test_ubsan_null_ptr_deref; /* to avoid unsed-function warning */ + /* Avoid unused-function warnings */ + (void)test_ubsan_divrem_overflow; + (void)test_ubsan_out_of_bounds; + (void)test_ubsan_null_ptr_deref; return 0; } -module_init(test_ubsan_init); -static void __exit test_ubsan_exit(void) -{ - /* do nothing */ -} -module_exit(test_ubsan_exit); +static struct kunit_suite ubsan_test_suite = { + .name = "ubsan", + .init = test_ubsan_init, + .test_cases = ubsan_test_cases, +}; + +kunit_test_suites(&ubsan_test_suite); MODULE_AUTHOR("Jinbum Park <jinb.park7@xxxxxxxxx>"); MODULE_LICENSE("GPL v2"); diff --git a/lib/ubsan.c b/lib/ubsan.c index 1460e2c828c8..9fe357eb1e81 100644 --- a/lib/ubsan.c +++ b/lib/ubsan.c @@ -138,7 +138,7 @@ static void ubsan_prologue(struct source_location *loc, const char *reason) { current->in_ubsan++; - kunit_fail_current_test(); + kunit_fail_from_tool(UBSAN); pr_err("========================================" "========================================\n"); pr_err("UBSAN: %s in %s:%d:%d\n", reason, loc->file_name, -- 2.28.0.297.g1956fa8f8d-goog