glibc/debug/tst-longjmp_chk2.c
Florian Weimer 2c41b52901 debug/tst-longjmp_chk2: Make signal handler more conservative [BZ #20248]
Currently, printf needs more stack space than what is available with
SIGSTKSZ.  This commit use the the write system call directly instead.

Also use sig_atomic_t for the “pass” variable (for general
correctness), and restore signal handlers to their defaults, to avoid
masking crashes.
2016-06-13 16:36:46 +02:00

133 lines
3.2 KiB
C

/* Verify longjmp fortify checking does not reject signal stacks.
Test case mostly written by Paolo Bonzini <pbonzini@redhat.com>. */
#include <assert.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
static jmp_buf mainloop;
static sigset_t mainsigset;
static volatile sig_atomic_t pass;
static void
write_message (const char *message)
{
ssize_t unused __attribute__ ((unused));
for (int i = 0; i < pass; ++i)
unused = write (STDOUT_FILENO, " ", 1);
unused = write (STDOUT_FILENO, message, strlen (message));
}
static void
stackoverflow_handler (int sig)
{
stack_t altstack;
/* Sanity check to keep test from looping forever (in case the longjmp
chk code is slightly broken). */
pass++;
assert (pass < 5);
sigaltstack (NULL, &altstack);
write_message ("in signal handler\n");
if (altstack.ss_flags & SS_ONSTACK)
write_message ("on alternate stack\n");
siglongjmp (mainloop, pass);
}
static volatile int *
recurse_1 (int n, volatile int *p)
{
if (n >= 0)
*recurse_1 (n + 1, p) += n;
return p;
}
static int
recurse (int n)
{
int sum = 0;
return *recurse_1 (n, &sum);
}
static int
do_test (void)
{
char mystack[SIGSTKSZ];
stack_t altstack;
struct sigaction action;
sigset_t emptyset;
/* Before starting the endless recursion, try to be friendly to the user's
machine. On some Linux 2.2.x systems, there is no stack limit for user
processes at all. We don't want to kill such systems. */
struct rlimit rl;
rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
setrlimit (RLIMIT_STACK, &rl);
/* Install the alternate stack. */
altstack.ss_sp = mystack;
altstack.ss_size = sizeof (mystack);
altstack.ss_flags = 0; /* no SS_DISABLE */
if (sigaltstack (&altstack, NULL) < 0)
{
puts ("first sigaltstack failed");
return 0;
}
/* Install the SIGSEGV handler. */
sigemptyset (&action.sa_mask);
action.sa_handler = &stackoverflow_handler;
action.sa_flags = SA_ONSTACK;
sigaction (SIGSEGV, &action, (struct sigaction *) NULL);
sigaction (SIGBUS, &action, (struct sigaction *) NULL);
/* Save the current signal mask. */
sigemptyset (&emptyset);
sigprocmask (SIG_BLOCK, &emptyset, &mainsigset);
/* Provoke two stack overflows in a row. */
if (sigsetjmp (mainloop, 1) != 0)
{
assert (pass != 0);
printf ("%*sout of signal handler\n", pass, "");
}
else
assert (pass == 0);
sigaltstack (NULL, &altstack);
if (altstack.ss_flags & SS_ONSTACK)
printf ("%*son alternate stack\n", pass, "");
else
printf ("%*snot on alternate stack\n", pass, "");
if (pass < 2)
{
recurse (0);
puts ("recurse call returned");
return 2;
}
altstack.ss_flags |= SS_DISABLE;
if (sigaltstack (&altstack, NULL) == -1)
printf ("disabling alternate stack failed\n");
else
printf ("disabling alternate stack succeeded \n");
/* Restore the signal handlers, in case we trigger a crash after the
tests above. */
signal (SIGBUS, SIG_DFL);
signal (SIGSEGV, SIG_DFL);
return 0;
}
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"