linux/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Test that loads/stores expand the stack segment, or trigger a SEGV, in
   4 * various conditions.
   5 *
   6 * Based on test code by Tom Lane.
   7 */
   8
   9#undef NDEBUG
  10#include <assert.h>
  11
  12#include <err.h>
  13#include <errno.h>
  14#include <stdio.h>
  15#include <signal.h>
  16#include <stdlib.h>
  17#include <string.h>
  18#include <sys/resource.h>
  19#include <sys/time.h>
  20#include <sys/types.h>
  21#include <sys/wait.h>
  22#include <unistd.h>
  23
  24#define _KB (1024)
  25#define _MB (1024 * 1024)
  26
  27volatile char *stack_top_ptr;
  28volatile unsigned long stack_top_sp;
  29volatile char c;
  30
  31enum access_type {
  32        LOAD,
  33        STORE,
  34};
  35
  36/*
  37 * Consume stack until the stack pointer is below @target_sp, then do an access
  38 * (load or store) at offset @delta from either the base of the stack or the
  39 * current stack pointer.
  40 */
  41__attribute__ ((noinline))
  42int consume_stack(unsigned long target_sp, unsigned long stack_high, int delta, enum access_type type)
  43{
  44        unsigned long target;
  45        char stack_cur;
  46
  47        if ((unsigned long)&stack_cur > target_sp)
  48                return consume_stack(target_sp, stack_high, delta, type);
  49        else {
  50                // We don't really need this, but without it GCC might not
  51                // generate a recursive call above.
  52                stack_top_ptr = &stack_cur;
  53
  54#ifdef __powerpc__
  55                asm volatile ("mr %[sp], %%r1" : [sp] "=r" (stack_top_sp));
  56#else
  57                asm volatile ("mov %%rsp, %[sp]" : [sp] "=r" (stack_top_sp));
  58#endif
  59                target = stack_high - delta + 1;
  60                volatile char *p = (char *)target;
  61
  62                if (type == STORE)
  63                        *p = c;
  64                else
  65                        c = *p;
  66
  67                // Do something to prevent the stack frame being popped prior to
  68                // our access above.
  69                getpid();
  70        }
  71
  72        return 0;
  73}
  74
  75static int search_proc_maps(char *needle, unsigned long *low, unsigned long *high)
  76{
  77        unsigned long start, end;
  78        static char buf[4096];
  79        char name[128];
  80        FILE *f;
  81        int rc;
  82
  83        f = fopen("/proc/self/maps", "r");
  84        if (!f) {
  85                perror("fopen");
  86                return -1;
  87        }
  88
  89        while (fgets(buf, sizeof(buf), f)) {
  90                rc = sscanf(buf, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n",
  91                            &start, &end, name);
  92                if (rc == 2)
  93                        continue;
  94
  95                if (rc != 3) {
  96                        printf("sscanf errored\n");
  97                        rc = -1;
  98                        break;
  99                }
 100
 101                if (strstr(name, needle)) {
 102                        *low = start;
 103                        *high = end - 1;
 104                        rc = 0;
 105                        break;
 106                }
 107        }
 108
 109        fclose(f);
 110
 111        return rc;
 112}
 113
 114int child(unsigned int stack_used, int delta, enum access_type type)
 115{
 116        unsigned long low, stack_high;
 117
 118        assert(search_proc_maps("[stack]", &low, &stack_high) == 0);
 119
 120        assert(consume_stack(stack_high - stack_used, stack_high, delta, type) == 0);
 121
 122        printf("Access OK: %s delta %-7d used size 0x%06x stack high 0x%lx top_ptr %p top sp 0x%lx actual used 0x%lx\n",
 123               type == LOAD ? "load" : "store", delta, stack_used, stack_high,
 124               stack_top_ptr, stack_top_sp, stack_high - stack_top_sp + 1);
 125
 126        return 0;
 127}
 128
 129static int test_one(unsigned int stack_used, int delta, enum access_type type)
 130{
 131        pid_t pid;
 132        int rc;
 133
 134        pid = fork();
 135        if (pid == 0)
 136                exit(child(stack_used, delta, type));
 137
 138        assert(waitpid(pid, &rc, 0) != -1);
 139
 140        if (WIFEXITED(rc) && WEXITSTATUS(rc) == 0)
 141                return 0;
 142
 143        // We don't expect a non-zero exit that's not a signal
 144        assert(!WIFEXITED(rc));
 145
 146        printf("Faulted:   %s delta %-7d used size 0x%06x signal %d\n",
 147               type == LOAD ? "load" : "store", delta, stack_used,
 148               WTERMSIG(rc));
 149
 150        return 1;
 151}
 152
 153// This is fairly arbitrary but is well below any of the targets below,
 154// so that the delta between the stack pointer and the target is large.
 155#define DEFAULT_SIZE    (32 * _KB)
 156
 157static void test_one_type(enum access_type type, unsigned long page_size, unsigned long rlim_cur)
 158{
 159        unsigned long delta;
 160
 161        // We should be able to access anywhere within the rlimit
 162        for (delta = page_size; delta <= rlim_cur; delta += page_size)
 163                assert(test_one(DEFAULT_SIZE, delta, type) == 0);
 164
 165        assert(test_one(DEFAULT_SIZE, rlim_cur, type) == 0);
 166
 167        // But if we go past the rlimit it should fail
 168        assert(test_one(DEFAULT_SIZE, rlim_cur + 1, type) != 0);
 169}
 170
 171static int test(void)
 172{
 173        unsigned long page_size;
 174        struct rlimit rlimit;
 175
 176        page_size = getpagesize();
 177        getrlimit(RLIMIT_STACK, &rlimit);
 178        printf("Stack rlimit is 0x%lx\n", rlimit.rlim_cur);
 179
 180        printf("Testing loads ...\n");
 181        test_one_type(LOAD, page_size, rlimit.rlim_cur);
 182        printf("Testing stores ...\n");
 183        test_one_type(STORE, page_size, rlimit.rlim_cur);
 184
 185        printf("All OK\n");
 186
 187        return 0;
 188}
 189
 190#ifdef __powerpc__
 191#include "utils.h"
 192
 193int main(void)
 194{
 195        return test_harness(test, "stack_expansion_ldst");
 196}
 197#else
 198int main(void)
 199{
 200        return test();
 201}
 202#endif
 203