Skip to content

Commit 63e6b2a

Browse files
keesshuahkh
authored andcommitted
selftests/harness: Run TEARDOWN for ASSERT failures
The kselftest test harness has traditionally not run the registered TEARDOWN handler when a test encountered an ASSERT. This creates unexpected situations and tests need to be very careful about using ASSERT, which seems a needless hurdle for test writers. Because of the harness's design for optional failure handlers, the original implementation of ASSERT used an abort() to immediately stop execution, but that meant the context for running teardown was lost. Instead, use setjmp/longjmp so that teardown can be done. Failed SETUP routines continue to not be followed by TEARDOWN, though. Cc: Andy Lutomirski <[email protected]> Cc: Will Drewry <[email protected]> Cc: Shuah Khan <[email protected]> Cc: [email protected] Signed-off-by: Kees Cook <[email protected]> Signed-off-by: Shuah Khan <[email protected]>
1 parent 187816d commit 63e6b2a

File tree

1 file changed

+34
-15
lines changed

1 file changed

+34
-15
lines changed

tools/testing/selftests/kselftest_harness.h

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include <sys/types.h>
6565
#include <sys/wait.h>
6666
#include <unistd.h>
67+
#include <setjmp.h>
6768

6869
#include "kselftest.h"
6970

@@ -183,7 +184,10 @@
183184
struct __test_metadata *_metadata, \
184185
struct __fixture_variant_metadata *variant) \
185186
{ \
186-
test_name(_metadata); \
187+
_metadata->setup_completed = true; \
188+
if (setjmp(_metadata->env) == 0) \
189+
test_name(_metadata); \
190+
__test_check_assert(_metadata); \
187191
} \
188192
static struct __test_metadata _##test_name##_object = \
189193
{ .name = #test_name, \
@@ -356,10 +360,7 @@
356360
* Defines a test that depends on a fixture (e.g., is part of a test case).
357361
* Very similar to TEST() except that *self* is the setup instance of fixture's
358362
* datatype exposed for use by the implementation.
359-
*
360-
* Warning: use of ASSERT_* here will skip TEARDOWN.
361363
*/
362-
/* TODO(wad) register fixtures on dedicated test lists. */
363364
#define TEST_F(fixture_name, test_name) \
364365
__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
365366

@@ -381,12 +382,17 @@
381382
/* fixture data is alloced, setup, and torn down per call. */ \
382383
FIXTURE_DATA(fixture_name) self; \
383384
memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \
384-
fixture_name##_setup(_metadata, &self, variant->data); \
385-
/* Let setup failure terminate early. */ \
386-
if (!_metadata->passed) \
387-
return; \
388-
fixture_name##_##test_name(_metadata, &self, variant->data); \
389-
fixture_name##_teardown(_metadata, &self); \
385+
if (setjmp(_metadata->env) == 0) { \
386+
fixture_name##_setup(_metadata, &self, variant->data); \
387+
/* Let setup failure terminate early. */ \
388+
if (!_metadata->passed) \
389+
return; \
390+
_metadata->setup_completed = true; \
391+
fixture_name##_##test_name(_metadata, &self, variant->data); \
392+
} \
393+
if (_metadata->setup_completed) \
394+
fixture_name##_teardown(_metadata, &self); \
395+
__test_check_assert(_metadata); \
390396
} \
391397
static struct __test_metadata \
392398
_##fixture_name##_##test_name##_object = { \
@@ -683,7 +689,7 @@
683689
*/
684690
#define OPTIONAL_HANDLER(_assert) \
685691
for (; _metadata->trigger; _metadata->trigger = \
686-
__bail(_assert, _metadata->no_print, _metadata->step))
692+
__bail(_assert, _metadata))
687693

688694
#define __INC_STEP(_metadata) \
689695
/* Keep "step" below 255 (which is used for "SKIP" reporting). */ \
@@ -830,6 +836,9 @@ struct __test_metadata {
830836
bool timed_out; /* did this test timeout instead of exiting? */
831837
__u8 step;
832838
bool no_print; /* manual trigger when TH_LOG_STREAM is not available */
839+
bool aborted; /* stopped test due to failed ASSERT */
840+
bool setup_completed; /* did setup finish? */
841+
jmp_buf env; /* for exiting out of test early */
833842
struct __test_results *results;
834843
struct __test_metadata *prev, *next;
835844
};
@@ -848,16 +857,26 @@ static inline void __register_test(struct __test_metadata *t)
848857
__LIST_APPEND(t->fixture->tests, t);
849858
}
850859

851-
static inline int __bail(int for_realz, bool no_print, __u8 step)
860+
static inline int __bail(int for_realz, struct __test_metadata *t)
852861
{
862+
/* if this is ASSERT, return immediately. */
853863
if (for_realz) {
854-
if (no_print)
855-
_exit(step);
856-
abort();
864+
t->aborted = true;
865+
longjmp(t->env, 1);
857866
}
867+
/* otherwise, end the for loop and continue. */
858868
return 0;
859869
}
860870

871+
static inline void __test_check_assert(struct __test_metadata *t)
872+
{
873+
if (t->aborted) {
874+
if (t->no_print)
875+
_exit(t->step);
876+
abort();
877+
}
878+
}
879+
861880
struct __test_metadata *__active_test;
862881
static void __timeout_handler(int sig, siginfo_t *info, void *ucontext)
863882
{

0 commit comments

Comments
 (0)