stack_expansion_ldst.c 4.4 KB

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