All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] kunit: expect failures from dynamic analysis tools
@ 2020-08-20 22:05 Uriel Guajardo
  2020-08-20 22:05 ` [PATCH 2/2] kunit: port UBSAN tests to KUnit Uriel Guajardo
  0 siblings, 1 reply; 2+ messages in thread
From: Uriel Guajardo @ 2020-08-20 22:05 UTC (permalink / raw)
  To: brendanhiggins
  Cc: urielguajardo, linux-kselftest, kunit-dev, linux-kernel, Uriel Guajardo

Adds support to KUnit for failure expectation for specific tools. Uses
named KUnit resources to keep track of whether or not a failure expectation has
been set by the user.

- Adds a generic KUNIT_EXPECT_TOOL_FAIL macro that can expect failure from any
supported tool that uses the same identifying name

- Adds kunit_fail_from_tool which is used to flag failures for specific
tools.

Requires "kunit: suppport failure from dynamic analysis tools":
https://lore.kernel.org/linux-kselftest/20200813205722.1384108-1-urielguajardojr@gmail.com/

Signed-off-by: Uriel Guajardo <urielguajardo@google.com>
---
 include/kunit/test-bug.h | 53 ++++++++++++++++++++++++++++++++++++++++
 include/kunit/test.h     |  5 ++++
 lib/kunit/test.c         | 46 +++++++++++++++++++++++++++++-----
 3 files changed, 98 insertions(+), 6 deletions(-)

diff --git a/include/kunit/test-bug.h b/include/kunit/test-bug.h
index 283c19ec328f..383198f70cb5 100644
--- a/include/kunit/test-bug.h
+++ b/include/kunit/test-bug.h
@@ -9,16 +9,69 @@
 #ifndef _KUNIT_TEST_BUG_H
 #define _KUNIT_TEST_BUG_H
 
+/**
+ * struct kunit_expectation - represents expectations in KUnit, specifically
+ * used to keep track of failure expectations from analysis tools
+ */
+struct kunit_expectation {
+	bool expected;
+	bool found;
+};
+
 #if IS_ENABLED(CONFIG_KUNIT)
 
 extern void kunit_fail_current_test(void);
 
+/**
+ * kunit_fail_from_tool() - Fails the currently running KUnit tests under the
+ * given tool name.
+ *
+ * Note: Uses a named KUnit resource to track state. Do not use the name
+ * KUNIT_TOOL_{tool} for KUnit resources outside of here.
+ */
+#define kunit_fail_from_tool(tool)			\
+	if (current->kunit_test)			\
+		kunit_tool_fail(current->kunit_test, #tool,\
+				"KUNIT_TOOL_" #tool)
+
+/**
+ * kunit_tool_expectation() - Returns the kunit_expectation for the given
+ * tool. If it cannot find the expectation, it creates an expectation and
+ * returns it.
+ *
+ * Note: Uses a named KUnit resource to track state. Do not use the name
+ * KUNIT_TOOL_{tool} for KUnit resources outside of here.
+ */
+#define kunit_tool_expectation(test, tool)			\
+	kunit_find_expectation(test, "KUNIT_TOOL_" #tool)
+
+
+/**
+ * KUNIT_EXPECT_TOOL_FAIL() - Fails the currently running KUnit test if the
+ * condition does not cause an error within the given tool.
+ *
+ * Note: 'tool' must be consistent with the name specified in
+ * kunit_fail_from_tool(). If the tool fails KUnit using another name, KUnit
+ * will treat it as a separate tool.
+ */
+#define KUNIT_EXPECT_TOOL_FAIL(test, condition, tool) do {		\
+	struct kunit_expectation *data = kunit_tool_expectation(test, tool);\
+	data->expected = true;						\
+	data->found = false;						\
+	condition;							\
+	KUNIT_EXPECT_EQ(test, data->expected, data->found);		\
+	data->expected = false;						\
+	data->found = false;						\
+} while (0)
+
 #else
 
 static inline void kunit_fail_current_test(void)
 {
 }
 
+#define kunit_fail_from_tool(tool) do { } while (0)
+
 #endif
 
 #endif /* _KUNIT_TEST_BUG_H */
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 81bf43a1abda..3da8e17ee32b 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -511,6 +511,11 @@ static inline int kunit_destroy_named_resource(struct kunit *test,
  */
 void kunit_remove_resource(struct kunit *test, struct kunit_resource *res);
 
+void kunit_tool_fail(struct kunit *test, char *tool_name, char *resource_name);
+
+struct kunit_expectation *kunit_find_expectation(struct kunit *test,
+						 char *resource_name);
+
 /**
  * kunit_kmalloc() - Like kmalloc() except the allocation is *test managed*.
  * @test: The test context object.
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index d8189d827368..458d1ad2daf2 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -22,6 +22,45 @@ void kunit_fail_current_test(void)
 		kunit_set_failure(current->kunit_test);
 }
 
+static void kunit_data_free(struct kunit_resource *res)
+{
+	kfree(res->data);
+}
+
+struct kunit_expectation *kunit_find_expectation(struct kunit *test,
+						 char *resource_name)
+{
+	struct kunit_resource *resource;
+	struct kunit_expectation *expectation;
+
+	struct kunit_resource *existing = kunit_find_named_resource(
+			test, resource_name);
+	if (!existing) {
+		expectation = kzalloc(sizeof(*expectation), GFP_KERNEL);
+		resource = kunit_alloc_and_get_resource(test, NULL,
+					kunit_data_free, GFP_KERNEL,
+					expectation);
+		resource->name = resource_name;
+		kunit_put_resource(resource);
+		return expectation;
+	}
+	kunit_put_resource(existing);
+	return existing->data;
+}
+EXPORT_SYMBOL_GPL(kunit_find_expectation);
+
+void kunit_tool_fail(struct kunit *test, char *tool_name, char *resource_name)
+{
+	struct kunit_expectation *data = kunit_find_expectation(test,
+								resource_name);
+	if (!data->expected) {
+		kunit_warn(test, "Dynamic analysis tool failure from %s",
+			   tool_name);
+		return kunit_fail_current_test();
+	}
+	data->found = true;
+}
+
 static void kunit_print_tap_version(void)
 {
 	static bool kunit_has_printed_tap_version;
@@ -538,11 +577,6 @@ static int kunit_kmalloc_init(struct kunit_resource *res, void *context)
 	return 0;
 }
 
-static void kunit_kmalloc_free(struct kunit_resource *res)
-{
-	kfree(res->data);
-}
-
 void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
 {
 	struct kunit_kmalloc_params params = {
@@ -552,7 +586,7 @@ void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
 
 	return kunit_alloc_resource(test,
 				    kunit_kmalloc_init,
-				    kunit_kmalloc_free,
+				    kunit_data_free,
 				    gfp,
 				    &params);
 }
-- 
2.28.0.297.g1956fa8f8d-goog


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH 2/2] kunit: port UBSAN tests to KUnit
  2020-08-20 22:05 [PATCH 1/2] kunit: expect failures from dynamic analysis tools Uriel Guajardo
@ 2020-08-20 22:05 ` Uriel Guajardo
  0 siblings, 0 replies; 2+ messages in thread
From: Uriel Guajardo @ 2020-08-20 22:05 UTC (permalink / raw)
  To: brendanhiggins; +Cc: urielguajardo, linux-kselftest, kunit-dev, linux-kernel

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 --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,
-- 
2.28.0.297.g1956fa8f8d-goog


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-08-20 22:06 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-20 22:05 [PATCH 1/2] kunit: expect failures from dynamic analysis tools Uriel Guajardo
2020-08-20 22:05 ` [PATCH 2/2] kunit: port UBSAN tests to KUnit Uriel Guajardo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.