linux/tools/testing/selftests/sigaltstack/sas.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Stas Sergeev <stsp@users.sourceforge.net>
   4 *
   5 * test sigaltstack(SS_ONSTACK | SS_AUTODISARM)
   6 * If that succeeds, then swapcontext() can be used inside sighandler safely.
   7 *
   8 */
   9
  10#define _GNU_SOURCE
  11#include <signal.h>
  12#include <stdio.h>
  13#include <stdlib.h>
  14#include <sys/mman.h>
  15#include <ucontext.h>
  16#include <alloca.h>
  17#include <string.h>
  18#include <assert.h>
  19#include <errno.h>
  20#include <sys/auxv.h>
  21
  22#include "../kselftest.h"
  23
  24#ifndef SS_AUTODISARM
  25#define SS_AUTODISARM  (1U << 31)
  26#endif
  27
  28#ifndef AT_MINSIGSTKSZ
  29#define AT_MINSIGSTKSZ  51
  30#endif
  31
  32static unsigned int stack_size;
  33static void *sstack, *ustack;
  34static ucontext_t uc, sc;
  35static const char *msg = "[OK]\tStack preserved";
  36static const char *msg2 = "[FAIL]\tStack corrupted";
  37struct stk_data {
  38        char msg[128];
  39        int flag;
  40};
  41
  42void my_usr1(int sig, siginfo_t *si, void *u)
  43{
  44        char *aa;
  45        int err;
  46        stack_t stk;
  47        struct stk_data *p;
  48
  49#if __s390x__
  50        register unsigned long sp asm("%15");
  51#else
  52        register unsigned long sp asm("sp");
  53#endif
  54
  55        if (sp < (unsigned long)sstack ||
  56                        sp >= (unsigned long)sstack + stack_size) {
  57                ksft_exit_fail_msg("SP is not on sigaltstack\n");
  58        }
  59        /* put some data on stack. other sighandler will try to overwrite it */
  60        aa = alloca(1024);
  61        assert(aa);
  62        p = (struct stk_data *)(aa + 512);
  63        strcpy(p->msg, msg);
  64        p->flag = 1;
  65        ksft_print_msg("[RUN]\tsignal USR1\n");
  66        err = sigaltstack(NULL, &stk);
  67        if (err) {
  68                ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
  69                exit(EXIT_FAILURE);
  70        }
  71        if (stk.ss_flags != SS_DISABLE)
  72                ksft_test_result_fail("tss_flags=%x, should be SS_DISABLE\n",
  73                                stk.ss_flags);
  74        else
  75                ksft_test_result_pass(
  76                                "sigaltstack is disabled in sighandler\n");
  77        swapcontext(&sc, &uc);
  78        ksft_print_msg("%s\n", p->msg);
  79        if (!p->flag) {
  80                ksft_exit_fail_msg("[RUN]\tAborting\n");
  81                exit(EXIT_FAILURE);
  82        }
  83}
  84
  85void my_usr2(int sig, siginfo_t *si, void *u)
  86{
  87        char *aa;
  88        struct stk_data *p;
  89
  90        ksft_print_msg("[RUN]\tsignal USR2\n");
  91        aa = alloca(1024);
  92        /* dont run valgrind on this */
  93        /* try to find the data stored by previous sighandler */
  94        p = memmem(aa, 1024, msg, strlen(msg));
  95        if (p) {
  96                ksft_test_result_fail("sigaltstack re-used\n");
  97                /* corrupt the data */
  98                strcpy(p->msg, msg2);
  99                /* tell other sighandler that his data is corrupted */
 100                p->flag = 0;
 101        }
 102}
 103
 104static void switch_fn(void)
 105{
 106        ksft_print_msg("[RUN]\tswitched to user ctx\n");
 107        raise(SIGUSR2);
 108        setcontext(&sc);
 109}
 110
 111int main(void)
 112{
 113        struct sigaction act;
 114        stack_t stk;
 115        int err;
 116
 117        /* Make sure more than the required minimum. */
 118        stack_size = getauxval(AT_MINSIGSTKSZ) + SIGSTKSZ;
 119        ksft_print_msg("[NOTE]\tthe stack size is %lu\n", stack_size);
 120
 121        ksft_print_header();
 122        ksft_set_plan(3);
 123
 124        sigemptyset(&act.sa_mask);
 125        act.sa_flags = SA_ONSTACK | SA_SIGINFO;
 126        act.sa_sigaction = my_usr1;
 127        sigaction(SIGUSR1, &act, NULL);
 128        act.sa_sigaction = my_usr2;
 129        sigaction(SIGUSR2, &act, NULL);
 130        sstack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
 131                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
 132        if (sstack == MAP_FAILED) {
 133                ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
 134                return EXIT_FAILURE;
 135        }
 136
 137        err = sigaltstack(NULL, &stk);
 138        if (err) {
 139                ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
 140                exit(EXIT_FAILURE);
 141        }
 142        if (stk.ss_flags == SS_DISABLE) {
 143                ksft_test_result_pass(
 144                                "Initial sigaltstack state was SS_DISABLE\n");
 145        } else {
 146                ksft_exit_fail_msg("Initial sigaltstack state was %x; "
 147                       "should have been SS_DISABLE\n", stk.ss_flags);
 148                return EXIT_FAILURE;
 149        }
 150
 151        stk.ss_sp = sstack;
 152        stk.ss_size = stack_size;
 153        stk.ss_flags = SS_ONSTACK | SS_AUTODISARM;
 154        err = sigaltstack(&stk, NULL);
 155        if (err) {
 156                if (errno == EINVAL) {
 157                        ksft_test_result_skip(
 158                                "[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n");
 159                        /*
 160                         * If test cases for the !SS_AUTODISARM variant were
 161                         * added, we could still run them.  We don't have any
 162                         * test cases like that yet, so just exit and report
 163                         * success.
 164                         */
 165                        return 0;
 166                } else {
 167                        ksft_exit_fail_msg(
 168                                "sigaltstack(SS_ONSTACK | SS_AUTODISARM)  %s\n",
 169                                        strerror(errno));
 170                        return EXIT_FAILURE;
 171                }
 172        }
 173
 174        ustack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
 175                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
 176        if (ustack == MAP_FAILED) {
 177                ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
 178                return EXIT_FAILURE;
 179        }
 180        getcontext(&uc);
 181        uc.uc_link = NULL;
 182        uc.uc_stack.ss_sp = ustack;
 183        uc.uc_stack.ss_size = stack_size;
 184        makecontext(&uc, switch_fn, 0);
 185        raise(SIGUSR1);
 186
 187        err = sigaltstack(NULL, &stk);
 188        if (err) {
 189                ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
 190                exit(EXIT_FAILURE);
 191        }
 192        if (stk.ss_flags != SS_AUTODISARM) {
 193                ksft_exit_fail_msg("ss_flags=%x, should be SS_AUTODISARM\n",
 194                                stk.ss_flags);
 195                exit(EXIT_FAILURE);
 196        }
 197        ksft_test_result_pass(
 198                        "sigaltstack is still SS_AUTODISARM after signal\n");
 199
 200        ksft_exit_pass();
 201        return 0;
 202}
 203