]> git.ozlabs.org Git - ccan/blob - ccan/altstack/altstack.c
altstack: New module
[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 <sys/mman.h>
12
13 static __thread char ebuf[ALTSTACK_ERR_MAXLEN];
14 static __thread unsigned elen;
15
16 #define bang(x)                                                 \
17         (elen += snprintf(ebuf + elen, sizeof(ebuf) - elen,     \
18                  "%s(altstack@%d) %s%s%s",                      \
19                  elen  ? "; " : "", __LINE__, (x),              \
20                  errno ? ": " : "", errno ? strerror(errno) : ""))
21
22 void altstack_perror(void)
23 {
24         fprintf(stderr, "%s\n", ebuf);
25 }
26
27 char *altstack_geterr(void)
28 {
29         return ebuf;
30 }
31
32 static __thread jmp_buf jmp;
33
34 static void segvjmp(int signum)
35 {
36         longjmp(jmp, 1);
37 }
38
39 static __thread void *rsp_save_[2];
40
41 static ptrdiff_t rsp_save(unsigned i) {
42         assert(i < 2);
43         asm volatile ("movq %%rsp, %0" : "=g" (rsp_save_[i]));
44         return (char *) rsp_save_[0] - (char *) rsp_save_[i];
45 }
46
47 void altstack_rsp_save(void) {
48         rsp_save(0);
49 }
50
51 ptrdiff_t altstack_used(void) {
52         return rsp_save(1);
53 }
54
55 static __thread void *(*fn_)(void *);
56 static __thread void *arg_, *out_;
57
58 int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out)
59 {
60         int ret = -1, undo = 0;
61         char *m;
62         struct rlimit rl_save;
63         struct sigaction sa_save;
64         int errno_save;
65
66         assert(max > 0 && fn);
67         #define ok(x, y) ({ long __r = (long) (x); if (__r == -1) { bang(#x); if (y) goto out; } __r; })
68
69         fn_  = fn;
70         arg_ = arg;
71         out_ = 0;
72         ebuf[elen = 0] = '\0';
73         if (out) *out = 0;
74
75         ok(getrlimit(RLIMIT_STACK, &rl_save), 1);
76         ok(setrlimit(RLIMIT_STACK, &(struct rlimit) { max, rl_save.rlim_max }), 1);
77         undo++;
78
79         ok(m = mmap(0, max, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_NORESERVE, -1, 0), 1);
80         undo++;
81
82         if (setjmp(jmp) == 0) {
83                 unsigned char sigstk[MINSIGSTKSZ];
84                 stack_t ss = { .ss_sp = sigstk, .ss_size = sizeof(sigstk) };
85                 struct sigaction sa = { .sa_handler = segvjmp, .sa_flags = SA_NODEFER|SA_RESETHAND|SA_ONSTACK };
86
87                 ok(sigaltstack(&ss, 0), 1);
88                 undo++;
89
90                 sigemptyset(&sa.sa_mask);
91                 ok(sigaction(SIGSEGV, &sa, &sa_save), 1);
92                 undo++;
93
94                 asm volatile ("movq %%rsp, %%r10\nmov %0, %%rsp\npush %%r10" : : "g" (m + max) : "r10");
95                 rsp_save(0);
96                 out_ = fn_(arg_);
97                 asm volatile ("pop %rsp");
98                 ret = 0;
99                 if (out) *out = out_;
100         }
101         else {
102                 errno = 0;
103                 bang("SIGSEGV caught");
104                 errno = EOVERFLOW;
105         }
106
107 out:
108         errno_save = errno;
109
110         switch (undo) {
111         case 4:
112                 ok(sigaction(SIGSEGV, &sa_save, 0), 0);
113         case 3:
114                 ok(sigaltstack(&(stack_t) { .ss_flags = SS_DISABLE }, 0), 0);
115         case 2:
116                 ok(munmap(m, max), 0);
117         case 1:
118                 ok(setrlimit(RLIMIT_STACK, &rl_save), 0);
119         }
120
121         if (errno_save)
122                 errno = errno_save;
123         return !ret && elen ? 1 : ret;
124 }