Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests/harness: Fix vfork() side effects

Setting the time namespace with CLONE_NEWTIME returns -EUSERS if the
calling thread shares memory with another thread (because of the shared
vDSO), which is the case when it is created with vfork().

Fix pidfd_setns_test by replacing test harness's vfork() call with a
clone3() call with CLONE_VFORK, and an explicit sharing of the
_metadata and self objects.

Replace _metadata->teardown_parent with a new FIXTURE_TEARDOWN_PARENT()
helper that can replace FIXTURE_TEARDOWN(). This is a cleaner approach
and it enables to selectively share the fixture data between the child
process running tests and the parent process running the fixture
teardown. This also avoids updating several tests to not rely on the
self object's copy-on-write property (e.g. storing the returned value of
a fork() call).

Cc: Christian Brauner <brauner@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Günther Noack <gnoack@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Will Drewry <wad@chromium.org>
Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202403291015.1fcfa957-oliver.sang@intel.com
Fixes: 0710a1a73fb4 ("selftests/harness: Merge TEST_F_FORK() into TEST_F()")
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20240511171445.904356-10-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>

+57 -25
+51 -15
tools/testing/selftests/kselftest_harness.h
··· 294 294 * A bare "return;" statement may be used to return early. 295 295 */ 296 296 #define FIXTURE_TEARDOWN(fixture_name) \ 297 + static const bool fixture_name##_teardown_parent; \ 298 + __FIXTURE_TEARDOWN(fixture_name) 299 + 300 + /** 301 + * FIXTURE_TEARDOWN_PARENT() 302 + * *_metadata* is included so that EXPECT_*, ASSERT_* etc. work correctly. 303 + * 304 + * @fixture_name: fixture name 305 + * 306 + * .. code-block:: c 307 + * 308 + * FIXTURE_TEARDOWN_PARENT(fixture_name) { implementation } 309 + * 310 + * Same as FIXTURE_TEARDOWN() but run this code in a parent process. This 311 + * enables the test process to drop its privileges without impacting the 312 + * related FIXTURE_TEARDOWN_PARENT() (e.g. to remove files from a directory 313 + * where write access was dropped). 314 + * 315 + * To make it possible for the parent process to use *self*, share (MAP_SHARED) 316 + * the fixture data between all forked processes. 317 + */ 318 + #define FIXTURE_TEARDOWN_PARENT(fixture_name) \ 319 + static const bool fixture_name##_teardown_parent = true; \ 320 + __FIXTURE_TEARDOWN(fixture_name) 321 + 322 + #define __FIXTURE_TEARDOWN(fixture_name) \ 297 323 void fixture_name##_teardown( \ 298 324 struct __test_metadata __attribute__((unused)) *_metadata, \ 299 325 FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ ··· 394 368 * Very similar to TEST() except that *self* is the setup instance of fixture's 395 369 * datatype exposed for use by the implementation. 396 370 * 397 - * The @test_name code is run in a separate process sharing the same memory 398 - * (i.e. vfork), which means that the test process can update its privileges 399 - * without impacting the related FIXTURE_TEARDOWN() (e.g. to remove files from 400 - * a directory where write access was dropped). 371 + * The _metadata object is shared (MAP_SHARED) with all the potential forked 372 + * processes, which enables them to use EXCEPT_*() and ASSERT_*(). 373 + * 374 + * The *self* object is only shared with the potential forked processes if 375 + * FIXTURE_TEARDOWN_PARENT() is used instead of FIXTURE_TEARDOWN(). 401 376 */ 402 377 #define TEST_F(fixture_name, test_name) \ 403 378 __TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT) ··· 419 392 struct __fixture_variant_metadata *variant) \ 420 393 { \ 421 394 /* fixture data is alloced, setup, and torn down per call. */ \ 422 - FIXTURE_DATA(fixture_name) self; \ 395 + FIXTURE_DATA(fixture_name) self_private, *self = NULL; \ 423 396 pid_t child = 1; \ 424 397 int status = 0; \ 425 398 /* Makes sure there is only one teardown, even when child forks again. */ \ 426 399 bool *teardown = mmap(NULL, sizeof(*teardown), \ 427 400 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); \ 428 401 *teardown = false; \ 429 - memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \ 402 + if (sizeof(*self) > 0) { \ 403 + if (fixture_name##_teardown_parent) { \ 404 + self = mmap(NULL, sizeof(*self), PROT_READ | PROT_WRITE, \ 405 + MAP_SHARED | MAP_ANONYMOUS, -1, 0); \ 406 + } else { \ 407 + memset(&self_private, 0, sizeof(self_private)); \ 408 + self = &self_private; \ 409 + } \ 410 + } \ 430 411 if (setjmp(_metadata->env) == 0) { \ 431 - /* Use the same _metadata. */ \ 432 - child = vfork(); \ 412 + /* _metadata and potentially self are shared with all forks. */ \ 413 + child = clone3_vfork(); \ 433 414 if (child == 0) { \ 434 - fixture_name##_setup(_metadata, &self, variant->data); \ 415 + fixture_name##_setup(_metadata, self, variant->data); \ 435 416 /* Let setup failure terminate early. */ \ 436 417 if (_metadata->exit_code) \ 437 418 _exit(0); \ 438 419 _metadata->setup_completed = true; \ 439 - fixture_name##_##test_name(_metadata, &self, variant->data); \ 420 + fixture_name##_##test_name(_metadata, self, variant->data); \ 440 421 } else if (child < 0 || child != waitpid(child, &status, 0)) { \ 441 422 ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \ 442 423 _metadata->exit_code = KSFT_FAIL; \ 443 424 } \ 444 425 } \ 445 426 if (child == 0) { \ 446 - if (_metadata->setup_completed && !_metadata->teardown_parent && \ 427 + if (_metadata->setup_completed && !fixture_name##_teardown_parent && \ 447 428 __sync_bool_compare_and_swap(teardown, false, true)) \ 448 - fixture_name##_teardown(_metadata, &self, variant->data); \ 429 + fixture_name##_teardown(_metadata, self, variant->data); \ 449 430 _exit(0); \ 450 431 } \ 451 - if (_metadata->setup_completed && _metadata->teardown_parent && \ 432 + if (_metadata->setup_completed && fixture_name##_teardown_parent && \ 452 433 __sync_bool_compare_and_swap(teardown, false, true)) \ 453 - fixture_name##_teardown(_metadata, &self, variant->data); \ 434 + fixture_name##_teardown(_metadata, self, variant->data); \ 454 435 munmap(teardown, sizeof(*teardown)); \ 436 + if (self && fixture_name##_teardown_parent) \ 437 + munmap(self, sizeof(*self)); \ 455 438 if (!WIFEXITED(status) && WIFSIGNALED(status)) \ 456 439 /* Forward signal to __wait_for_test(). */ \ 457 440 kill(getpid(), WTERMSIG(status)); \ ··· 935 898 bool timed_out; /* did this test timeout instead of exiting? */ 936 899 bool aborted; /* stopped test due to failed ASSERT */ 937 900 bool setup_completed; /* did setup finish? */ 938 - bool teardown_parent; /* run teardown in a parent process */ 939 901 jmp_buf env; /* for exiting out of test early */ 940 902 struct __test_results *results; 941 903 struct __test_metadata *prev, *next;
+6 -10
tools/testing/selftests/landlock/fs_test.c
··· 286 286 287 287 static void prepare_layout(struct __test_metadata *const _metadata) 288 288 { 289 - _metadata->teardown_parent = true; 290 - 291 289 prepare_layout_opt(_metadata, &mnt_tmp); 292 290 } 293 291 ··· 314 316 prepare_layout(_metadata); 315 317 } 316 318 317 - FIXTURE_TEARDOWN(layout0) 319 + FIXTURE_TEARDOWN_PARENT(layout0) 318 320 { 319 321 cleanup_layout(_metadata); 320 322 } ··· 377 379 create_layout1(_metadata); 378 380 } 379 381 380 - FIXTURE_TEARDOWN(layout1) 382 + FIXTURE_TEARDOWN_PARENT(layout1) 381 383 { 382 384 remove_layout1(_metadata); 383 385 ··· 3690 3692 create_file(_metadata, file1_s1d1); 3691 3693 } 3692 3694 3693 - FIXTURE_TEARDOWN(ftruncate) 3695 + FIXTURE_TEARDOWN_PARENT(ftruncate) 3694 3696 { 3695 3697 EXPECT_EQ(0, remove_path(file1_s1d1)); 3696 3698 cleanup_layout(_metadata); ··· 3868 3870 clear_cap(_metadata, CAP_SYS_ADMIN); 3869 3871 } 3870 3872 3871 - FIXTURE_TEARDOWN(layout1_bind) 3873 + FIXTURE_TEARDOWN_PARENT(layout1_bind) 3872 3874 { 3873 3875 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 3874 3876 ··· 4273 4275 clear_cap(_metadata, CAP_SYS_ADMIN); 4274 4276 } 4275 4277 4276 - FIXTURE_TEARDOWN(layout2_overlay) 4278 + FIXTURE_TEARDOWN_PARENT(layout2_overlay) 4277 4279 { 4278 4280 if (self->skip_test) 4279 4281 SKIP(return, "overlayfs is not supported (teardown)"); ··· 4706 4708 SKIP(return, "this filesystem is not supported (setup)"); 4707 4709 } 4708 4710 4709 - _metadata->teardown_parent = true; 4710 - 4711 4711 prepare_layout_opt(_metadata, &variant->mnt); 4712 4712 4713 4713 /* Creates directory when required. */ ··· 4739 4743 free(dir_path); 4740 4744 } 4741 4745 4742 - FIXTURE_TEARDOWN(layout3_fs) 4746 + FIXTURE_TEARDOWN_PARENT(layout3_fs) 4743 4747 { 4744 4748 if (self->skip_test) 4745 4749 SKIP(return, "this filesystem is not supported (teardown)");