Improve robustness of sigprocmask by overriding signal.
[gnulib.git] / lib / sigprocmask.c
1 /* POSIX compatible signal blocking.
2    Copyright (C) 2006-2008 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2006.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <signal.h>
22
23 #include <errno.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26
27 /* We assume that a platform without POSIX signal blocking functions
28    also does not have the POSIX sigaction() function, only the
29    signal() function.  We also assume signal() has SysV semantics,
30    where any handler is uninstalled prior to being invoked.  This is
31    true for Woe32 platforms.  */
32
33 /* We use raw signal(), but also provide a wrapper rpl_signal() so
34    that applications can query or change a blocked signal.  */
35 #undef signal
36
37 /* Provide invalid signal numbers as fallbacks if the uncatchable
38    signals are not defined.  */
39 #ifndef SIGKILL
40 # define SIGKILL (-1)
41 #endif
42 #ifndef SIGSTOP
43 # define SIGSTOP (-1)
44 #endif
45
46 /* A signal handler.  */
47 typedef void (*handler_t) (int signal);
48
49 int
50 sigismember (const sigset_t *set, int sig)
51 {
52   if (sig >= 0 && sig < NSIG)
53     return (*set >> sig) & 1;
54   else
55     return 0;
56 }
57
58 int
59 sigemptyset (sigset_t *set)
60 {
61   *set = 0;
62   return 0;
63 }
64
65 int
66 sigaddset (sigset_t *set, int sig)
67 {
68   if (sig >= 0 && sig < NSIG)
69     {
70       *set |= 1U << sig;
71       return 0;
72     }
73   else
74     {
75       errno = EINVAL;
76       return -1;
77     }
78 }
79
80 int
81 sigdelset (sigset_t *set, int sig)
82 {
83   if (sig >= 0 && sig < NSIG)
84     {
85       *set &= ~(1U << sig);
86       return 0;
87     }
88   else
89     {
90       errno = EINVAL;
91       return -1;
92     }
93 }
94
95 int
96 sigfillset (sigset_t *set)
97 {
98   *set = (2U << (NSIG - 1)) - 1;
99   return 0;
100 }
101
102 /* Set of currently blocked signals.  */
103 static volatile sigset_t blocked_set /* = 0 */;
104
105 /* Set of currently blocked and pending signals.  */
106 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
107
108 /* Signal handler that is installed for blocked signals.  */
109 static void
110 blocked_handler (int sig)
111 {
112   /* Reinstall the handler, in case the signal occurs multiple times
113      while blocked.  There is an inherent race where an asynchronous
114      signal in between when the kernel uninstalled the handler and
115      when we reinstall it will trigger the default handler; oh
116      well.  */
117   signal (sig, blocked_handler);
118   if (sig >= 0 && sig < NSIG)
119     pending_array[sig] = 1;
120 }
121
122 int
123 sigpending (sigset_t *set)
124 {
125   sigset_t pending = 0;
126   int sig;
127
128   for (sig = 0; sig < NSIG; sig++)
129     if (pending_array[sig])
130       pending |= 1U << sig;
131   *set = pending;
132   return 0;
133 }
134
135 /* The previous signal handlers.
136    Only the array elements corresponding to blocked signals are relevant.  */
137 static volatile handler_t old_handlers[NSIG];
138
139 int
140 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
141 {
142   if (old_set != NULL)
143     *old_set = blocked_set;
144
145   if (set != NULL)
146     {
147       sigset_t new_blocked_set;
148       sigset_t to_unblock;
149       sigset_t to_block;
150
151       switch (operation)
152         {
153         case SIG_BLOCK:
154           new_blocked_set = blocked_set | *set;
155           break;
156         case SIG_SETMASK:
157           new_blocked_set = *set;
158           break;
159         case SIG_UNBLOCK:
160           new_blocked_set = blocked_set & ~*set;
161           break;
162         default:
163           errno = EINVAL;
164           return -1;
165         }
166       to_unblock = blocked_set & ~new_blocked_set;
167       to_block = new_blocked_set & ~blocked_set;
168
169       if (to_block != 0)
170         {
171           int sig;
172
173           for (sig = 0; sig < NSIG; sig++)
174             if ((to_block >> sig) & 1)
175               {
176                 pending_array[sig] = 0;
177                 if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
178                   blocked_set |= 1U << sig;
179               }
180         }
181
182       if (to_unblock != 0)
183         {
184           sig_atomic_t received[NSIG];
185           int sig;
186
187           for (sig = 0; sig < NSIG; sig++)
188             if ((to_unblock >> sig) & 1)
189               {
190                 if (signal (sig, old_handlers[sig]) != blocked_handler)
191                   /* The application changed a signal handler while the signal
192                      was blocked.  We don't support this.  */
193                   abort ();
194                 received[sig] = pending_array[sig];
195                 blocked_set &= ~(1U << sig);
196                 pending_array[sig] = 0;
197               }
198             else
199               received[sig] = 0;
200
201           for (sig = 0; sig < NSIG; sig++)
202             if (received[sig])
203               raise (sig);
204         }
205     }
206   return 0;
207 }
208
209 /* Install the handler FUNC for signal SIG, and return the previous
210    handler.  */
211 handler_t
212 rpl_signal (int sig, handler_t handler)
213 {
214   /* We must provide a wrapper, so that a user can query what handler
215      they installed even if that signal is currently blocked.  */
216   if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
217       && handler != SIG_ERR)
218     {
219       if (blocked_set & (1U << sig))
220         {
221           /* POSIX states that sigprocmask and signal are both
222              async-signal-safe.  This is not true of our
223              implementation - there is a slight data race where an
224              asynchronous interrupt on signal A can occur after we
225              install blocked_handler but before we have updated
226              old_handlers for signal B, such that handler A can see
227              stale information if it calls signal(B).  Oh well -
228              signal handlers really shouldn't try to manipulate the
229              installed handlers of unrelated signals.  */
230           handler_t result = old_handlers[sig];
231           old_handlers[sig] = handler;
232           return result;
233         }
234       return signal (sig, handler);
235     }
236   else
237     {
238       errno = EINVAL;
239       return SIG_ERR;
240     }
241 }