1 #include <assert.h>
2 #include <execinfo.h>
3 #include <signal.h>
4 #include <string.h>
5 #include <sys/mman.h>
6 #include <ucontext.h>
7 #include <unistd.h>
8
9 /*
10 * Various instructions that generate SIGILL and SIGSEGV. They could have been
11 * defined in a separate .s file, but this would complicate the build, so the
12 * inline asm is used instead.
13 */
14
15 #define DEFINE_ASM_FUNCTION(name, body) \
16 asm(".globl " #name "\n" \
17 #name ":\n" \
18 ".cfi_startproc\n" \
19 body "\n" \
20 "br %r14\n" \
21 ".cfi_endproc");
22
23 void illegal_op(void);
24 extern const char after_illegal_op;
25 DEFINE_ASM_FUNCTION(illegal_op,
26 ".byte 0x00,0x00\n"
27 ".globl after_illegal_op\n"
28 "after_illegal_op:")
29
30 void stg(void *dst, unsigned long src);
31 DEFINE_ASM_FUNCTION(stg, "stg %r3,0(%r2)")
32
33 void mvc_8(void *dst, void *src);
34 DEFINE_ASM_FUNCTION(mvc_8, "mvc 0(8,%r2),0(%r3)")
35
36 extern const char return_from_main_1;
37
safe_puts(const char * s)38 static void safe_puts(const char *s)
39 {
40 write(0, s, strlen(s));
41 write(0, "\n", 1);
42 }
43
44 enum exception {
45 exception_operation,
46 exception_translation,
47 exception_protection,
48 };
49
50 static struct {
51 int sig;
52 void *addr;
53 unsigned long psw_addr;
54 enum exception exception;
55 } expected;
56
handle_signal(int sig,siginfo_t * info,void * ucontext)57 static void handle_signal(int sig, siginfo_t *info, void *ucontext)
58 {
59 int err, i, n_frames;
60 void *frames[16];
61 void *page;
62
63 if (sig != expected.sig) {
64 safe_puts("[ FAILED ] wrong signal");
65 _exit(1);
66 }
67
68 if (info->si_addr != expected.addr) {
69 safe_puts("[ FAILED ] wrong si_addr");
70 _exit(1);
71 }
72
73 if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != expected.psw_addr) {
74 safe_puts("[ FAILED ] wrong psw.addr");
75 _exit(1);
76 }
77
78 switch (expected.exception) {
79 case exception_translation:
80 page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE,
81 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
82 if (page != expected.addr) {
83 safe_puts("[ FAILED ] mmap() failed");
84 _exit(1);
85 }
86 break;
87 case exception_protection:
88 err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE);
89 if (err != 0) {
90 safe_puts("[ FAILED ] mprotect() failed");
91 _exit(1);
92 }
93 break;
94 default:
95 break;
96 }
97
98 n_frames = backtrace(frames, sizeof(frames) / sizeof(frames[0]));
99 for (i = 0; i < n_frames; i++) {
100 if (frames[i] == &return_from_main_1) {
101 break;
102 }
103 }
104 if (i == n_frames) {
105 safe_puts("[ FAILED ] backtrace() is broken");
106 _exit(1);
107 }
108 }
109
check_sigsegv(void * func,enum exception exception,unsigned long val)110 static void check_sigsegv(void *func, enum exception exception,
111 unsigned long val)
112 {
113 int prot;
114 unsigned long *page;
115 unsigned long *addr;
116 int err;
117
118 prot = exception == exception_translation ? PROT_NONE : PROT_READ;
119 page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
120 assert(page != MAP_FAILED);
121 if (exception == exception_translation) {
122 /* Hopefully nothing will be mapped at this address. */
123 err = munmap(page, 4096);
124 assert(err == 0);
125 }
126 addr = page + (val & 0x1ff);
127
128 expected.sig = SIGSEGV;
129 expected.addr = page;
130 expected.psw_addr = (unsigned long)func;
131 expected.exception = exception;
132 if (func == stg) {
133 stg(addr, val);
134 } else {
135 assert(func == mvc_8);
136 mvc_8(addr, &val);
137 }
138 assert(*addr == val);
139
140 err = munmap(page, 4096);
141 assert(err == 0);
142 }
143
main_1(void)144 int main_1(void)
145 {
146 struct sigaction act;
147 int err;
148
149 memset(&act, 0, sizeof(act));
150 act.sa_sigaction = handle_signal;
151 act.sa_flags = SA_SIGINFO;
152 err = sigaction(SIGILL, &act, NULL);
153 assert(err == 0);
154 err = sigaction(SIGSEGV, &act, NULL);
155 assert(err == 0);
156
157 safe_puts("[ RUN ] Operation exception");
158 expected.sig = SIGILL;
159 expected.addr = illegal_op;
160 expected.psw_addr = (unsigned long)&after_illegal_op;
161 expected.exception = exception_operation;
162 illegal_op();
163 safe_puts("[ OK ]");
164
165 safe_puts("[ RUN ] Translation exception from stg");
166 check_sigsegv(stg, exception_translation, 42);
167 safe_puts("[ OK ]");
168
169 safe_puts("[ RUN ] Translation exception from mvc");
170 check_sigsegv(mvc_8, exception_translation, 4242);
171 safe_puts("[ OK ]");
172
173 safe_puts("[ RUN ] Protection exception from stg");
174 check_sigsegv(stg, exception_protection, 424242);
175 safe_puts("[ OK ]");
176
177 safe_puts("[ RUN ] Protection exception from mvc");
178 check_sigsegv(mvc_8, exception_protection, 42424242);
179 safe_puts("[ OK ]");
180
181 safe_puts("[ PASSED ]");
182
183 _exit(0);
184 }
185
186 /*
187 * Define main() in assembly in order to test that unwinding from signal
188 * handlers until main() works. This way we can define a specific point that
189 * the unwinder should reach. This is also better than defining main() in C
190 * and using inline assembly to call main_1(), since it's not easy to get all
191 * the clobbers right.
192 */
193
194 DEFINE_ASM_FUNCTION(main,
195 "stmg %r14,%r15,112(%r15)\n"
196 ".cfi_offset 14,-48\n"
197 ".cfi_offset 15,-40\n"
198 "lay %r15,-160(%r15)\n"
199 ".cfi_def_cfa_offset 320\n"
200 "brasl %r14,main_1\n"
201 ".globl return_from_main_1\n"
202 "return_from_main_1:\n"
203 "lmg %r14,%r15,272(%r15)\n"
204 ".cfi_restore 15\n"
205 ".cfi_restore 14\n"
206 ".cfi_def_cfa_offset 160");
207