1 /* Licensed under Apache License v2.0 - see LICENSE file for details */
14 static __thread struct altstack_state {
15 char ebuf[ALTSTACK_ERR_MAXLEN];
25 (state.elen += snprintf(state.ebuf + state.elen, \
26 sizeof(state.ebuf) - state.elen, \
27 "%s(altstack@%d) %s%s%s", \
28 state.elen ? "; " : "", __LINE__, (x), \
30 errno ? strerror(errno) : ""))
32 void altstack_perror(void)
34 fprintf(stderr, "%s\n", state.ebuf);
37 char *altstack_geterr(void)
42 static void segvjmp(int signum)
44 longjmp(state.jmp, 1);
48 rlim_t altstack_max(void) {
52 static ptrdiff_t rsp_save(unsigned i) {
54 asm volatile ("movq %%rsp, %0" : "=g" (state.rsp_save[i]));
55 return (char *) state.rsp_save[0] - (char *) state.rsp_save[i];
58 void altstack_rsp_save(void) {
62 ptrdiff_t altstack_used(void) {
66 int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out)
68 long pgsz = sysconf(_SC_PAGESIZE);
69 int ret = -1, undo = 0;
71 struct rlimit rl_save;
72 struct sigaction sa_save;
75 assert(max > 0 && fn);
76 #define ok(x, y) ({ long __r = (long) (x); if (__r == -1) { bang(#x); if (y) goto out; } __r; })
82 state.ebuf[state.elen = 0] = '\0';
85 // if the first page below the mapping is in use, we get max-pgsz usable bytes
86 // add pgsz to max to guarantee at least max usable bytes
89 ok(getrlimit(RLIMIT_STACK, &rl_save), 1);
90 ok(setrlimit(RLIMIT_STACK, &(struct rlimit) { state.max, rl_save.rlim_max }), 1);
93 ok(m = mmap(0, max, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_NORESERVE, -1, 0), 1);
96 if (setjmp(state.jmp) == 0) {
97 unsigned char sigstk[SIGSTKSZ];
98 stack_t ss = { .ss_sp = sigstk, .ss_size = sizeof(sigstk) };
99 struct sigaction sa = { .sa_handler = segvjmp, .sa_flags = SA_NODEFER|SA_RESETHAND|SA_ONSTACK };
101 ok(sigaltstack(&ss, 0), 1);
104 sigemptyset(&sa.sa_mask);
105 ok(sigaction(SIGSEGV, &sa, &sa_save), 1);
109 "mov %%rsp, %%r10\n\t"
113 : "=r" (state.rsp_save[0])
114 : "0" (m + max) : "r10", "memory");
115 state.out = state.fn(state.arg);
116 asm volatile ("pop %%rsp"
119 if (out) *out = state.out;
123 bang("SIGSEGV caught");
132 ok(sigaction(SIGSEGV, &sa_save, 0), 0);
134 ok(sigaltstack(&(stack_t) { .ss_flags = SS_DISABLE }, 0), 0);
136 ok(munmap(m, max), 0);
138 ok(setrlimit(RLIMIT_STACK, &rl_save), 0);
143 return !ret && state.elen ? 1 : ret;