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