selftests/harness: Handle timeouts cleanly

When a selftest would timeout before, the program would just fall over
and no accounting of failures would be reported (i.e. it would result in
an incomplete TAP report). Instead, add an explicit SIGALRM handler to
cleanly catch and report the timeout.

Before:

	[==========] Running 2 tests from 2 test cases.
	[ RUN      ] timeout.finish
	[       OK ] timeout.finish
	[ RUN      ] timeout.too_long
	Alarm clock

After:

	[==========] Running 2 tests from 2 test cases.
	[ RUN      ] timeout.finish
	[       OK ] timeout.finish
	[ RUN      ] timeout.too_long
	timeout.too_long: Test terminated by timeout
	[     FAIL ] timeout.too_long
	[==========] 1 / 2 tests passed.
	[  FAILED  ]

Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
This commit is contained in:
Kees Cook 2020-03-13 16:12:52 -07:00 committed by Shuah Khan
parent f46f576280
commit c31801da6e
1 changed files with 51 additions and 2 deletions

View File

@ -639,7 +639,8 @@ struct __test_metadata {
int termsig;
int passed;
int trigger; /* extra handler after the evaluation */
int timeout;
int timeout; /* seconds to wait for test timeout */
bool timed_out; /* did this test timeout instead of exiting? */
__u8 step;
bool no_print; /* manual trigger when TH_LOG_STREAM is not available */
struct __test_metadata *prev, *next;
@ -696,15 +697,63 @@ static inline int __bail(int for_realz, bool no_print, __u8 step)
return 0;
}
struct __test_metadata *__active_test;
static void __timeout_handler(int sig, siginfo_t *info, void *ucontext)
{
struct __test_metadata *t = __active_test;
/* Sanity check handler execution environment. */
if (!t) {
fprintf(TH_LOG_STREAM,
"no active test in SIGARLM handler!?\n");
abort();
}
if (sig != SIGALRM || sig != info->si_signo) {
fprintf(TH_LOG_STREAM,
"%s: SIGALRM handler caught signal %d!?\n",
t->name, sig != SIGALRM ? sig : info->si_signo);
abort();
}
t->timed_out = true;
kill(t->pid, SIGKILL);
}
void __wait_for_test(struct __test_metadata *t)
{
struct sigaction action = {
.sa_sigaction = __timeout_handler,
.sa_flags = SA_SIGINFO,
};
struct sigaction saved_action;
int status;
if (sigaction(SIGALRM, &action, &saved_action)) {
t->passed = 0;
fprintf(TH_LOG_STREAM,
"%s: unable to install SIGARLM handler\n",
t->name);
return;
}
__active_test = t;
t->timed_out = false;
alarm(t->timeout);
waitpid(t->pid, &status, 0);
alarm(0);
if (sigaction(SIGALRM, &saved_action, NULL)) {
t->passed = 0;
fprintf(TH_LOG_STREAM,
"%s: unable to uninstall SIGARLM handler\n",
t->name);
return;
}
__active_test = NULL;
if (WIFEXITED(status)) {
if (t->timed_out) {
t->passed = 0;
fprintf(TH_LOG_STREAM,
"%s: Test terminated by timeout\n", t->name);
} else if (WIFEXITED(status)) {
t->passed = t->termsig == -1 ? !WEXITSTATUS(status) : 0;
if (t->termsig != -1) {
fprintf(TH_LOG_STREAM,