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        ksft_set_plan(3);
 113
 114        sigemptyset(&act.sa_mask);
 115        act.sa_flags = SA_ONSTACK | SA_SIGINFO;
 116        act.sa_sigaction = my_usr1;
 117        sigaction(SIGUSR1, &act, NULL);
 118        act.sa_sigaction = my_usr2;
 119        sigaction(SIGUSR2, &act, NULL);
 120        sstack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
 121                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
 122        if (sstack == MAP_FAILED) {
 123                ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
 124                return EXIT_FAILURE;
 125        }
 126
 127        err = sigaltstack(NULL, &stk);
 128        if (err) {
 129                ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
 130                exit(EXIT_FAILURE);
 131        }
 132        if (stk.ss_flags == SS_DISABLE) {
 133                ksft_test_result_pass(
 134                                "Initial sigaltstack state was SS_DISABLE\n");
 135        } else {
 136                ksft_exit_fail_msg("Initial sigaltstack state was %x; "
 137                       "should have been SS_DISABLE\n", stk.ss_flags);
 138                return EXIT_FAILURE;
 139        }
 140
 141        stk.ss_sp = sstack;
 142        stk.ss_size = SIGSTKSZ;
 143        stk.ss_flags = SS_ONSTACK | SS_AUTODISARM;
 144        err = sigaltstack(&stk, NULL);
 145        if (err) {
 146                if (errno == EINVAL) {
 147                        ksft_exit_skip(
 148                                "[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n");
 149                        /*
 150                         * If test cases for the !SS_AUTODISARM variant were
 151                         * added, we could still run them.  We don't have any
 152                         * test cases like that yet, so just exit and report
 153                         * success.
 154                         */
 155                        return 0;
 156                } else {
 157                        ksft_exit_fail_msg(
 158                                "sigaltstack(SS_ONSTACK | SS_AUTODISARM)  %s\n",
 159                                        strerror(errno));
 160                        return EXIT_FAILURE;
 161                }
 162        }
 163
 164        ustack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
 165                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
 166        if (ustack == MAP_FAILED) {
 167                ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
 168                return EXIT_FAILURE;
 169        }
 170        getcontext(&uc);
 171        uc.uc_link = NULL;
 172        uc.uc_stack.ss_sp = ustack;
 173        uc.uc_stack.ss_size = SIGSTKSZ;
 174        makecontext(&uc, switch_fn, 0);
 175        raise(SIGUSR1);
 176
 177        err = sigaltstack(NULL, &stk);
 178        if (err) {
 179                ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
 180                exit(EXIT_FAILURE);
 181        }
 182        if (stk.ss_flags != SS_AUTODISARM) {
 183                ksft_exit_fail_msg("ss_flags=%x, should be SS_AUTODISARM\n",
 184                                stk.ss_flags);
 185                exit(EXIT_FAILURE);
 186        }
 187        ksft_test_result_pass(
 188                        "sigaltstack is still SS_AUTODISARM after signal\n");
 189
 190        ksft_exit_pass();
 191        return 0;
 192}
 193