Skip to content

Commit 0710a1a

Browse files
l0koddavem330
authored andcommitted
selftests/harness: Merge TEST_F_FORK() into TEST_F()
Replace Landlock-specific TEST_F_FORK() with an improved TEST_F() which brings four related changes: Run TEST_F()'s tests in a grandchild process to make it possible to drop privileges and delegate teardown to the parent. Compared to TEST_F_FORK(), simplify handling of the test grandchild process thanks to vfork(2), and makes it generic (e.g. no explicit conversion between exit code and _metadata). Compared to TEST_F_FORK(), run teardown even when tests failed with an assert thanks to commit 63e6b2a ("selftests/harness: Run TEARDOWN for ASSERT failures"). Simplify the test harness code by removing the no_print and step fields which are not used. I added this feature just after I made kselftest_harness.h more broadly available but this step counter remained even though it wasn't needed after all. See commit 369130b ("selftests: Enhance kselftest_harness.h to print which assert failed"). Replace spaces with tabs in one line of __TEST_F_IMPL(). Cc: Günther Noack <[email protected]> Cc: Shuah Khan <[email protected]> Cc: Will Drewry <[email protected]> Signed-off-by: Mickaël Salaün <[email protected]> Reviewed-by: Kees Cook <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent e740486 commit 0710a1a

File tree

2 files changed

+27
-91
lines changed

2 files changed

+27
-91
lines changed

tools/testing/selftests/kselftest_harness.h

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,6 @@
9595
* E.g., #define TH_LOG_ENABLED 1
9696
*
9797
* If no definition is provided, logging is enabled by default.
98-
*
99-
* If there is no way to print an error message for the process running the
100-
* test (e.g. not allowed to write to stderr), it is still possible to get the
101-
* ASSERT_* number for which the test failed. This behavior can be enabled by
102-
* writing `_metadata->no_print = true;` before the check sequence that is
103-
* unable to print. When an error occur, instead of printing an error message
104-
* and calling `abort(3)`, the test process call `_exit(2)` with the assert
105-
* number as argument, which is then printed by the parent process.
10698
*/
10799
#define TH_LOG(fmt, ...) do { \
108100
if (TH_LOG_ENABLED) \
@@ -363,6 +355,11 @@
363355
* Defines a test that depends on a fixture (e.g., is part of a test case).
364356
* Very similar to TEST() except that *self* is the setup instance of fixture's
365357
* datatype exposed for use by the implementation.
358+
*
359+
* The @test_name code is run in a separate process sharing the same memory
360+
* (i.e. vfork), which means that the test process can update its privileges
361+
* without impacting the related FIXTURE_TEARDOWN() (e.g. to remove files from
362+
* a directory where write access was dropped).
366363
*/
367364
#define TEST_F(fixture_name, test_name) \
368365
__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
@@ -384,15 +381,28 @@
384381
{ \
385382
/* fixture data is alloced, setup, and torn down per call. */ \
386383
FIXTURE_DATA(fixture_name) self; \
384+
pid_t child = 1; \
387385
memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \
388386
if (setjmp(_metadata->env) == 0) { \
389387
fixture_name##_setup(_metadata, &self, variant->data); \
390388
/* Let setup failure terminate early. */ \
391-
if (!_metadata->passed || _metadata->skip) \
389+
if (!_metadata->passed || _metadata->skip) \
392390
return; \
393391
_metadata->setup_completed = true; \
394-
fixture_name##_##test_name(_metadata, &self, variant->data); \
392+
/* Use the same _metadata. */ \
393+
child = vfork(); \
394+
if (child == 0) { \
395+
fixture_name##_##test_name(_metadata, &self, variant->data); \
396+
_exit(0); \
397+
} \
398+
if (child < 0) { \
399+
ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \
400+
_metadata->passed = 0; \
401+
} \
395402
} \
403+
if (child == 0) \
404+
/* Child failed and updated the shared _metadata. */ \
405+
_exit(0); \
396406
if (_metadata->setup_completed) \
397407
fixture_name##_teardown(_metadata, &self, variant->data); \
398408
__test_check_assert(_metadata); \
@@ -694,18 +704,12 @@
694704
for (; _metadata->trigger; _metadata->trigger = \
695705
__bail(_assert, _metadata))
696706

697-
#define __INC_STEP(_metadata) \
698-
/* Keep "step" below 255 (which is used for "SKIP" reporting). */ \
699-
if (_metadata->passed && _metadata->step < 253) \
700-
_metadata->step++;
701-
702707
#define is_signed_type(var) (!!(((__typeof__(var))(-1)) < (__typeof__(var))1))
703708

704709
#define __EXPECT(_expected, _expected_str, _seen, _seen_str, _t, _assert) do { \
705710
/* Avoid multiple evaluation of the cases */ \
706711
__typeof__(_expected) __exp = (_expected); \
707712
__typeof__(_seen) __seen = (_seen); \
708-
if (_assert) __INC_STEP(_metadata); \
709713
if (!(__exp _t __seen)) { \
710714
/* Report with actual signedness to avoid weird output. */ \
711715
switch (is_signed_type(__exp) * 2 + is_signed_type(__seen)) { \
@@ -751,7 +755,6 @@
751755
#define __EXPECT_STR(_expected, _seen, _t, _assert) do { \
752756
const char *__exp = (_expected); \
753757
const char *__seen = (_seen); \
754-
if (_assert) __INC_STEP(_metadata); \
755758
if (!(strcmp(__exp, __seen) _t 0)) { \
756759
__TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \
757760
_metadata->passed = 0; \
@@ -837,8 +840,6 @@ struct __test_metadata {
837840
int trigger; /* extra handler after the evaluation */
838841
int timeout; /* seconds to wait for test timeout */
839842
bool timed_out; /* did this test timeout instead of exiting? */
840-
__u8 step;
841-
bool no_print; /* manual trigger when TH_LOG_STREAM is not available */
842843
bool aborted; /* stopped test due to failed ASSERT */
843844
bool setup_completed; /* did setup finish? */
844845
jmp_buf env; /* for exiting out of test early */
@@ -873,11 +874,8 @@ static inline int __bail(int for_realz, struct __test_metadata *t)
873874

874875
static inline void __test_check_assert(struct __test_metadata *t)
875876
{
876-
if (t->aborted) {
877-
if (t->no_print)
878-
_exit(t->step);
877+
if (t->aborted)
879878
abort();
880-
}
881879
}
882880

883881
struct __test_metadata *__active_test;
@@ -954,13 +952,12 @@ void __wait_for_test(struct __test_metadata *t)
954952
case 0:
955953
t->passed = 1;
956954
break;
957-
/* Other failure, assume step report. */
955+
/* Failure */
958956
default:
959957
t->passed = 0;
960958
fprintf(TH_LOG_STREAM,
961-
"# %s: Test failed at step #%d\n",
962-
t->name,
963-
WEXITSTATUS(status));
959+
"# %s: Test failed\n",
960+
t->name);
964961
}
965962
}
966963
} else if (WIFSIGNALED(status)) {
@@ -1114,8 +1111,6 @@ void __run_test(struct __fixture_metadata *f,
11141111
t->passed = 1;
11151112
t->skip = 0;
11161113
t->trigger = 0;
1117-
t->step = 1;
1118-
t->no_print = 0;
11191114
memset(t->results->reason, 0, sizeof(t->results->reason));
11201115

11211116
ksft_print_msg(" RUN %s%s%s.%s ...\n",
@@ -1137,8 +1132,7 @@ void __run_test(struct __fixture_metadata *f,
11371132
/* Pass is exit 0 */
11381133
if (t->passed)
11391134
_exit(0);
1140-
/* Something else happened, report the step. */
1141-
_exit(t->step);
1135+
_exit(1);
11421136
} else {
11431137
__wait_for_test(t);
11441138
}

tools/testing/selftests/landlock/common.h

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -23,66 +23,8 @@
2323
#define __maybe_unused __attribute__((__unused__))
2424
#endif
2525

26-
/*
27-
* TEST_F_FORK() is useful when a test drop privileges but the corresponding
28-
* FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory
29-
* where write actions are denied). For convenience, FIXTURE_TEARDOWN() is
30-
* also called when the test failed, but not when FIXTURE_SETUP() failed. For
31-
* this to be possible, we must not call abort() but instead exit smoothly
32-
* (hence the step print).
33-
*/
34-
/* clang-format off */
35-
#define TEST_F_FORK(fixture_name, test_name) \
36-
static void fixture_name##_##test_name##_child( \
37-
struct __test_metadata *_metadata, \
38-
FIXTURE_DATA(fixture_name) *self, \
39-
const FIXTURE_VARIANT(fixture_name) *variant); \
40-
__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT) \
41-
{ \
42-
int status; \
43-
const pid_t child = fork(); \
44-
if (child < 0) \
45-
abort(); \
46-
if (child == 0) { \
47-
_metadata->no_print = 1; \
48-
fixture_name##_##test_name##_child(_metadata, self, variant); \
49-
if (_metadata->skip) \
50-
_exit(255); \
51-
if (_metadata->passed) \
52-
_exit(0); \
53-
_exit(_metadata->step); \
54-
} \
55-
if (child != waitpid(child, &status, 0)) \
56-
abort(); \
57-
if (WIFSIGNALED(status) || !WIFEXITED(status)) { \
58-
_metadata->passed = 0; \
59-
_metadata->step = 1; \
60-
return; \
61-
} \
62-
switch (WEXITSTATUS(status)) { \
63-
case 0: \
64-
_metadata->passed = 1; \
65-
break; \
66-
case 255: \
67-
_metadata->passed = 1; \
68-
_metadata->skip = 1; \
69-
break; \
70-
default: \
71-
_metadata->passed = 0; \
72-
_metadata->step = WEXITSTATUS(status); \
73-
break; \
74-
} \
75-
} \
76-
static void fixture_name##_##test_name##_child( \
77-
struct __test_metadata __attribute__((unused)) *_metadata, \
78-
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
79-
const FIXTURE_VARIANT(fixture_name) \
80-
__attribute__((unused)) *variant)
81-
/* clang-format on */
82-
83-
/* Makes backporting easier. */
84-
#undef TEST_F
85-
#define TEST_F(fixture_name, test_name) TEST_F_FORK(fixture_name, test_name)
26+
/* TEST_F_FORK() should not be used for new tests. */
27+
#define TEST_F_FORK(fixture_name, test_name) TEST_F(fixture_name, test_name)
8628

8729
#ifndef landlock_create_ruleset
8830
static inline int

0 commit comments

Comments
 (0)