1 #include "signalfd_ctx.h"
2 
3 #include <sys/types.h>
4 
5 #include <sys/event.h>
6 
7 #include <assert.h>
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <string.h>
11 
12 #include <fcntl.h>
13 #include <poll.h>
14 #include <unistd.h>
15 
16 static errno_t
signalfd_has_pending(SignalFDCtx const * signalfd,bool * has_pending,sigset_t * pending)17 signalfd_has_pending(SignalFDCtx const *signalfd, bool *has_pending,
18     sigset_t *pending)
19 {
20 	sigset_t pending_sigs;
21 
22 	if (sigpending(&pending_sigs) < 0 ||
23 	    sigandset(&pending_sigs, &pending_sigs, &signalfd->sigs) < 0) {
24 		return errno;
25 	}
26 
27 	*has_pending = !sigisemptyset(&pending_sigs);
28 	if (pending) {
29 		*pending = pending_sigs;
30 	}
31 	return 0;
32 }
33 
34 static errno_t
signalfd_ctx_trigger_manually(SignalFDCtx * signalfd)35 signalfd_ctx_trigger_manually(SignalFDCtx *signalfd)
36 {
37 	return kqueue_event_trigger(&signalfd->kqueue_event, signalfd->kq);
38 }
39 
40 static void
signalfd_signal_handler(int signo)41 signalfd_signal_handler(int signo)
42 {
43 	(void)signo;
44 }
45 
46 errno_t
signalfd_ctx_init(SignalFDCtx * signalfd,int kq,const sigset_t * sigs)47 signalfd_ctx_init(SignalFDCtx *signalfd, int kq, const sigset_t *sigs)
48 {
49 	errno_t ec;
50 
51 	assert(sigs != NULL);
52 
53 	*signalfd = (SignalFDCtx) { .kq = kq, .sigs = *sigs };
54 
55 #ifndef _SIG_MAXSIG
56 #define _SIG_MAXSIG (8 * sizeof(sigset_t))
57 #endif
58 
59 	struct kevent kevs[_SIG_MAXSIG + 2];
60 	int n = 0;
61 
62 	if ((ec = kqueue_event_init(&signalfd->kqueue_event, kevs, &n,
63 		 false)) != 0) {
64 		goto out2;
65 	}
66 
67 	for (int i = 1; i <= _SIG_MAXSIG; ++i) {
68 		if (sigismember(&signalfd->sigs, i)) {
69 			EV_SET(&kevs[n++], (unsigned int)i, EVFILT_SIGNAL,
70 			    EV_ADD, 0, 0, 0);
71 
72 			/*
73 			 * On Linux, signals with disposition SIG_DFL and a
74 			 * default action of "ignored" are returned from
75 			 * sigwait.
76 			 * We can emulate this by registering an empty signal
77 			 * handler.
78 			 */
79 			if (i == SIGCHLD || /**/
80 			    i == SIGURG ||  /**/
81 			    i == SIGCONT || /**/
82 #ifdef SIGIO
83 			    i == SIGIO || /**/
84 #endif
85 #ifdef SIGWINCH
86 			    i == SIGWINCH || /**/
87 #endif
88 #ifdef SIGINFO
89 			    i == SIGINFO || /**/
90 #endif
91 #ifdef SIGPWR
92 			    i == SIGPWR || /**/
93 #endif
94 #ifdef SIGTHR
95 			    i == SIGTHR || /**/
96 #endif
97 			    false) {
98 				struct sigaction sa;
99 
100 				if (sigaction(i, NULL, &sa) == 0 &&
101 				    !(sa.sa_flags & SA_SIGINFO) &&
102 				    sa.sa_handler == SIG_DFL) {
103 					sa.sa_flags |= SA_RESTART;
104 					sa.sa_handler = signalfd_signal_handler;
105 					(void)sigaction(i, &sa, NULL);
106 				}
107 			}
108 		}
109 	}
110 
111 	n = kevent(signalfd->kq, kevs, n, NULL, 0, NULL);
112 	if (n < 0) {
113 		ec = errno;
114 		goto out;
115 	}
116 
117 	bool has_pending;
118 	if ((ec = signalfd_has_pending(signalfd, &has_pending, NULL)) != 0) {
119 		goto out;
120 	}
121 	if (has_pending) {
122 		if ((ec = signalfd_ctx_trigger_manually(signalfd)) != 0) {
123 			goto out;
124 		}
125 	}
126 
127 	return 0;
128 
129 out:
130 	(void)kqueue_event_terminate(&signalfd->kqueue_event);
131 out2:
132 	return ec;
133 }
134 
135 errno_t
signalfd_ctx_terminate(SignalFDCtx * signalfd)136 signalfd_ctx_terminate(SignalFDCtx *signalfd)
137 {
138 	errno_t ec = 0;
139 	errno_t ec_local;
140 
141 	ec_local = kqueue_event_terminate(&signalfd->kqueue_event);
142 	ec = ec != 0 ? ec : ec_local;
143 
144 	return ec;
145 }
146 
147 static errno_t
signalfd_ctx_read_impl(SignalFDCtx * signalfd,SignalFDCtxSiginfo * signalfd_siginfo)148 signalfd_ctx_read_impl(SignalFDCtx *signalfd,
149     SignalFDCtxSiginfo *signalfd_siginfo)
150 {
151 	errno_t ec;
152 
153 	_Static_assert(sizeof(*signalfd_siginfo) == 128, "");
154 
155 	/*
156 	 * EVFILT_SIGNAL is an "observer". It does not hook into the
157 	 * signal disposition mechanism. On the other hand, `signalfd` does.
158 	 * Therefore, to properly emulate `signalfd`, `sigtimedwait` must be
159 	 * called.
160 	 */
161 
162 	siginfo_t siginfo;
163 	memset(&siginfo, 0, sizeof(siginfo));
164 
165 #if defined(__OpenBSD__)
166 	for (;;) {
167 		bool has_pending;
168 		sigset_t pending_sigs;
169 		if ((ec = signalfd_has_pending(signalfd, &has_pending,
170 			 &pending_sigs)) != 0) {
171 			return ec;
172 		}
173 		if (!has_pending) {
174 			return EAGAIN;
175 		}
176 
177 		/*
178 		 * sigwait does not behave nicely when multiple signals
179 		 * are pending (as of OpenBSD 6.8). So, only try to
180 		 * grab one.
181 		 */
182 		int signum = __builtin_ffsll((long long)pending_sigs);
183 		sigset_t mask = sigmask(signum);
184 
185 		extern int __thrsigdivert(sigset_t set, siginfo_t * info,
186 		    struct timespec const *timeout);
187 
188 		/* `&(struct timespec){0, 0}` returns EAGAIN but spams
189 		 * the dmesg log. Let's do it with an invalid timespec
190 		 * and EINVAL. */
191 		int s = __thrsigdivert(mask, NULL,
192 		    &(struct timespec) { 0, -1 });
193 		ec = s < 0 ? errno : 0;
194 		if (ec == EINVAL || ec == EAGAIN) {
195 			/* We must retry because we only checked for
196 			 * one signal. There may be others pending. */
197 			continue;
198 		}
199 		if (ec != 0) {
200 			break;
201 		}
202 
203 		siginfo.si_signo = s;
204 
205 		break;
206 	}
207 #else
208 	{
209 		int s = sigtimedwait(&signalfd->sigs, &siginfo,
210 		    &(struct timespec) { 0, 0 });
211 		ec = s < 0 ? errno : 0;
212 		if (ec == 0) {
213 			assert(siginfo.si_signo == s);
214 		}
215 	}
216 #endif
217 	if (ec != 0) {
218 		return ec;
219 	}
220 
221 	/*
222 	 * First, fill the POSIX compatible fields, then anything else OS
223 	 * specific we have.
224 	 */
225 
226 	signalfd_siginfo->ssi_signo = (uint32_t)siginfo.si_signo;
227 	signalfd_siginfo->ssi_code = siginfo.si_code;
228 	signalfd_siginfo->ssi_errno = siginfo.si_errno;
229 
230 	signalfd_siginfo->ssi_pid = (uint32_t)siginfo.si_pid;
231 	signalfd_siginfo->ssi_uid = siginfo.si_uid;
232 
233 	signalfd_siginfo->ssi_addr = (uint64_t)(uintptr_t)siginfo.si_addr;
234 
235 	signalfd_siginfo->ssi_status = siginfo.si_status;
236 
237 #ifndef __OpenBSD__
238 	signalfd_siginfo->ssi_band = (uint32_t)siginfo.si_band;
239 #endif
240 
241 	signalfd_siginfo->ssi_int = siginfo.si_value.sival_int;
242 	signalfd_siginfo->ssi_ptr = (uint64_t)(uintptr_t)
243 					siginfo.si_value.sival_ptr;
244 
245 	/*
246 	 * Sane defaults for Linux specific fields, may be amended in the
247 	 * future.
248 	 */
249 	signalfd_siginfo->ssi_fd = -1;
250 	signalfd_siginfo->ssi_tid = (uint32_t)-1;
251 
252 #ifdef __FreeBSD__
253 	signalfd_siginfo->ssi_trapno = (uint32_t)siginfo.si_trapno;
254 
255 	signalfd_siginfo->ssi_tid = (uint32_t)siginfo.si_timerid;
256 	signalfd_siginfo->ssi_overrun = (uint32_t)siginfo.si_overrun;
257 
258 	if (siginfo.si_code == SI_MESGQ) {
259 		/* Re-use this field for si_mqd. */
260 		signalfd_siginfo->ssi_fd = siginfo.si_mqd;
261 	}
262 #elif __NetBSD__
263 	signalfd_siginfo->ssi_utime = siginfo.si_utime;
264 	signalfd_siginfo->ssi_stime = siginfo.si_stime;
265 
266 	signalfd_siginfo->ssi_trapno = siginfo.si_trap;
267 	/* No space for trap2/trap3 */
268 
269 	signalfd_siginfo->ssi_fd = siginfo.si_fd;
270 
271 	/* No space for syscall/ptrace_state */
272 #endif
273 	return 0;
274 }
275 
276 static bool
signalfd_ctx_clear_signal(SignalFDCtx * signalfd,bool was_triggered)277 signalfd_ctx_clear_signal(SignalFDCtx *signalfd, bool was_triggered)
278 {
279 	if (was_triggered) {
280 		/*
281 		 * When there are other signals pending we can keep the kq
282 		 * readable and therefore don't need to clear it.
283 		 */
284 		bool has_pending;
285 		if (signalfd_has_pending(signalfd, &has_pending, NULL) != 0 ||
286 		    has_pending) {
287 			return true;
288 		}
289 	}
290 
291 	/*
292 	 * Clear the kq. Signals can arrive here, leading to a race.
293 	 */
294 
295 	kqueue_event_clear(&signalfd->kqueue_event, signalfd->kq);
296 
297 	/*
298 	 * Because of the race, we must recheck and manually trigger if
299 	 * necessary.
300 	 */
301 	bool has_pending;
302 	if (signalfd_has_pending(signalfd, &has_pending, NULL) != 0 ||
303 	    has_pending) {
304 		(void)signalfd_ctx_trigger_manually(signalfd);
305 		return true;
306 	}
307 	return false;
308 }
309 
310 errno_t
signalfd_ctx_read(SignalFDCtx * signalfd,SignalFDCtxSiginfo * siginfo)311 signalfd_ctx_read(SignalFDCtx *signalfd, SignalFDCtxSiginfo *siginfo)
312 {
313 	errno_t ec;
314 
315 	ec = signalfd_ctx_read_impl(signalfd, siginfo);
316 	if (ec == 0 || ec == EAGAIN || ec == EWOULDBLOCK) {
317 		(void)signalfd_ctx_clear_signal(signalfd, false);
318 	}
319 
320 	return ec;
321 }
322 
323 void
signalfd_ctx_poll(SignalFDCtx * signalfd,uint32_t * revents)324 signalfd_ctx_poll(SignalFDCtx *signalfd, uint32_t *revents)
325 {
326 	bool pending = signalfd_ctx_clear_signal(signalfd, revents != NULL);
327 	if (revents) {
328 		*revents = pending ? POLLIN : 0;
329 	}
330 }
331