1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3  * An implementation of initjmp() in C, that plays well with the system's
4  * setjmp() and longjmp() functions.
5  * Taken verbatim from arch/sandbox/os/setjmp.c in the barebox project.
6  * Modified so that initjmp() accepts a stack_size argument.
7  *
8  * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
9  * Copyright (C) 2011  Kevin Wolf <kwolf@redhat.com>
10  * Copyright (C) 2012  Alex Barcelo <abarcelo@ac.upc.edu>
11  * Copyright (C) 2021  Ahmad Fatoum, Pengutronix
12  * Copyright (C) 2025  Linaro Ltd.
13  * This file is partly based on pth_mctx.c, from the GNU Portable Threads
14  *  Copyright (c) 1999-2006 Ralf S. Engelschall <rse@engelschall.com>
15  */
16 
17 /* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
18 #ifdef _FORTIFY_SOURCE
19 #undef _FORTIFY_SOURCE
20 #endif
21 
22 #include <pthread.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <setjmp.h>
26 #include <signal.h>
27 
28 typedef sigjmp_buf _jmp_buf __attribute__((aligned((16))));
29 _Static_assert(sizeof(_jmp_buf) <= 512, "sigjmp_buf size exceeds expectation");
30 
31 /*
32  * Information for the signal handler (trampoline)
33  */
34 static struct {
35 	_jmp_buf *reenter;
36 	void (*entry)(void);
37 	volatile sig_atomic_t called;
38 } tr_state;
39 
40 /*
41  * "boot" function
42  * This is what starts the coroutine, is called from the trampoline
43  * (from the signal handler when it is not signal handling, read ahead
44  * for more information).
45  */
46 static void __attribute__((noinline, noreturn))
coroutine_bootstrap(void (* entry)(void))47 coroutine_bootstrap(void (*entry)(void))
48 {
49 	for (;;)
50 		entry();
51 }
52 
53 /*
54  * This is used as the signal handler. This is called with the brand new stack
55  * (thanks to sigaltstack). We have to return, given that this is a signal
56  * handler and the sigmask and some other things are changed.
57  */
coroutine_trampoline(int signal)58 static void coroutine_trampoline(int signal)
59 {
60 	/* Get the thread specific information */
61 	tr_state.called = 1;
62 
63 	/*
64 	 * Here we have to do a bit of a ping pong between the caller, given that
65 	 * this is a signal handler and we have to do a return "soon". Then the
66 	 * caller can reestablish everything and do a siglongjmp here again.
67 	 */
68 	if (!sigsetjmp(*tr_state.reenter, 0)) {
69 		return;
70 	}
71 
72 	/*
73 	 * Ok, the caller has siglongjmp'ed back to us, so now prepare
74 	 * us for the real machine state switching. We have to jump
75 	 * into another function here to get a new stack context for
76 	 * the auto variables (which have to be auto-variables
77 	 * because the start of the thread happens later). Else with
78 	 * PIC (i.e. Position Independent Code which is used when PTH
79 	 * is built as a shared library) most platforms would
80 	 * horrible core dump as experience showed.
81 	 */
82 	coroutine_bootstrap(tr_state.entry);
83 }
84 
initjmp(_jmp_buf jmp,void (* func)(void),void * stack_base,size_t stack_size)85 int __attribute__((weak)) initjmp(_jmp_buf jmp, void (*func)(void),
86 				  void *stack_base, size_t stack_size)
87 {
88 	struct sigaction sa;
89 	struct sigaction osa;
90 	stack_t ss;
91 	stack_t oss;
92 	sigset_t sigs;
93 	sigset_t osigs;
94 
95 	/* The way to manipulate stack is with the sigaltstack function. We
96 	 * prepare a stack, with it delivering a signal to ourselves and then
97 	 * put sigsetjmp/siglongjmp where needed.
98 	 * This has been done keeping coroutine-ucontext (from the QEMU project)
99 	 * as a model and with the pth ideas (GNU Portable Threads).
100 	 * See coroutine-ucontext for the basics of the coroutines and see
101 	 * pth_mctx.c (from the pth project) for the
102 	 * sigaltstack way of manipulating stacks.
103 	 */
104 
105 	tr_state.entry = func;
106 	tr_state.reenter = (void *)jmp;
107 
108 	/*
109 	 * Preserve the SIGUSR2 signal state, block SIGUSR2,
110 	 * and establish our signal handler. The signal will
111 	 * later transfer control onto the signal stack.
112 	 */
113 	sigemptyset(&sigs);
114 	sigaddset(&sigs, SIGUSR2);
115 	pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
116 	sa.sa_handler = coroutine_trampoline;
117 	sigfillset(&sa.sa_mask);
118 	sa.sa_flags = SA_ONSTACK;
119 	if (sigaction(SIGUSR2, &sa, &osa) != 0) {
120 		return -1;
121 	}
122 
123 	/*
124 	 * Set the new stack.
125 	 */
126 	ss.ss_sp = stack_base;
127 	ss.ss_size = stack_size;
128 	ss.ss_flags = 0;
129 	if (sigaltstack(&ss, &oss) < 0) {
130 		return -1;
131 	}
132 
133 	/*
134 	 * Now transfer control onto the signal stack and set it up.
135 	 * It will return immediately via "return" after the sigsetjmp()
136 	 * was performed. Be careful here with race conditions.  The
137 	 * signal can be delivered the first time sigsuspend() is
138 	 * called.
139 	 */
140 	tr_state.called = 0;
141 	pthread_kill(pthread_self(), SIGUSR2);
142 	sigfillset(&sigs);
143 	sigdelset(&sigs, SIGUSR2);
144 	while (!tr_state.called) {
145 		sigsuspend(&sigs);
146 	}
147 
148 	/*
149 	 * Inform the system that we are back off the signal stack by
150 	 * removing the alternative signal stack. Be careful here: It
151 	 * first has to be disabled, before it can be removed.
152 	 */
153 	sigaltstack(NULL, &ss);
154 	ss.ss_flags = SS_DISABLE;
155 	if (sigaltstack(&ss, NULL) < 0) {
156 		return -1;
157 	}
158 	sigaltstack(NULL, &ss);
159 	if (!(oss.ss_flags & SS_DISABLE)) {
160 		sigaltstack(&oss, NULL);
161 	}
162 
163 	/*
164 	 * Restore the old SIGUSR2 signal handler and mask
165 	 */
166 	sigaction(SIGUSR2, &osa, NULL);
167 	pthread_sigmask(SIG_SETMASK, &osigs, NULL);
168 
169 	/*
170 	 * jmp can now be used to enter the trampoline again, but not as a
171 	 * signal handler. Instead it's longjmp'd to directly.
172 	 */
173 	return 0;
174 }
175 
176