]> git.ozlabs.org Git - ccan/blob - ccan/altstack/altstack.c
altstack: Consolidate thread-local variables
[ccan] / ccan / altstack / altstack.c
1 /* Licensed under Apache License v2.0 - see LICENSE file for details */
2 #include "config.h"
3 #include "altstack.h"
4
5 #include <assert.h>
6 #include <errno.h>
7 #include <setjmp.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <sys/mman.h>
13
14 static __thread struct altstack_state {
15         char ebuf[ALTSTACK_ERR_MAXLEN];
16         unsigned elen;
17         jmp_buf jmp;
18         void *rsp_save[2];
19         rlim_t max;
20         void *(*fn)(void *);
21         void *arg, *out;
22 } state;
23
24 #define bang(x)                                                         \
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), \
29                                 errno ? ": " : "",                      \
30                                 errno ? strerror(errno) : ""))
31
32 void altstack_perror(void)
33 {
34         fprintf(stderr, "%s\n", state.ebuf);
35 }
36
37 char *altstack_geterr(void)
38 {
39         return state.ebuf;
40 }
41
42 static void segvjmp(int signum)
43 {
44         longjmp(state.jmp, 1);
45 }
46
47
48 rlim_t altstack_max(void) {
49         return state.max;
50 }
51
52 static ptrdiff_t rsp_save(unsigned i) {
53         assert(i < 2);
54         asm volatile ("movq %%rsp, %0" : "=g" (state.rsp_save[i]));
55         return (char *) state.rsp_save[0] - (char *) state.rsp_save[i];
56 }
57
58 void altstack_rsp_save(void) {
59         rsp_save(0);
60 }
61
62 ptrdiff_t altstack_used(void) {
63         return rsp_save(1);
64 }
65
66 int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out)
67 {
68         long pgsz = sysconf(_SC_PAGESIZE);
69         int ret = -1, undo = 0;
70         char *m;
71         struct rlimit rl_save;
72         struct sigaction sa_save;
73         int errno_save;
74
75         assert(max > 0 && fn);
76         #define ok(x, y) ({ long __r = (long) (x); if (__r == -1) { bang(#x); if (y) goto out; } __r; })
77
78         state.fn  = fn;
79         state.arg = arg;
80         state.out = 0;
81         state.max = max;
82         state.ebuf[state.elen = 0] = '\0';
83         if (out) *out = 0;
84
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
87         max += pgsz;
88
89         ok(getrlimit(RLIMIT_STACK, &rl_save), 1);
90         ok(setrlimit(RLIMIT_STACK, &(struct rlimit) { state.max, rl_save.rlim_max }), 1);
91         undo++;
92
93         ok(m = mmap(0, max, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_NORESERVE, -1, 0), 1);
94         undo++;
95
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 };
100
101                 ok(sigaltstack(&ss, 0), 1);
102                 undo++;
103
104                 sigemptyset(&sa.sa_mask);
105                 ok(sigaction(SIGSEGV, &sa, &sa_save), 1);
106                 undo++;
107
108                 asm volatile (
109                         "mov %%rsp, %%r10\n\t"
110                         "mov %1, %%rsp\n\t"
111                         "sub $8, %%rsp\n\t"
112                         "push %%r10"
113                         : "=r" (state.rsp_save[0])
114                         : "0" (m + max) : "r10", "memory");
115                 state.out = state.fn(state.arg);
116                 asm volatile ("pop %%rsp"
117                               : : : "memory");
118                 ret = 0;
119                 if (out) *out = state.out;
120         }
121         else {
122                 errno = 0;
123                 bang("SIGSEGV caught");
124                 errno = EOVERFLOW;
125         }
126
127 out:
128         errno_save = errno;
129
130         switch (undo) {
131         case 4:
132                 ok(sigaction(SIGSEGV, &sa_save, 0), 0);
133         case 3:
134                 ok(sigaltstack(&(stack_t) { .ss_flags = SS_DISABLE }, 0), 0);
135         case 2:
136                 ok(munmap(m, max), 0);
137         case 1:
138                 ok(setrlimit(RLIMIT_STACK, &rl_save), 0);
139         }
140
141         if (errno_save)
142                 errno = errno_save;
143         return !ret && state.elen ? 1 : ret;
144 }