]> git.ozlabs.org Git - ccan/blob - ccan/altstack/altstack.c
base64: fix for unsigned chars (e.g. ARM).
[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         stack_t ss_save;
75
76         assert(max > 0 && fn);
77         #define ok(x, y) ({ long __r = (long) (x); if (__r == -1) { bang(#x); if (y) goto out; } __r; })
78
79         state.fn  = fn;
80         state.arg = arg;
81         state.out = NULL;
82         state.max = max;
83         state.ebuf[state.elen = 0] = '\0';
84         if (out) *out = NULL;
85
86         // if the first page below the mapping is in use, we get max-pgsz usable bytes
87         // add pgsz to max to guarantee at least max usable bytes
88         max += pgsz;
89
90         ok(getrlimit(RLIMIT_STACK, &rl_save), 1);
91         ok(setrlimit(RLIMIT_STACK, &(struct rlimit) { state.max, rl_save.rlim_max }), 1);
92         undo++;
93
94         ok(m = mmap(NULL, max, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_NORESERVE, -1, 0), 1);
95         undo++;
96
97         if (setjmp(state.jmp) == 0) {
98                 unsigned char sigstk[SIGSTKSZ];
99                 stack_t ss = { .ss_sp = sigstk, .ss_size = sizeof(sigstk) };
100                 struct sigaction sa = { .sa_handler = segvjmp, .sa_flags = SA_NODEFER|SA_RESETHAND|SA_ONSTACK };
101
102                 ok(sigaltstack(&ss, &ss_save), 1);
103                 undo++;
104
105                 sigemptyset(&sa.sa_mask);
106                 ok(sigaction(SIGSEGV, &sa, &sa_save), 1);
107                 undo++;
108
109                 asm volatile (
110                         "mov %%rsp, %%r10\n\t"
111                         "mov %1, %%rsp\n\t"
112                         "sub $8, %%rsp\n\t"
113                         "push %%r10"
114                         : "=r" (state.rsp_save[0])
115                         : "0" (m + max) : "r10", "memory");
116                 state.out = state.fn(state.arg);
117                 asm volatile ("pop %%rsp"
118                               : : : "memory");
119                 ret = 0;
120                 if (out) *out = state.out;
121         }
122         else {
123                 errno = 0;
124                 bang("SIGSEGV caught");
125                 errno = EOVERFLOW;
126         }
127
128 out:
129         errno_save = errno;
130
131         switch (undo) {
132         case 4:
133                 ok(sigaction(SIGSEGV, &sa_save, NULL), 0);
134         case 3:
135                 ok(sigaltstack(&ss_save, NULL), 0);
136         case 2:
137                 ok(munmap(m, max), 0);
138         case 1:
139                 ok(setrlimit(RLIMIT_STACK, &rl_save), 0);
140         }
141
142         if (errno_save)
143                 errno = errno_save;
144         return !ret && state.elen ? 1 : ret;
145 }