diff mbox series

[2/2] kunit: port UBSAN tests to KUnit

Message ID 20200820220552.3427995-2-urielguajardojr@gmail.com (mailing list archive)
State New
Headers show
Series [1/2] kunit: expect failures from dynamic analysis tools | expand

Commit Message

Uriel Guajardo Aug. 20, 2020, 10:05 p.m. UTC
From: Uriel Guajardo <urielguajardo@google.com>

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@google.com/

Requires "kunit: UBSAN integration":
https://lore.kernel.org/linux-kselftest/20200813205722.1384108-2-urielguajardojr@gmail.com/

Signed-off-by: Uriel Guajardo <urielguajardo@google.com>
---
 lib/Kconfig.ubsan | 12 ++++--
 lib/Makefile      |  2 +-
 lib/test_ubsan.c  | 96 ++++++++++++++++++++++++-----------------------
 lib/ubsan.c       |  2 +-
 4 files changed, 59 insertions(+), 53 deletions(-)
diff mbox series

Patch

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@gmail.com>");
 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,