Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Test that loads/stores expand the stack segment, or trigger a SEGV, in
0004  * various conditions.
0005  *
0006  * Based on test code by Tom Lane.
0007  */
0008 
0009 #undef NDEBUG
0010 #include <assert.h>
0011 
0012 #include <err.h>
0013 #include <errno.h>
0014 #include <stdio.h>
0015 #include <signal.h>
0016 #include <stdlib.h>
0017 #include <string.h>
0018 #include <sys/resource.h>
0019 #include <sys/time.h>
0020 #include <sys/types.h>
0021 #include <sys/wait.h>
0022 #include <unistd.h>
0023 
0024 #define _KB (1024)
0025 #define _MB (1024 * 1024)
0026 
0027 volatile char *stack_top_ptr;
0028 volatile unsigned long stack_top_sp;
0029 volatile char c;
0030 
0031 enum access_type {
0032     LOAD,
0033     STORE,
0034 };
0035 
0036 /*
0037  * Consume stack until the stack pointer is below @target_sp, then do an access
0038  * (load or store) at offset @delta from either the base of the stack or the
0039  * current stack pointer.
0040  */
0041 __attribute__ ((noinline))
0042 int consume_stack(unsigned long target_sp, unsigned long stack_high, int delta, enum access_type type)
0043 {
0044     unsigned long target;
0045     char stack_cur;
0046 
0047     if ((unsigned long)&stack_cur > target_sp)
0048         return consume_stack(target_sp, stack_high, delta, type);
0049     else {
0050         // We don't really need this, but without it GCC might not
0051         // generate a recursive call above.
0052         stack_top_ptr = &stack_cur;
0053 
0054 #ifdef __powerpc__
0055         asm volatile ("mr %[sp], %%r1" : [sp] "=r" (stack_top_sp));
0056 #else
0057         asm volatile ("mov %%rsp, %[sp]" : [sp] "=r" (stack_top_sp));
0058 #endif
0059         target = stack_high - delta + 1;
0060         volatile char *p = (char *)target;
0061 
0062         if (type == STORE)
0063             *p = c;
0064         else
0065             c = *p;
0066 
0067         // Do something to prevent the stack frame being popped prior to
0068         // our access above.
0069         getpid();
0070     }
0071 
0072     return 0;
0073 }
0074 
0075 static int search_proc_maps(char *needle, unsigned long *low, unsigned long *high)
0076 {
0077     unsigned long start, end;
0078     static char buf[4096];
0079     char name[128];
0080     FILE *f;
0081     int rc;
0082 
0083     f = fopen("/proc/self/maps", "r");
0084     if (!f) {
0085         perror("fopen");
0086         return -1;
0087     }
0088 
0089     while (fgets(buf, sizeof(buf), f)) {
0090         rc = sscanf(buf, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n",
0091                 &start, &end, name);
0092         if (rc == 2)
0093             continue;
0094 
0095         if (rc != 3) {
0096             printf("sscanf errored\n");
0097             rc = -1;
0098             break;
0099         }
0100 
0101         if (strstr(name, needle)) {
0102             *low = start;
0103             *high = end - 1;
0104             rc = 0;
0105             break;
0106         }
0107     }
0108 
0109     fclose(f);
0110 
0111     return rc;
0112 }
0113 
0114 int child(unsigned int stack_used, int delta, enum access_type type)
0115 {
0116     unsigned long low, stack_high;
0117 
0118     assert(search_proc_maps("[stack]", &low, &stack_high) == 0);
0119 
0120     assert(consume_stack(stack_high - stack_used, stack_high, delta, type) == 0);
0121 
0122     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",
0123            type == LOAD ? "load" : "store", delta, stack_used, stack_high,
0124            stack_top_ptr, stack_top_sp, stack_high - stack_top_sp + 1);
0125 
0126     return 0;
0127 }
0128 
0129 static int test_one(unsigned int stack_used, int delta, enum access_type type)
0130 {
0131     pid_t pid;
0132     int rc;
0133 
0134     pid = fork();
0135     if (pid == 0)
0136         exit(child(stack_used, delta, type));
0137 
0138     assert(waitpid(pid, &rc, 0) != -1);
0139 
0140     if (WIFEXITED(rc) && WEXITSTATUS(rc) == 0)
0141         return 0;
0142 
0143     // We don't expect a non-zero exit that's not a signal
0144     assert(!WIFEXITED(rc));
0145 
0146     printf("Faulted:   %s delta %-7d used size 0x%06x signal %d\n",
0147            type == LOAD ? "load" : "store", delta, stack_used,
0148            WTERMSIG(rc));
0149 
0150     return 1;
0151 }
0152 
0153 // This is fairly arbitrary but is well below any of the targets below,
0154 // so that the delta between the stack pointer and the target is large.
0155 #define DEFAULT_SIZE    (32 * _KB)
0156 
0157 static void test_one_type(enum access_type type, unsigned long page_size, unsigned long rlim_cur)
0158 {
0159     unsigned long delta;
0160 
0161     // We should be able to access anywhere within the rlimit
0162     for (delta = page_size; delta <= rlim_cur; delta += page_size)
0163         assert(test_one(DEFAULT_SIZE, delta, type) == 0);
0164 
0165     assert(test_one(DEFAULT_SIZE, rlim_cur, type) == 0);
0166 
0167     // But if we go past the rlimit it should fail
0168     assert(test_one(DEFAULT_SIZE, rlim_cur + 1, type) != 0);
0169 }
0170 
0171 static int test(void)
0172 {
0173     unsigned long page_size;
0174     struct rlimit rlimit;
0175 
0176     page_size = getpagesize();
0177     getrlimit(RLIMIT_STACK, &rlimit);
0178     printf("Stack rlimit is 0x%lx\n", rlimit.rlim_cur);
0179 
0180     printf("Testing loads ...\n");
0181     test_one_type(LOAD, page_size, rlimit.rlim_cur);
0182     printf("Testing stores ...\n");
0183     test_one_type(STORE, page_size, rlimit.rlim_cur);
0184 
0185     printf("All OK\n");
0186 
0187     return 0;
0188 }
0189 
0190 #ifdef __powerpc__
0191 #include "utils.h"
0192 
0193 int main(void)
0194 {
0195     return test_harness(test, "stack_expansion_ldst");
0196 }
0197 #else
0198 int main(void)
0199 {
0200     return test();
0201 }
0202 #endif