1 | /* Copyright (C) 2001-2022 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <errno.h> |
19 | #include <signal.h> |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | #include <ucontext.h> |
24 | #include <unistd.h> |
25 | |
26 | static ucontext_t ctx[3]; |
27 | |
28 | static int was_in_f1; |
29 | static int was_in_f2; |
30 | |
31 | static char st2[32768]; |
32 | |
33 | static void |
34 | f1 (int a0, int a1, int a2, int a3) |
35 | { |
36 | printf (format: "start f1(a0=%x,a1=%x,a2=%x,a3=%x)\n" , a0, a1, a2, a3); |
37 | |
38 | if (a0 != 1 || a1 != 2 || a2 != 3 || a3 != -4) |
39 | { |
40 | puts (s: "arg mismatch" ); |
41 | exit (-1); |
42 | } |
43 | |
44 | if (swapcontext (oucp: &ctx[1], ucp: &ctx[2]) != 0) |
45 | { |
46 | printf (format: "%s: swapcontext: %m\n" , __FUNCTION__); |
47 | exit (1); |
48 | } |
49 | puts (s: "finish f1" ); |
50 | was_in_f1 = 1; |
51 | } |
52 | |
53 | static void |
54 | f2 (void) |
55 | { |
56 | char on_stack[1]; |
57 | |
58 | puts (s: "start f2" ); |
59 | |
60 | printf (format: "&on_stack=%p\n" , on_stack); |
61 | if (&on_stack[0] < &st2[0] || &on_stack[0] >= st2 + sizeof (st2)) |
62 | { |
63 | printf (format: "%s: memory stack is not where it belongs!" , __FUNCTION__); |
64 | exit (1); |
65 | } |
66 | |
67 | if (swapcontext (oucp: &ctx[2], ucp: &ctx[1]) != 0) |
68 | { |
69 | printf (format: "%s: swapcontext: %m\n" , __FUNCTION__); |
70 | exit (1); |
71 | } |
72 | puts (s: "finish f2" ); |
73 | was_in_f2 = 1; |
74 | } |
75 | |
76 | void |
77 | test_stack (volatile int a, volatile int b, |
78 | volatile int c, volatile int d) |
79 | { |
80 | volatile int e = 5; |
81 | volatile int f = 6; |
82 | ucontext_t uc; |
83 | |
84 | /* Test for cases where getcontext is clobbering the callers |
85 | stack, including parameters. */ |
86 | getcontext (ucp: &uc); |
87 | |
88 | if (a != 1) |
89 | { |
90 | printf (format: "%s: getcontext clobbers parm a\n" , __FUNCTION__); |
91 | exit (1); |
92 | } |
93 | |
94 | if (b != 2) |
95 | { |
96 | printf (format: "%s: getcontext clobbers parm b\n" , __FUNCTION__); |
97 | exit (1); |
98 | } |
99 | |
100 | if (c != 3) |
101 | { |
102 | printf (format: "%s: getcontext clobbers parm c\n" , __FUNCTION__); |
103 | exit (1); |
104 | } |
105 | |
106 | if (d != 4) |
107 | { |
108 | printf (format: "%s: getcontext clobbers parm d\n" , __FUNCTION__); |
109 | exit (1); |
110 | } |
111 | |
112 | if (e != 5) |
113 | { |
114 | printf (format: "%s: getcontext clobbers varible e\n" , __FUNCTION__); |
115 | exit (1); |
116 | } |
117 | |
118 | if (f != 6) |
119 | { |
120 | printf (format: "%s: getcontext clobbers variable f\n" , __FUNCTION__); |
121 | exit (1); |
122 | } |
123 | } |
124 | |
125 | volatile int global; |
126 | |
127 | |
128 | static int back_in_main; |
129 | |
130 | |
131 | static void |
132 | check_called (void) |
133 | { |
134 | if (back_in_main == 0) |
135 | { |
136 | puts (s: "program did not reach main again" ); |
137 | _exit (1); |
138 | } |
139 | } |
140 | |
141 | |
142 | int |
143 | main (void) |
144 | { |
145 | atexit (func: check_called); |
146 | |
147 | char st1[32768]; |
148 | stack_t stack_before, stack_after; |
149 | |
150 | sigaltstack (NULL, oss: &stack_before); |
151 | |
152 | puts (s: "making contexts" ); |
153 | if (getcontext (ucp: &ctx[1]) != 0) |
154 | { |
155 | if (errno == ENOSYS) |
156 | { |
157 | back_in_main = 1; |
158 | exit (0); |
159 | } |
160 | |
161 | printf (format: "%s: getcontext: %m\n" , __FUNCTION__); |
162 | exit (1); |
163 | } |
164 | |
165 | test_stack (a: 1, b: 2, c: 3, d: 4); |
166 | |
167 | /* Play some tricks with this context. */ |
168 | if (++global == 1) |
169 | if (setcontext (&ctx[1]) != 0) |
170 | { |
171 | printf (format: "%s: setcontext: %m\n" , __FUNCTION__); |
172 | exit (1); |
173 | } |
174 | if (global != 2) |
175 | { |
176 | printf (format: "%s: 'global' not incremented twice\n" , __FUNCTION__); |
177 | exit (1); |
178 | } |
179 | |
180 | ctx[1].uc_stack.ss_sp = st1; |
181 | ctx[1].uc_stack.ss_size = sizeof st1; |
182 | ctx[1].uc_link = &ctx[0]; |
183 | { |
184 | ucontext_t tempctx = ctx[1]; |
185 | makecontext (ucp: &ctx[1], func: (void (*) (void)) f1, argc: 4, 1, 2, 3, -4); |
186 | |
187 | /* Without this check, a stub makecontext can make us spin forever. */ |
188 | if (memcmp (&tempctx, &ctx[1], sizeof ctx[1]) == 0) |
189 | { |
190 | puts (s: "makecontext was a no-op, presuming not implemented" ); |
191 | return 0; |
192 | } |
193 | } |
194 | |
195 | if (getcontext (ucp: &ctx[2]) != 0) |
196 | { |
197 | printf (format: "%s: second getcontext: %m\n" , __FUNCTION__); |
198 | exit (1); |
199 | } |
200 | ctx[2].uc_stack.ss_sp = st2; |
201 | ctx[2].uc_stack.ss_size = sizeof st2; |
202 | ctx[2].uc_link = &ctx[1]; |
203 | makecontext (ucp: &ctx[2], func: f2, argc: 0); |
204 | |
205 | puts (s: "swapping contexts" ); |
206 | if (swapcontext (oucp: &ctx[0], ucp: &ctx[2]) != 0) |
207 | { |
208 | printf (format: "%s: swapcontext: %m\n" , __FUNCTION__); |
209 | exit (1); |
210 | } |
211 | puts (s: "back at main program" ); |
212 | back_in_main = 1; |
213 | |
214 | sigaltstack (NULL, oss: &stack_after); |
215 | |
216 | if (was_in_f1 == 0) |
217 | { |
218 | puts (s: "didn't reach f1" ); |
219 | exit (1); |
220 | } |
221 | if (was_in_f2 == 0) |
222 | { |
223 | puts (s: "didn't reach f2" ); |
224 | exit (1); |
225 | } |
226 | |
227 | /* Check sigaltstack state is not clobbered as in BZ #16629. */ |
228 | if (stack_before.ss_sp != stack_after.ss_sp) |
229 | { |
230 | printf (format: "stack ss_sp mismatch: %p %p\n" , |
231 | stack_before.ss_sp, stack_after.ss_sp); |
232 | exit (1); |
233 | } |
234 | |
235 | if (stack_before.ss_size != stack_after.ss_size) |
236 | { |
237 | printf (format: "stack ss_size mismatch: %zd %zd\n" , |
238 | stack_before.ss_size, stack_after.ss_size); |
239 | exit (1); |
240 | } |
241 | |
242 | puts (s: "test succeeded" ); |
243 | return 0; |
244 | } |
245 | |