66
66
#include <sys/wait.h>
67
67
#include <unistd.h>
68
68
#include <setjmp.h>
69
+ #include <syscall.h>
70
+ #include <linux/sched.h>
69
71
70
72
#include "kselftest.h"
71
73
80
82
# define TH_LOG_ENABLED 1
81
83
#endif
82
84
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
+
83
96
/**
84
97
* TH_LOG()
85
98
*
281
294
* A bare "return;" statement may be used to return early.
282
295
*/
283
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 ) \
284
323
void fixture_name##_teardown( \
285
324
struct __test_metadata __attribute__((unused)) *_metadata, \
286
325
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
325
364
* variant.
326
365
*/
327
366
#define FIXTURE_VARIANT_ADD (fixture_name , variant_name ) \
328
- extern FIXTURE_VARIANT(fixture_name) \
367
+ extern const FIXTURE_VARIANT(fixture_name) \
329
368
_##fixture_name##_##variant_name##_variant; \
330
369
static struct __fixture_variant_metadata \
331
370
_##fixture_name##_##variant_name##_object = \
337
376
__register_fixture_variant(&_##fixture_name##_fixture_object, \
338
377
&_##fixture_name##_##variant_name##_object); \
339
378
} \
340
- FIXTURE_VARIANT(fixture_name) \
379
+ const FIXTURE_VARIANT(fixture_name) \
341
380
_##fixture_name##_##variant_name##_variant =
342
381
343
382
/**
355
394
* Very similar to TEST() except that *self* is the setup instance of fixture's
356
395
* datatype exposed for use by the implementation.
357
396
*
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().
362
402
*/
363
403
#define TEST_F (fixture_name , test_name ) \
364
404
__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
379
419
struct __fixture_variant_metadata *variant) \
380
420
{ \
381
421
/* 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 ; \
383
423
pid_t child = 1 ; \
384
424
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
+ } \
387
438
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 (); \
390
441
if (child == 0 ) { \
391
- fixture_name ##_setup (_metadata , & self , variant -> data ); \
442
+ fixture_name ##_setup (_metadata , self , variant -> data ); \
392
443
/* Let setup failure terminate early. */ \
393
444
if (_metadata -> exit_code ) \
394
445
_exit (0 ); \
395
446
_metadata -> setup_completed = true; \
396
- fixture_name ##_ ##test_name (_metadata, & self, variant->data); \
447
+ fixture_name ##_ ##test_name (_metadata, self, variant->data); \
397
448
} else if (child < 0 || child != waitpid(child, &status, 0)) { \
398
449
ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \
399
450
_metadata->exit_code = KSFT_FAIL; \
400
451
} \
401
452
} \
402
- else \
403
- jmp = true; \
404
453
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); \
407
457
_exit(0); \
408
458
} \
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)) { \
412
469
/* Forward signal to __wait_for_test(). */ \
413
470
kill (getpid (), WTERMSIG (status )); \
471
+ } \
414
472
__test_check_assert (_metadata ); \
415
473
} \
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; \
425
475
static void __attribute__((constructor)) \
426
476
_register_##fixture_name##_##test_name(void) \
427
477
{ \
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); \
429
487
} \
430
488
static void fixture_name##_##test_name( \
431
489
struct __test_metadata __attribute__((unused)) *_metadata, \
@@ -833,11 +891,12 @@ struct __test_xfail {
833
891
{ \
834
892
.fixture = &_##fixture_name##_fixture_object, \
835
893
.variant = &_##fixture_name##_##variant_name##_object, \
836
- .test = &_##fixture_name##_##test_name##_object, \
837
894
}; \
838
895
static void __attribute__((constructor)) \
839
896
_register_##fixture_name##_##variant_name##_##test_name##_xfail(void) \
840
897
{ \
898
+ _##fixture_name##_##variant_name##_##test_name##_xfail.test = \
899
+ _##fixture_name##_##test_name##_object; \
841
900
__register_xfail(&_##fixture_name##_##variant_name##_##test_name##_xfail); \
842
901
}
843
902
@@ -880,7 +939,6 @@ struct __test_metadata {
880
939
bool timed_out ; /* did this test timeout instead of exiting? */
881
940
bool aborted ; /* stopped test due to failed ASSERT */
882
941
bool setup_completed ; /* did setup finish? */
883
- bool teardown_parent ; /* run teardown in a parent process */
884
942
jmp_buf env ; /* for exiting out of test early */
885
943
struct __test_results * results ;
886
944
struct __test_metadata * prev , * next ;
@@ -1164,6 +1222,9 @@ void __run_test(struct __fixture_metadata *f,
1164
1222
/* reset test struct */
1165
1223
t -> exit_code = KSFT_PASS ;
1166
1224
t -> trigger = 0 ;
1225
+ t -> aborted = false;
1226
+ t -> setup_completed = false;
1227
+ memset (t -> env , 0 , sizeof (t -> env ));
1167
1228
memset (t -> results -> reason , 0 , sizeof (t -> results -> reason ));
1168
1229
1169
1230
if (asprintf (& test_name , "%s%s%s.%s" , f -> name ,
@@ -1179,7 +1240,7 @@ void __run_test(struct __fixture_metadata *f,
1179
1240
fflush (stdout );
1180
1241
fflush (stderr );
1181
1242
1182
- t -> pid = fork ();
1243
+ t -> pid = clone3_vfork ();
1183
1244
if (t -> pid < 0 ) {
1184
1245
ksft_print_msg ("ERROR SPAWNING TEST CHILD\n" );
1185
1246
t -> exit_code = KSFT_FAIL ;
0 commit comments