[PATCH] kunit: debugfs: Handle errors from alloc_string_stream()
In kunit_debugfs_create_suite() give up and skip creating the debugfs file if any of the alloc_string_stream() calls return an error or NULL. Only put a value in the log pointer of kunit_suite and kunit_test if it is a valid pointer to a log. This prevents the potential invalid dereference reported by smatch: lib/kunit/debugfs.c:115 kunit_debugfs_create_suite() error: 'suite->log' dereferencing possible ERR_PTR() lib/kunit/debugfs.c:119 kunit_debugfs_create_suite() error: 'test_case->log' dereferencing possible ERR_PTR() Signed-off-by: Richard Fitzgerald Reported-by: Dan Carpenter Fixes: 05e2006ce493 ("kunit: Use string_stream for test log") --- lib/kunit/debugfs.c | 29 - 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/kunit/debugfs.c b/lib/kunit/debugfs.c index 270d185737e6..73075ca6e88c 100644 --- a/lib/kunit/debugfs.c +++ b/lib/kunit/debugfs.c @@ -109,14 +109,27 @@ static const struct file_operations debugfs_results_fops = { void kunit_debugfs_create_suite(struct kunit_suite *suite) { struct kunit_case *test_case; + struct string_stream *stream; - /* Allocate logs before creating debugfs representation. */ - suite->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(suite->log, true); + /* +* Allocate logs before creating debugfs representation. +* The log pointer must be NULL if there isn't a log so only +* set it if the log stream was created successfully. +*/ + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + goto err; + + string_stream_set_append_newlines(stream, true); + suite->log = stream; kunit_suite_for_each_test_case(suite, test_case) { - test_case->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(test_case->log, true); + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + goto err; + + string_stream_set_append_newlines(stream, true); + test_case->log = stream; } suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir); @@ -124,6 +137,12 @@ void kunit_debugfs_create_suite(struct kunit_suite *suite) debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444, suite->debugfs, suite, &debugfs_results_fops); + return; + +err: + string_stream_destroy(suite->log); + kunit_suite_for_each_test_case(suite, test_case) + string_stream_destroy(test_case->log); } void kunit_debugfs_destroy_suite(struct kunit_suite *suite) -- 2.30.2
Re: [PATCH] kunit: debugfs: Handle errors from alloc_string_stream()
On 27/09/2023 17:50, Richard Fitzgerald wrote: In kunit_debugfs_create_suite() give up and skip creating the debugfs file if any of the alloc_string_stream() calls return an error or NULL. Only put a value in the log pointer of kunit_suite and kunit_test if it is a valid pointer to a log. This prevents the potential invalid dereference reported by smatch: lib/kunit/debugfs.c:115 kunit_debugfs_create_suite() error: 'suite->log' dereferencing possible ERR_PTR() lib/kunit/debugfs.c:119 kunit_debugfs_create_suite() error: 'test_case->log' dereferencing possible ERR_PTR() Signed-off-by: Richard Fitzgerald Reported-by: Dan Carpenter Fixes: 05e2006ce493 ("kunit: Use string_stream for test log") --- lib/kunit/debugfs.c | 29 - 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/kunit/debugfs.c b/lib/kunit/debugfs.c index 270d185737e6..73075ca6e88c 100644 --- a/lib/kunit/debugfs.c +++ b/lib/kunit/debugfs.c @@ -109,14 +109,27 @@ static const struct file_operations debugfs_results_fops = { void kunit_debugfs_create_suite(struct kunit_suite *suite) { struct kunit_case *test_case; + struct string_stream *stream; - /* Allocate logs before creating debugfs representation. */ - suite->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(suite->log, true); + /* +* Allocate logs before creating debugfs representation. +* The log pointer must be NULL if there isn't a log so only +* set it if the log stream was created successfully. +*/ + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + goto err; This can be a return. Nothing has been created at this point so there is nothing to clean up. I'll send a V2. + + string_stream_set_append_newlines(stream, true); + suite->log = stream; kunit_suite_for_each_test_case(suite, test_case) { - test_case->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(test_case->log, true); + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + goto err; + + string_stream_set_append_newlines(stream, true); + test_case->log = stream; } suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir); @@ -124,6 +137,12 @@ void kunit_debugfs_create_suite(struct kunit_suite *suite) debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444, suite->debugfs, suite, &debugfs_results_fops); + return; + +err: + string_stream_destroy(suite->log); + kunit_suite_for_each_test_case(suite, test_case) + string_stream_destroy(test_case->log); } void kunit_debugfs_destroy_suite(struct kunit_suite *suite)
[PATCH v2] kunit: debugfs: Handle errors from alloc_string_stream()
In kunit_debugfs_create_suite() give up and skip creating the debugfs file if any of the alloc_string_stream() calls return an error or NULL. Only put a value in the log pointer of kunit_suite and kunit_test if it is a valid pointer to a log. This prevents the potential invalid dereference reported by smatch: lib/kunit/debugfs.c:115 kunit_debugfs_create_suite() error: 'suite->log' dereferencing possible ERR_PTR() lib/kunit/debugfs.c:119 kunit_debugfs_create_suite() error: 'test_case->log' dereferencing possible ERR_PTR() Signed-off-by: Richard Fitzgerald Reported-by: Dan Carpenter Fixes: 05e2006ce493 ("kunit: Use string_stream for test log") Reviewed-by: Rae Moar --- Changes from V1: - If the alloc_string_stream() for the suite->log fails just return. Nothing has been created at this point so there's nothing to clean up. - Re-word the explanation of why the log pointers are only set if they point to a valid log. As these changes are trivial I've carried Rae Moar's Reviewed-by from V1. --- lib/kunit/debugfs.c | 30 +- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/kunit/debugfs.c b/lib/kunit/debugfs.c index 270d185737e6..9d167adfa746 100644 --- a/lib/kunit/debugfs.c +++ b/lib/kunit/debugfs.c @@ -109,14 +109,28 @@ static const struct file_operations debugfs_results_fops = { void kunit_debugfs_create_suite(struct kunit_suite *suite) { struct kunit_case *test_case; + struct string_stream *stream; - /* Allocate logs before creating debugfs representation. */ - suite->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(suite->log, true); + /* +* Allocate logs before creating debugfs representation. +* The suite->log and test_case->log pointer are expected to be NULL +* if there isn't a log, so only set it if the log stream was created +* successfully. +*/ + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + return; + + string_stream_set_append_newlines(stream, true); + suite->log = stream; kunit_suite_for_each_test_case(suite, test_case) { - test_case->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(test_case->log, true); + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + goto err; + + string_stream_set_append_newlines(stream, true); + test_case->log = stream; } suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir); @@ -124,6 +138,12 @@ void kunit_debugfs_create_suite(struct kunit_suite *suite) debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444, suite->debugfs, suite, &debugfs_results_fops); + return; + +err: + string_stream_destroy(suite->log); + kunit_suite_for_each_test_case(suite, test_case) + string_stream_destroy(test_case->log); } void kunit_debugfs_destroy_suite(struct kunit_suite *suite) -- 2.30.2
[PATCH] kunit: string-stream: Allow ERR_PTR to be passed to string_stream_destroy()
Check the stream pointer passed to string_stream_destroy() for IS_ERR_OR_NULL() instead of only NULL. Whatever alloc_string_stream() returns should be safe to pass to string_stream_destroy(), and that will be an ERR_PTR. It's obviously good practise and generally helpful to also check for NULL pointers so that client cleanup code can call string_stream_destroy() unconditionally - which could include pointers that have never been set to anything and so are NULL. Signed-off-by: Richard Fitzgerald --- lib/kunit/string-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kunit/string-stream.c b/lib/kunit/string-stream.c index a6f3616c2048..54f4fdcbfac8 100644 --- a/lib/kunit/string-stream.c +++ b/lib/kunit/string-stream.c @@ -173,7 +173,7 @@ void string_stream_destroy(struct string_stream *stream) { KUNIT_STATIC_STUB_REDIRECT(string_stream_destroy, stream); - if (!stream) + if (IS_ERR_OR_NULL(stream)) return; string_stream_clear(stream); -- 2.30.2
[PATCH] kunit: debugfs: Fix unchecked dereference in debugfs_print_results()
Move the call to kunit_suite_has_succeeded() after the check that the kunit_suite pointer is valid. This was found by smatch: lib/kunit/debugfs.c:66 debugfs_print_results() warn: variable dereferenced before check 'suite' (see line 63) Signed-off-by: Richard Fitzgerald Reported-by: Dan Carpenter Fixes: 38289a26e1b8 ("kunit: fix debugfs code to use enum kunit_status, not bool") --- lib/kunit/debugfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/kunit/debugfs.c b/lib/kunit/debugfs.c index 9d167adfa746..382706dfb47d 100644 --- a/lib/kunit/debugfs.c +++ b/lib/kunit/debugfs.c @@ -60,12 +60,14 @@ static void debugfs_print_result(struct seq_file *seq, struct string_stream *log static int debugfs_print_results(struct seq_file *seq, void *v) { struct kunit_suite *suite = (struct kunit_suite *)seq->private; - enum kunit_status success = kunit_suite_has_succeeded(suite); + enum kunit_status success; struct kunit_case *test_case; if (!suite) return 0; + success = kunit_suite_has_succeeded(suite); + /* Print KTAP header so the debugfs log can be parsed as valid KTAP. */ seq_puts(seq, "KTAP version 1\n"); seq_puts(seq, "1..1\n"); -- 2.30.2
[PATCH v2 RESEND] kunit: debugfs: Handle errors from alloc_string_stream()
In kunit_debugfs_create_suite() give up and skip creating the debugfs file if any of the alloc_string_stream() calls return an error or NULL. Only put a value in the log pointer of kunit_suite and kunit_test if it is a valid pointer to a log. This prevents the potential invalid dereference reported by smatch: lib/kunit/debugfs.c:115 kunit_debugfs_create_suite() error: 'suite->log' dereferencing possible ERR_PTR() lib/kunit/debugfs.c:119 kunit_debugfs_create_suite() error: 'test_case->log' dereferencing possible ERR_PTR() Signed-off-by: Richard Fitzgerald Reported-by: Dan Carpenter Fixes: 05e2006ce493 ("kunit: Use string_stream for test log") --- lib/kunit/debugfs.c | 30 +- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/kunit/debugfs.c b/lib/kunit/debugfs.c index 270d185737e6..9d167adfa746 100644 --- a/lib/kunit/debugfs.c +++ b/lib/kunit/debugfs.c @@ -109,14 +109,28 @@ static const struct file_operations debugfs_results_fops = { void kunit_debugfs_create_suite(struct kunit_suite *suite) { struct kunit_case *test_case; + struct string_stream *stream; - /* Allocate logs before creating debugfs representation. */ - suite->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(suite->log, true); + /* +* Allocate logs before creating debugfs representation. +* The suite->log and test_case->log pointer are expected to be NULL +* if there isn't a log, so only set it if the log stream was created +* successfully. +*/ + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + return; + + string_stream_set_append_newlines(stream, true); + suite->log = stream; kunit_suite_for_each_test_case(suite, test_case) { - test_case->log = alloc_string_stream(GFP_KERNEL); - string_stream_set_append_newlines(test_case->log, true); + stream = alloc_string_stream(GFP_KERNEL); + if (IS_ERR_OR_NULL(stream)) + goto err; + + string_stream_set_append_newlines(stream, true); + test_case->log = stream; } suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir); @@ -124,6 +138,12 @@ void kunit_debugfs_create_suite(struct kunit_suite *suite) debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444, suite->debugfs, suite, &debugfs_results_fops); + return; + +err: + string_stream_destroy(suite->log); + kunit_suite_for_each_test_case(suite, test_case) + string_stream_destroy(test_case->log); } void kunit_debugfs_destroy_suite(struct kunit_suite *suite) -- 2.30.2
[PATCH RESEND] kunit: string-stream: Allow ERR_PTR to be passed to string_stream_destroy()
Check the stream pointer passed to string_stream_destroy() for IS_ERR_OR_NULL() instead of only NULL. Whatever alloc_string_stream() returns should be safe to pass to string_stream_destroy(), and that will be an ERR_PTR. It's obviously good practise and generally helpful to also check for NULL pointers so that client cleanup code can call string_stream_destroy() unconditionally - which could include pointers that have never been set to anything and so are NULL. Signed-off-by: Richard Fitzgerald --- lib/kunit/string-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kunit/string-stream.c b/lib/kunit/string-stream.c index a6f3616c2048..54f4fdcbfac8 100644 --- a/lib/kunit/string-stream.c +++ b/lib/kunit/string-stream.c @@ -173,7 +173,7 @@ void string_stream_destroy(struct string_stream *stream) { KUNIT_STATIC_STUB_REDIRECT(string_stream_destroy, stream); - if (!stream) + if (IS_ERR_OR_NULL(stream)) return; string_stream_clear(stream); -- 2.30.2
[PATCH RESEND] kunit: debugfs: Fix unchecked dereference in debugfs_print_results()
Move the call to kunit_suite_has_succeeded() after the check that the kunit_suite pointer is valid. This was found by smatch: lib/kunit/debugfs.c:66 debugfs_print_results() warn: variable dereferenced before check 'suite' (see line 63) Signed-off-by: Richard Fitzgerald Reported-by: Dan Carpenter Fixes: 38289a26e1b8 ("kunit: fix debugfs code to use enum kunit_status, not bool") --- lib/kunit/debugfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/kunit/debugfs.c b/lib/kunit/debugfs.c index 9d167adfa746..382706dfb47d 100644 --- a/lib/kunit/debugfs.c +++ b/lib/kunit/debugfs.c @@ -60,12 +60,14 @@ static void debugfs_print_result(struct seq_file *seq, struct string_stream *log static int debugfs_print_results(struct seq_file *seq, void *v) { struct kunit_suite *suite = (struct kunit_suite *)seq->private; - enum kunit_status success = kunit_suite_has_succeeded(suite); + enum kunit_status success; struct kunit_case *test_case; if (!suite) return 0; + success = kunit_suite_has_succeeded(suite); + /* Print KTAP header so the debugfs log can be parsed as valid KTAP. */ seq_puts(seq, "KTAP version 1\n"); seq_puts(seq, "1..1\n"); -- 2.30.2
[PATCH] kunit: test: Avoid cast warning in when adding kfree() as an action
In kunit_log_test() pass the kfree_wrapper() function to kunit_add_action() instead of directly passing kfree(). This prevents a cast warning: lib/kunit/kunit-test.c:565:25: warning: cast from 'void (*)(const void *)' to 'kunit_action_t *' (aka 'void (*)(void *)') converts to incompatible function type [-Wcast-function-type-strict] 564 full_log = string_stream_get_string(test->log); > 565 kunit_add_action(test, (kunit_action_t *)kfree, full_log); Signed-off-by: Richard Fitzgerald Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202311070041.kwvyx7yp-...@intel.com/ Fixes: 05e2006ce493 ("kunit: Use string_stream for test log") --- lib/kunit/kunit-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c index 99d2a3a528e1..de2113a58fa0 100644 --- a/lib/kunit/kunit-test.c +++ b/lib/kunit/kunit-test.c @@ -562,7 +562,7 @@ static void kunit_log_test(struct kunit *test) KUNIT_EXPECT_TRUE(test, test->log->append_newlines); full_log = string_stream_get_string(test->log); - kunit_add_action(test, (kunit_action_t *)kfree, full_log); + kunit_add_action(test, kfree_wrapper, full_log); KUNIT_EXPECT_NOT_ERR_OR_NULL(test, strstr(full_log, "put this in log.")); KUNIT_EXPECT_NOT_ERR_OR_NULL(test, -- 2.30.2
Re: [PATCH] kunit: test: Avoid cast warning in when adding kfree() as an action
On 6/11/23 17:23, Richard Fitzgerald wrote: In kunit_log_test() pass the kfree_wrapper() function to kunit_add_action() instead of directly passing kfree(). Aargh! Sorry, noticed spurious word in commit title. Will send a V2.
[PATCH v2] kunit: test: Avoid cast warning when adding kfree() as an action
In kunit_log_test() pass the kfree_wrapper() function to kunit_add_action() instead of directly passing kfree(). This prevents a cast warning: lib/kunit/kunit-test.c:565:25: warning: cast from 'void (*)(const void *)' to 'kunit_action_t *' (aka 'void (*)(void *)') converts to incompatible function type [-Wcast-function-type-strict] 564 full_log = string_stream_get_string(test->log); > 565 kunit_add_action(test, (kunit_action_t *)kfree, full_log); Signed-off-by: Richard Fitzgerald Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202311070041.kwvyx7yp-...@intel.com/ Fixes: 05e2006ce493 ("kunit: Use string_stream for test log") --- lib/kunit/kunit-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c index 99d2a3a528e1..de2113a58fa0 100644 --- a/lib/kunit/kunit-test.c +++ b/lib/kunit/kunit-test.c @@ -562,7 +562,7 @@ static void kunit_log_test(struct kunit *test) KUNIT_EXPECT_TRUE(test, test->log->append_newlines); full_log = string_stream_get_string(test->log); - kunit_add_action(test, (kunit_action_t *)kfree, full_log); + kunit_add_action(test, kfree_wrapper, full_log); KUNIT_EXPECT_NOT_ERR_OR_NULL(test, strstr(full_log, "put this in log.")); KUNIT_EXPECT_NOT_ERR_OR_NULL(test, -- 2.30.2
[PATCH] kunit: string-stream-test: Avoid cast warning when testing gfp_t flags
Passing a gfp_t to KUNIT_EXPECT_EQ() causes a cast warning: lib/kunit/string-stream-test.c:73:9: sparse: sparse: incorrect type in initializer (different base types) expected long long right_value got restricted gfp_t const __right Avoid this by testing stream->gfp for the expected value and passing the boolean result of this comparison to KUNIT_EXPECT_TRUE(), as was already done a few lines above in string_stream_managed_init_test(). Signed-off-by: Richard Fitzgerald Fixes: d1a0d699bfc0 ("kunit: string-stream: Add tests for freeing resource-managed string_stream") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202311181918.0mpcu2xh-...@intel.com/ --- lib/kunit/string-stream-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kunit/string-stream-test.c b/lib/kunit/string-stream-test.c index 06822766f29a..03fb511826f7 100644 --- a/lib/kunit/string-stream-test.c +++ b/lib/kunit/string-stream-test.c @@ -72,7 +72,7 @@ static void string_stream_unmanaged_init_test(struct kunit *test) KUNIT_EXPECT_EQ(test, stream->length, 0); KUNIT_EXPECT_TRUE(test, list_empty(&stream->fragments)); - KUNIT_EXPECT_EQ(test, stream->gfp, GFP_KERNEL); + KUNIT_EXPECT_TRUE(test, (stream->gfp == GFP_KERNEL)); KUNIT_EXPECT_FALSE(test, stream->append_newlines); KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream)); -- 2.30.2
Re: [PATCH v2] kunit: run test suites only after module initialization completes
On 28/11/2023 10:16, Marco Pagani wrote: Commit 2810c1e99867 ("kunit: Fix wild-memory-access bug in kunit_free_suite_set()") fixed a wild-memory-access bug that could have happened during the loading phase of test suites built and executed as loadable modules. However, it also introduced a problematic side effect that causes test suites modules to crash when they attempt to register fake devices. When a module is loaded, it traverses the MODULE_STATE_UNFORMED and MODULE_STATE_COMING states before reaching the normal operating state MODULE_STATE_LIVE. Finally, when the module is removed, it moves to MODULE_STATE_GOING before being released. However, if the loading function load_module() fails between complete_formation() and do_init_module(), the module goes directly from MODULE_STATE_COMING to MODULE_STATE_GOING without passing through MODULE_STATE_LIVE. This behavior was causing kunit_module_exit() to be called without having first executed kunit_module_init(). Since kunit_module_exit() is responsible for freeing the memory allocated by kunit_module_init() through kunit_filter_suites(), this behavior was resulting in a wild-memory-access bug. Commit 2810c1e99867 ("kunit: Fix wild-memory-access bug in kunit_free_suite_set()") fixed this issue by running the tests when the module is still in MODULE_STATE_COMING. However, modules in that state are not fully initialized, lacking sysfs kobjects. Therefore, if a test module attempts to register a fake device, it will inevitably crash. This patch proposes a different approach to fix the original wild-memory-access bug while restoring the normal module execution flow by making kunit_module_exit() able to detect if kunit_module_init() has previously initialized the tests suite set. In this way, test modules can once again register fake devices without crashing. This behavior is achieved by checking whether mod->kunit_suites is a virtual or direct mapping address. If it is a virtual address, then kunit_module_init() has allocated the suite_set in kunit_filter_suites() using kmalloc_array(). On the contrary, if mod->kunit_suites is still pointing to the original address that was set when looking up the .kunit_test_suites section of the module, then the loading phase has failed and there's no memory to be freed. v2: - add include Fixes: 2810c1e99867 ("kunit: Fix wild-memory-access bug in kunit_free_suite_set()") Signed-off-by: Marco Pagani Tested-by: Richard Fitzgerald Fixed this crash: https://lore.kernel.org/all/e239b94b-462a-41e5-9a4c-cd1ffd530...@opensource.cirrus.com/ Also tested with sound/pci/hda/cirrus_scodec_test.c
[PATCH] kunit: Fix NULL-dereference in kunit_init_suite() if suite->log is NULL
suite->log must be checked for NULL before passing it to string_stream_clear(). This was done in kunit_init_test() but was missing from kunit_init_suite(). Signed-off-by: Richard Fitzgerald Fixes: 6d696c4695c5 ("kunit: add ability to run tests after boot using debugfs") --- lib/kunit/test.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/kunit/test.c b/lib/kunit/test.c index e803d998e855..ea7f0913e55a 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -658,7 +658,9 @@ static void kunit_init_suite(struct kunit_suite *suite) kunit_debugfs_create_suite(suite); suite->status_comment[0] = '\0'; suite->suite_init_err = 0; - string_stream_clear(suite->log); + + if (suite->log) + string_stream_clear(suite->log); } bool kunit_enabled(void) -- 2.30.2
[PATCH 1/2] kunit: Allow passing function pointer to kunit_activate_static_stub()
Swap the arguments to typecheck_fn() in kunit_activate_static_stub() so that real_fn_addr can be either the function itself or a pointer to that function. This is useful to simplify redirecting static functions in a module. Having to pass the actual function meant that it must be exported from the module. Either making the 'static' and EXPORT_SYMBOL*() conditional (which makes the code messy), or change it to always exported (which increases the export namespace and prevents the compiler inlining a trivial stub function in non-test builds). With the original definition of kunit_activate_static_stub() the address of real_fn_addr was passed to typecheck_fn() as the type to be passed. This meant that if real_fn_addr was a pointer-to-function it would resolve to a ** instead of a *, giving an error like this: error: initialization of ‘int (**)(int)’ from incompatible pointer type ‘int (*)(int)’ [-Werror=incompatible-pointer-types] kunit_activate_static_stub(test, add_one_fn_ptr, subtract_one); | ^~~~ ./include/linux/typecheck.h:21:25: note: in definition of macro ‘typecheck_fn’ 21 | ({ typeof(type) __tmp = function; \ Swapping the arguments to typecheck_fn makes it take the type of a pointer to the replacement function. Either a function or a pointer to function can be assigned to that. For example: static int some_function(int x) { /* whatever */ } int (* some_function_ptr)(int) = some_function; static int replacement(int x) { /* whatever */ } Then: kunit_activate_static_stub(test, some_function, replacement); yields: typecheck_fn(typeof(&replacement), some_function); and: kunit_activate_static_stub(test, some_function_ptr, replacement); yields: typecheck_fn(typeof(&replacement), some_function_ptr); The two typecheck_fn() then resolve to: int (*__tmp)(int) = some_function; and int (*__tmp)(int) = some_function_ptr; Both of these are valid. In the first case the compiler inserts an implicit '&' to take the address of the supplied function, and in the second case the RHS is already a pointer to the same type. Signed-off-by: Richard Fitzgerald --- include/kunit/static_stub.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/kunit/static_stub.h b/include/kunit/static_stub.h index 85315c80b303..bf940322dfc0 100644 --- a/include/kunit/static_stub.h +++ b/include/kunit/static_stub.h @@ -93,7 +93,7 @@ void __kunit_activate_static_stub(struct kunit *test, * The redirection can be disabled again with kunit_deactivate_static_stub(). */ #define kunit_activate_static_stub(test, real_fn_addr, replacement_addr) do { \ - typecheck_fn(typeof(&real_fn_addr), replacement_addr); \ + typecheck_fn(typeof(&replacement_addr), real_fn_addr); \ __kunit_activate_static_stub(test, real_fn_addr, replacement_addr); \ } while (0) -- 2.30.2
[PATCH 2/2] kunit: Add example of kunit_activate_static_stub() with pointer-to-function
Adds a variant of example_static_stub_test() that shows use of a pointer-to-function with kunit_activate_static_stub(). A const pointer to the add_one() function is declared. This pointer-to-function is passed to kunit_activate_static_stub() and kunit_deactivate_static_stub() instead of passing add_one directly. Signed-off-by: Richard Fitzgerald --- lib/kunit/kunit-example-test.c | 27 +++ 1 file changed, 27 insertions(+) diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index d2f7a3c62c18..9e57f341dc37 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -168,6 +168,8 @@ static int subtract_one(int i) return i - 1; } +static int (* const add_one_fn_ptr)(int i) = add_one; + /* * This test shows the use of static stubs. */ @@ -187,6 +189,30 @@ static void example_static_stub_test(struct kunit *test) KUNIT_EXPECT_EQ(test, add_one(1), 2); } +/* + * This test shows the use of static stubs when the function being + * replaced is provided as a pointer-to-function instead of the + * actual function. This is useful for providing access to static + * functions in a module by exporting a pointer to that function + * instead of having to change the static function to a non-static + * exported function. + */ +static void example_static_stub_using_fn_ptr_test(struct kunit *test) +{ + /* By default, function is not stubbed. */ + KUNIT_EXPECT_EQ(test, add_one(1), 2); + + /* Replace add_one() with subtract_one(). */ + kunit_activate_static_stub(test, add_one_fn_ptr, subtract_one); + + /* add_one() is now replaced. */ + KUNIT_EXPECT_EQ(test, add_one(1), 0); + + /* Return add_one() to normal. */ + kunit_deactivate_static_stub(test, add_one_fn_ptr); + KUNIT_EXPECT_EQ(test, add_one(1), 2); +} + static const struct example_param { int value; } example_params_array[] = { @@ -245,6 +271,7 @@ static struct kunit_case example_test_cases[] = { KUNIT_CASE(example_mark_skipped_test), KUNIT_CASE(example_all_expect_macros_test), KUNIT_CASE(example_static_stub_test), + KUNIT_CASE(example_static_stub_using_fn_ptr_test), KUNIT_CASE_PARAM(example_params_test, example_gen_params), KUNIT_CASE_SLOW(example_slow_test), {} -- 2.30.2
Re: [PATCH v3] kunit: run test suites only after module initialization completes
On 06/12/2023 15:07, Marco Pagani wrote: Commit 2810c1e99867 ("kunit: Fix wild-memory-access bug in kunit_free_suite_set()") fixed a wild-memory-access bug that could have happened during the loading phase of test suites built and executed as loadable modules. However, it also introduced a problematic side effect that causes test suites modules to crash when they attempt to register fake devices. When a module is loaded, it traverses the MODULE_STATE_UNFORMED and MODULE_STATE_COMING states before reaching the normal operating state MODULE_STATE_LIVE. Finally, when the module is removed, it moves to MODULE_STATE_GOING before being released. However, if the loading function load_module() fails between complete_formation() and do_init_module(), the module goes directly from MODULE_STATE_COMING to MODULE_STATE_GOING without passing through MODULE_STATE_LIVE. This behavior was causing kunit_module_exit() to be called without having first executed kunit_module_init(). Since kunit_module_exit() is responsible for freeing the memory allocated by kunit_module_init() through kunit_filter_suites(), this behavior was resulting in a wild-memory-access bug. Commit 2810c1e99867 ("kunit: Fix wild-memory-access bug in kunit_free_suite_set()") fixed this issue by running the tests when the module is still in MODULE_STATE_COMING. However, modules in that state are not fully initialized, lacking sysfs kobjects. Therefore, if a test module attempts to register a fake device, it will inevitably crash. This patch proposes a different approach to fix the original wild-memory-access bug while restoring the normal module execution flow by making kunit_module_exit() able to detect if kunit_module_init() has previously initialized the tests suite set. In this way, test modules can once again register fake devices without crashing. This behavior is achieved by checking whether mod->kunit_suites is a virtual or direct mapping address. If it is a virtual address, then kunit_module_init() has allocated the suite_set in kunit_filter_suites() using kmalloc_array(). On the contrary, if mod->kunit_suites is still pointing to the original address that was set when looking up the .kunit_test_suites section of the module, then the loading phase has failed and there's no memory to be freed. v3: - add a comment to clarify why the start address is checked v2: - add include Fixes: 2810c1e99867 ("kunit: Fix wild-memory-access bug in kunit_free_suite_set()") Tested-by: Richard Fitzgerald Reviewed-by: Javier Martinez Canillas Signed-off-by: Marco Pagani --- For V3: Tested-by: Richard Fitzgerald Fixes this crash: https://lore.kernel.org/all/e239b94b-462a-41e5-9a4c-cd1ffd530...@opensource.cirrus.com/ Also tested with sound/pci/hda/cirrus_scodec_test.c
[PATCH] kunit: Protect string comparisons against NULL
Add NULL checks to KUNIT_BINARY_STR_ASSERTION() so that it will fail cleanly if either pointer is NULL, instead of causing a NULL pointer dereference in the strcmp(). A test failure could be that a string is unexpectedly NULL. This could be trapped by KUNIT_ASSERT_NOT_NULL() but that would terminate the test at that point. It's preferable that the KUNIT_EXPECT_STR*() macros can handle NULL pointers as a failure. Signed-off-by: Richard Fitzgerald --- include/kunit/test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index b163b9984b33..c2ce379c329b 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -758,7 +758,7 @@ do { \ .right_text = #right, \ }; \ \ - if (likely(strcmp(__left, __right) op 0)) \ + if (likely((__left) && (__right) && (strcmp(__left, __right) op 0))) \ break; \ \ \ -- 2.30.2
[PATCH v2 2/2] kunit: Add example of kunit_activate_static_stub() with pointer-to-function
Adds a variant of example_static_stub_test() that shows use of a pointer-to-function with kunit_activate_static_stub(). A const pointer to the add_one() function is declared. This pointer-to-function is passed to kunit_activate_static_stub() and kunit_deactivate_static_stub() instead of passing add_one directly. Signed-off-by: Richard Fitzgerald --- V2: Added comment for add_one_fn_ptr. --- lib/kunit/kunit-example-test.c | 35 ++ 1 file changed, 35 insertions(+) diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index 359dbee10201..798924f7cc86 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -168,6 +168,16 @@ static int subtract_one(int i) return i - 1; } +/* + * If the function to be replaced is static within a module it is + * useful to export a pointer to that function instead of having + * to change the static function to a non-static exported function. + * + * This pointer simulates a module exporting a pointer to a static + * function. + */ +static int (* const add_one_fn_ptr)(int i) = add_one; + /* * This test shows the use of static stubs. */ @@ -187,6 +197,30 @@ static void example_static_stub_test(struct kunit *test) KUNIT_EXPECT_EQ(test, add_one(1), 2); } +/* + * This test shows the use of static stubs when the function being + * replaced is provided as a pointer-to-function instead of the + * actual function. This is useful for providing access to static + * functions in a module by exporting a pointer to that function + * instead of having to change the static function to a non-static + * exported function. + */ +static void example_static_stub_using_fn_ptr_test(struct kunit *test) +{ + /* By default, function is not stubbed. */ + KUNIT_EXPECT_EQ(test, add_one(1), 2); + + /* Replace add_one() with subtract_one(). */ + kunit_activate_static_stub(test, add_one_fn_ptr, subtract_one); + + /* add_one() is now replaced. */ + KUNIT_EXPECT_EQ(test, add_one(1), 0); + + /* Return add_one() to normal. */ + kunit_deactivate_static_stub(test, add_one_fn_ptr); + KUNIT_EXPECT_EQ(test, add_one(1), 2); +} + static const struct example_param { int value; } example_params_array[] = { @@ -259,6 +293,7 @@ static struct kunit_case example_test_cases[] = { KUNIT_CASE(example_mark_skipped_test), KUNIT_CASE(example_all_expect_macros_test), KUNIT_CASE(example_static_stub_test), + KUNIT_CASE(example_static_stub_using_fn_ptr_test), KUNIT_CASE(example_priv_test), KUNIT_CASE_PARAM(example_params_test, example_gen_params), KUNIT_CASE_SLOW(example_slow_test), -- 2.30.2
[PATCH v2 1/2] kunit: Allow passing function pointer to kunit_activate_static_stub()
Swap the arguments to typecheck_fn() in kunit_activate_static_stub() so that real_fn_addr can be either the function itself or a pointer to that function. This is useful to simplify redirecting static functions in a module. Having to pass the actual function meant that it must be exported from the module. Either making the 'static' and EXPORT_SYMBOL*() conditional (which makes the code messy), or change it to always exported (which increases the export namespace and prevents the compiler inlining a trivial stub function in non-test builds). With the original definition of kunit_activate_static_stub() the address of real_fn_addr was passed to typecheck_fn() as the type to be passed. This meant that if real_fn_addr was a pointer-to-function it would resolve to a ** instead of a *, giving an error like this: error: initialization of ‘int (**)(int)’ from incompatible pointer type ‘int (*)(int)’ [-Werror=incompatible-pointer-types] kunit_activate_static_stub(test, add_one_fn_ptr, subtract_one); | ^~~~ ./include/linux/typecheck.h:21:25: note: in definition of macro ‘typecheck_fn’ 21 | ({ typeof(type) __tmp = function; \ Swapping the arguments to typecheck_fn makes it take the type of a pointer to the replacement function. Either a function or a pointer to function can be assigned to that. For example: static int some_function(int x) { /* whatever */ } int (* some_function_ptr)(int) = some_function; static int replacement(int x) { /* whatever */ } Then: kunit_activate_static_stub(test, some_function, replacement); yields: typecheck_fn(typeof(&replacement), some_function); and: kunit_activate_static_stub(test, some_function_ptr, replacement); yields: typecheck_fn(typeof(&replacement), some_function_ptr); The two typecheck_fn() then resolve to: int (*__tmp)(int) = some_function; and int (*__tmp)(int) = some_function_ptr; Both of these are valid. In the first case the compiler inserts an implicit '&' to take the address of the supplied function, and in the second case the RHS is already a pointer to the same type. Signed-off-by: Richard Fitzgerald Reviewed-by: Rae Moar --- No changes since V1. --- include/kunit/static_stub.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/kunit/static_stub.h b/include/kunit/static_stub.h index 85315c80b303..bf940322dfc0 100644 --- a/include/kunit/static_stub.h +++ b/include/kunit/static_stub.h @@ -93,7 +93,7 @@ void __kunit_activate_static_stub(struct kunit *test, * The redirection can be disabled again with kunit_deactivate_static_stub(). */ #define kunit_activate_static_stub(test, real_fn_addr, replacement_addr) do { \ - typecheck_fn(typeof(&real_fn_addr), replacement_addr); \ + typecheck_fn(typeof(&replacement_addr), real_fn_addr); \ __kunit_activate_static_stub(test, real_fn_addr, replacement_addr); \ } while (0) -- 2.30.2
Re: [PATCH] kunit: Protect string comparisons against NULL
On 22/12/23 08:39, David Gow wrote: On Wed, 20 Dec 2023 at 23:52, Richard Fitzgerald wrote: Add NULL checks to KUNIT_BINARY_STR_ASSERTION() so that it will fail cleanly if either pointer is NULL, instead of causing a NULL pointer dereference in the strcmp(). A test failure could be that a string is unexpectedly NULL. This could be trapped by KUNIT_ASSERT_NOT_NULL() but that would terminate the test at that point. It's preferable that the KUNIT_EXPECT_STR*() macros can handle NULL pointers as a failure. Signed-off-by: Richard Fitzgerald --- I think this is the right thing to do. There's possibly an argument that this should succeed if both are NULL, but I prefer it this way. Maybe an _OR_NULL() variant of the string test macros would be better to be explicit that NULL is acceptable. Reviewed-by: David Gow Cheers, -- David
Re: Regression on drm-tip
On 31/1/24 05:34, Borah, Chaitanya Kumar wrote: Hello Richard, Hope you are doing well. I am Chaitanya from the Linux graphics team in Intel. This mail is regarding a regression we are seeing in our CI runs[1] on drm-tip[2] repository. These are captured by gitlab issues[3]. We bisected the issue and have found the following commit to be the first bad commit. ` commit a0b84213f947176ddcd0e96e0751a109f28cde21 Author: Richard Fitzgerald r...@opensource.cirrus.com Date: Mon Dec 18 15:17:29 2023 + kunit: Fix NULL-dereference in kunit_init_suite() if suite->log is NULL suite->log must be checked for NULL before passing it to string_stream_clear(). This was done in kunit_init_test() but was missing from kunit_init_suite(). Signed-off-by: Richard Fitzgerald r...@opensource.cirrus.com Fixes: 6d696c4695c5 ("kunit: add ability to run tests after boot using debugfs") Reviewed-by: Rae Moar rm...@google.com Acked-by: David Gow david...@google.com Reviewed-by: Muhammad Usama Anjum usama.an...@collabora.com Signed-off-by: Shuah Khan sk...@linuxfoundation.org lib/kunit/test.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) ` We tried reverting the patch and the original issue is not seen but it results in NULL pointer deference[4] which I am guessing is expected. Could you please check why the patch causes this regression and provide a fix if necessary? [1] https://intel-gfx-ci.01.org/tree/drm-tip/index.html?testfilter=drm [2] https://cgit.freedesktop.org/drm-tip/ [3] https://gitlab.freedesktop.org/drm/intel/-/issues/10140 https://gitlab.freedesktop.org/drm/intel/-/issues/10143 [4] [ 179.849411] [IGT] drm_buddy: executing [ 179.856385] [IGT] drm_buddy: starting subtest drm_buddy [ 179.862594] KTAP version 1 [ 179.862600] 1..1 [ 179.863375] BUG: kernel NULL pointer dereference, address: 0030 [ 179.863381] #PF: supervisor read access in kernel mode [ 179.863384] #PF: error_code(0x) - not-present page [ 179.863387] PGD 0 P4D 0 [ 179.863391] Oops: [#1] PREEMPT SMP NOPTI [ 179.863395] CPU: 1 PID: 1319 Comm: drm_buddy Not tainted 6.8.0-rc1-bisecttrail015 #16 [ 179.863398] Hardware name: Intel Corporation Meteor Lake Client Platform/MTL-P DDR5 SODIMM SBS RVP, BIOS MTLPFWI1.R00.3471.D81.2311291340 11/29/2023 [ 179.863400] RIP: 0010:__lock_acquire+0x71f/0x2300 [ 179.863408] Code: 84 03 06 00 00 44 8b 15 27 f6 72 01 45 85 d2 0f 84 9c 00 00 00 f6 45 22 10 0f 84 63 03 00 00 41 bf 01 00 00 00 e9 8a 00 00 00 <48> 81 3f 40 d7 fa 82 41 b9 00 00 00 00 45 0f 45 c8 83 fe 01 0f 87 ... [ 179.863445] PKRU: 5554 [ 179.863448] Call Trace: [ 179.863450] [ 179.863453] ? __die_body+0x1a/0x60 [ 179.863459] ? page_fault_oops+0x156/0x450 [ 179.863465] ? do_user_addr_fault+0x65/0x9e0 [ 179.863472] ? exc_page_fault+0x68/0x1a0 [ 179.863479] ? asm_exc_page_fault+0x26/0x30 [ 179.863487] ? __lock_acquire+0x71f/0x2300 [ 179.863493] ? __pfx_do_sync_core+0x10/0x10 [ 179.863500] lock_acquire+0xd8/0x2d0 [ 179.863505] ? string_stream_clear+0x29/0xb0 [kunit] [ 179.863523] _raw_spin_lock+0x2e/0x40 [ 179.863528] ? string_stream_clear+0x29/0xb0 [kunit] [ 179.863540] string_stream_clear+0x29/0xb0 [kunit] [ 179.863554] __kunit_test_suites_init+0x7e/0xe0 [kunit] [ 179.863568] kunit_module_notify+0x20f/0x220 [kunit] [ 179.863583] notifier_call_chain+0x46/0x130 [ 179.863591] notifier_call_chain_robust+0x3e/0x90 [ 179.863598] blocking_notifier_call_chain_robust+0x42/0x60 [ 179.863605] load_module+0x1bcd/0x1f80 [ 179.863617] ? init_module_from_file+0x86/0xd0 [ 179.863621] init_module_from_file+0x86/0xd0 [ 179.863629] idempotent_init_module+0x17c/0x230 [ 179.863637] __x64_sys_finit_module+0x56/0xb0 [ 179.863642] do_syscall_64+0x6f/0x140 [ 179.863649] entry_SYSCALL_64_after_hwframe+0x6e/0x76 [ 179.863654] RIP: 0033:0x7f0e6676195d Looking at the gitlab bug reports compared to the crash log above: [3] You have hit a failure on the 3rd test case: <6> [59.039608] [IGT] drm_buddy: starting dynamic subtest drm_test_buddy_alloc_limit <6> [59.077701] KTAP version 1 <6> [59.077705] 1..1 <6> [59.078487] KTAP version 1 <6> [59.078494] # Subtest: drm_buddy <6> [59.078496] # module: drm_buddy_test <6> [59.078498] 1..4 <6> [59.07