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