6666#include <sys/wait.h>
6767#include <unistd.h>
6868#include <setjmp.h>
69+ #include <syscall.h>
70+ #include <linux/sched.h>
6971
7072#include "kselftest.h"
7173
8082# define TH_LOG_ENABLED 1
8183#endif
8284
85+ /* Wait for the child process to end but without sharing memory mapping. */
86+ static inline pid_t clone3_vfork (void )
87+ {
88+ struct clone_args args = {
89+ .flags = CLONE_VFORK ,
90+ .exit_signal = SIGCHLD ,
91+ };
92+
93+ return syscall (__NR_clone3 , & args , sizeof (args ));
94+ }
95+
8396/**
8497 * TH_LOG()
8598 *
281294 * A bare "return;" statement may be used to return early.
282295 */
283296#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 ) \
284323 void fixture_name##_teardown( \
285324 struct __test_metadata __attribute__((unused)) *_metadata, \
286325 FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
325364 * variant.
326365 */
327366#define FIXTURE_VARIANT_ADD (fixture_name , variant_name ) \
328- extern FIXTURE_VARIANT(fixture_name) \
367+ extern const FIXTURE_VARIANT(fixture_name) \
329368 _##fixture_name##_##variant_name##_variant; \
330369 static struct __fixture_variant_metadata \
331370 _##fixture_name##_##variant_name##_object = \
337376 __register_fixture_variant(&_##fixture_name##_fixture_object, \
338377 &_##fixture_name##_##variant_name##_object); \
339378 } \
340- FIXTURE_VARIANT(fixture_name) \
379+ const FIXTURE_VARIANT(fixture_name) \
341380 _##fixture_name##_##variant_name##_variant =
342381
343382/**
355394 * Very similar to TEST() except that *self* is the setup instance of fixture's
356395 * datatype exposed for use by the implementation.
357396 *
358- * The @test_name code is run in a separate process sharing the same memory
359- * (i.e. vfork), which means that the test process can update its privileges
360- * without impacting the related FIXTURE_TEARDOWN() (e.g. to remove files from
361- * a directory where write access was dropped).
397+ * The _metadata object is shared (MAP_SHARED) with all the potential forked
398+ * processes, which enables them to use EXCEPT_*() and ASSERT_*().
399+ *
400+ * The *self* object is only shared with the potential forked processes if
401+ * FIXTURE_TEARDOWN_PARENT() is used instead of FIXTURE_TEARDOWN().
362402 */
363403#define TEST_F (fixture_name , test_name ) \
364404 __TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
379419 struct __fixture_variant_metadata *variant) \
380420 { \
381421 /* fixture data is alloced, setup, and torn down per call. */ \
382- FIXTURE_DATA (fixture_name ) self ; \
422+ FIXTURE_DATA (fixture_name ) self_private , * self = NULL ; \
383423 pid_t child = 1 ; \
384424 int status = 0 ; \
385- bool jmp = false; \
386- memset (& self , 0 , sizeof (FIXTURE_DATA (fixture_name ))); \
425+ /* Makes sure there is only one teardown, even when child forks again. */ \
426+ bool * teardown = mmap (NULL , sizeof (* teardown ), \
427+ PROT_READ | PROT_WRITE , MAP_SHARED | MAP_ANONYMOUS , -1 , 0 ); \
428+ * teardown = false; \
429+ if (sizeof (* self ) > 0 ) { \
430+ if (fixture_name ##_teardown_parent ) { \
431+ self = mmap (NULL , sizeof (* self ), PROT_READ | PROT_WRITE , \
432+ MAP_SHARED | MAP_ANONYMOUS , -1 , 0 ); \
433+ } else { \
434+ memset (& self_private , 0 , sizeof (self_private )); \
435+ self = & self_private ; \
436+ } \
437+ } \
387438 if (setjmp (_metadata -> env ) == 0 ) { \
388- /* Use the same _metadata . */ \
389- child = vfork (); \
439+ /* _metadata and potentially self are shared with all forks . */ \
440+ child = clone3_vfork (); \
390441 if (child == 0 ) { \
391- fixture_name ##_setup (_metadata , & self , variant -> data ); \
442+ fixture_name ##_setup (_metadata , self , variant -> data ); \
392443 /* Let setup failure terminate early. */ \
393444 if (_metadata -> exit_code ) \
394445 _exit (0 ); \
395446 _metadata -> setup_completed = true; \
396- fixture_name ##_ ##test_name (_metadata, & self, variant->data); \
447+ fixture_name ##_ ##test_name (_metadata, self, variant->data); \
397448 } else if (child < 0 || child != waitpid(child, &status, 0)) { \
398449 ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \
399450 _metadata->exit_code = KSFT_FAIL; \
400451 } \
401452 } \
402- else \
403- jmp = true; \
404453 if (child == 0) { \
405- if (_metadata->setup_completed && !_metadata->teardown_parent && !jmp) \
406- fixture_name##_teardown(_metadata, &self, variant->data); \
454+ if (_metadata->setup_completed && !fixture_name##_teardown_parent && \
455+ __sync_bool_compare_and_swap(teardown, false, true)) \
456+ fixture_name##_teardown(_metadata, self, variant->data); \
407457 _exit(0); \
408458 } \
409- if (_metadata->setup_completed && _metadata->teardown_parent) \
410- fixture_name##_teardown(_metadata, &self, variant->data); \
411- if (!WIFEXITED(status) && WIFSIGNALED(status)) \
459+ if (_metadata->setup_completed && fixture_name##_teardown_parent && \
460+ __sync_bool_compare_and_swap(teardown, false, true)) \
461+ fixture_name##_teardown(_metadata, self, variant->data); \
462+ munmap(teardown, sizeof(*teardown)); \
463+ if (self && fixture_name##_teardown_parent) \
464+ munmap(self, sizeof(*self)); \
465+ if (WIFEXITED(status)) { \
466+ if (WEXITSTATUS(status)) \
467+ _metadata->exit_code = WEXITSTATUS(status); \
468+ } else if (WIFSIGNALED(status)) { \
412469 /* Forward signal to __wait_for_test(). */ \
413470 kill (getpid (), WTERMSIG (status )); \
471+ } \
414472 __test_check_assert (_metadata ); \
415473 } \
416- static struct __test_metadata \
417- _ ##fixture_name ##_##test_name##_object = { \
418- .name = #test_name, \
419- .fn = &wrapper_##fixture_name##_##test_name, \
420- .fixture = &_##fixture_name##_fixture_object, \
421- .termsig = signal, \
422- .timeout = tmout, \
423- .teardown_parent = false, \
424- }; \
474+ static struct __test_metadata * _ ##fixture_name ##_##test_name##_object; \
425475 static void __attribute__((constructor)) \
426476 _register_##fixture_name##_##test_name(void) \
427477 { \
428- __register_test(&_##fixture_name##_##test_name##_object); \
478+ struct __test_metadata *object = mmap(NULL, sizeof(*object), \
479+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); \
480+ object->name = #test_name; \
481+ object->fn = &wrapper_##fixture_name##_##test_name; \
482+ object->fixture = &_##fixture_name##_fixture_object; \
483+ object->termsig = signal; \
484+ object->timeout = tmout; \
485+ _##fixture_name##_##test_name##_object = object; \
486+ __register_test(object); \
429487 } \
430488 static void fixture_name##_##test_name( \
431489 struct __test_metadata __attribute__((unused)) *_metadata, \
@@ -833,11 +891,12 @@ struct __test_xfail {
833891 { \
834892 .fixture = &_##fixture_name##_fixture_object, \
835893 .variant = &_##fixture_name##_##variant_name##_object, \
836- .test = &_##fixture_name##_##test_name##_object, \
837894 }; \
838895 static void __attribute__((constructor)) \
839896 _register_##fixture_name##_##variant_name##_##test_name##_xfail(void) \
840897 { \
898+ _##fixture_name##_##variant_name##_##test_name##_xfail.test = \
899+ _##fixture_name##_##test_name##_object; \
841900 __register_xfail(&_##fixture_name##_##variant_name##_##test_name##_xfail); \
842901 }
843902
@@ -880,7 +939,6 @@ struct __test_metadata {
880939 bool timed_out ; /* did this test timeout instead of exiting? */
881940 bool aborted ; /* stopped test due to failed ASSERT */
882941 bool setup_completed ; /* did setup finish? */
883- bool teardown_parent ; /* run teardown in a parent process */
884942 jmp_buf env ; /* for exiting out of test early */
885943 struct __test_results * results ;
886944 struct __test_metadata * prev , * next ;
@@ -1164,6 +1222,9 @@ void __run_test(struct __fixture_metadata *f,
11641222 /* reset test struct */
11651223 t -> exit_code = KSFT_PASS ;
11661224 t -> trigger = 0 ;
1225+ t -> aborted = false;
1226+ t -> setup_completed = false;
1227+ memset (t -> env , 0 , sizeof (t -> env ));
11671228 memset (t -> results -> reason , 0 , sizeof (t -> results -> reason ));
11681229
11691230 if (asprintf (& test_name , "%s%s%s.%s" , f -> name ,
@@ -1179,7 +1240,7 @@ void __run_test(struct __fixture_metadata *f,
11791240 fflush (stdout );
11801241 fflush (stderr );
11811242
1182- t -> pid = fork ();
1243+ t -> pid = clone3_vfork ();
11831244 if (t -> pid < 0 ) {
11841245 ksft_print_msg ("ERROR SPAWNING TEST CHILD\n" );
11851246 t -> exit_code = KSFT_FAIL ;
0 commit comments