From 41cca0542d7c79b6be3920a917f643c075ad1561 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Tue, 5 Mar 2024 21:10:29 +0100 Subject: selftests/harness: Fix TEST_F()'s vfork handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always run fixture setup in the grandchild process, and by default also run the teardown in the same process. However, this change makes it possible to run the teardown in a parent process when _metadata->teardown_parent is set to true (e.g. in fixture setup). Fix TEST_SIGNAL() by forwarding grandchild's signal to its parent. Fix seccomp tests by running the test setup in the parent of the test thread, as expected by the related test code. Fix Landlock tests by waiting for the grandchild before processing _metadata. Use of exit(3) in tests should be OK because the environment in which the vfork(2) call happen is already dedicated to the running test (with flushed stdio, setpgrp() call), see __run_test() and the call to fork(2) just before running the setup/test/teardown. Even if the test configures its own exit handlers, they will not be run by the parent because it never calls exit(3), and the test function either ends with a call to _exit(2) or a signal. Cc: Günther Noack Cc: Shuah Khan Cc: Will Drewry Fixes: 0710a1a73fb4 ("selftests/harness: Merge TEST_F_FORK() into TEST_F()") Reviewed-by: Kees Cook Tested-by: Kees Cook Signed-off-by: Mickaël Salaün Reported-by: Mark Brown Tested-by: Mark Brown Link: https://lore.kernel.org/r/20240305201029.1331333-1-mic@digikod.net Signed-off-by: Jakub Kicinski --- tools/testing/selftests/kselftest_harness.h | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'tools/testing/selftests/kselftest_harness.h') diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index 634be793ad58..4fd735e48ee7 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -382,29 +382,33 @@ /* fixture data is alloced, setup, and torn down per call. */ \ FIXTURE_DATA(fixture_name) self; \ pid_t child = 1; \ + int status = 0; \ memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \ if (setjmp(_metadata->env) == 0) { \ - fixture_name##_setup(_metadata, &self, variant->data); \ - /* Let setup failure terminate early. */ \ - if (_metadata->exit_code) \ - return; \ - _metadata->setup_completed = true; \ /* Use the same _metadata. */ \ child = vfork(); \ if (child == 0) { \ + fixture_name##_setup(_metadata, &self, variant->data); \ + /* Let setup failure terminate early. */ \ + if (_metadata->exit_code) \ + _exit(0); \ + _metadata->setup_completed = true; \ fixture_name##_##test_name(_metadata, &self, variant->data); \ - _exit(0); \ - } \ - if (child < 0) { \ + } else if (child < 0 || child != waitpid(child, &status, 0)) { \ ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \ _metadata->exit_code = KSFT_FAIL; \ } \ } \ - if (child == 0) \ - /* Child failed and updated the shared _metadata. */ \ + if (child == 0) { \ + if (_metadata->setup_completed && !_metadata->teardown_parent) \ + fixture_name##_teardown(_metadata, &self, variant->data); \ _exit(0); \ - if (_metadata->setup_completed) \ + } \ + if (_metadata->setup_completed && _metadata->teardown_parent) \ fixture_name##_teardown(_metadata, &self, variant->data); \ + if (!WIFEXITED(status) && WIFSIGNALED(status)) \ + /* Forward signal to __wait_for_test(). */ \ + kill(getpid(), WTERMSIG(status)); \ __test_check_assert(_metadata); \ } \ static struct __test_metadata \ @@ -414,6 +418,7 @@ .fixture = &_##fixture_name##_fixture_object, \ .termsig = signal, \ .timeout = tmout, \ + .teardown_parent = false, \ }; \ static void __attribute__((constructor)) \ _register_##fixture_name##_##test_name(void) \ @@ -873,6 +878,7 @@ struct __test_metadata { bool timed_out; /* did this test timeout instead of exiting? */ bool aborted; /* stopped test due to failed ASSERT */ bool setup_completed; /* did setup finish? */ + bool teardown_parent; /* run teardown in a parent process */ jmp_buf env; /* for exiting out of test early */ struct __test_results *results; struct __test_metadata *prev, *next; -- cgit v1.2.3