]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/sigbus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "memory-util.h"
8 #include "signal-util.h"
10 #define SIGBUS_QUEUE_MAX 64
12 static struct sigaction old_sigaction
;
13 static unsigned n_installed
= 0;
15 /* We maintain a fixed size list of page addresses that triggered a
16 SIGBUS. We access with list with atomic operations, so that we
17 don't have to deal with locks between signal handler and main
18 programs in possibly multiple threads. */
20 static void* volatile sigbus_queue
[SIGBUS_QUEUE_MAX
];
21 static volatile sig_atomic_t n_sigbus_queue
= 0;
23 static void sigbus_push(void *addr
) {
26 /* Find a free place, increase the number of entries and leave, if we can */
27 FOREACH_ELEMENT(u
, sigbus_queue
) {
28 /* OK to initialize this here since we haven't started the atomic ops yet */
30 if (__atomic_compare_exchange_n(u
, &tmp
, addr
, false,
31 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
)) {
32 __atomic_fetch_add(&n_sigbus_queue
, 1, __ATOMIC_SEQ_CST
);
37 /* If we can't, make sure the queue size is out of bounds, to
38 * mark it as overflowed */
42 __atomic_thread_fence(__ATOMIC_SEQ_CST
);
45 if (c
> SIGBUS_QUEUE_MAX
) /* already overflowed */
48 /* OK if we clobber c here, since we either immediately return
49 * or it will be immediately reinitialized on next loop */
50 if (__atomic_compare_exchange_n(&n_sigbus_queue
, &c
, c
+ SIGBUS_QUEUE_MAX
, false,
51 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
))
56 int sigbus_pop(void **ret
) {
62 __atomic_thread_fence(__ATOMIC_SEQ_CST
);
68 if (_unlikely_(c
> SIGBUS_QUEUE_MAX
))
71 for (u
= 0; u
< SIGBUS_QUEUE_MAX
; u
++) {
74 addr
= sigbus_queue
[u
];
78 /* OK if we clobber addr here, since we either immediately return
79 * or it will be immediately reinitialized on next loop */
80 if (__atomic_compare_exchange_n(&sigbus_queue
[u
], &addr
, NULL
, false,
81 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
)) {
82 __atomic_fetch_sub(&n_sigbus_queue
, 1, __ATOMIC_SEQ_CST
);
83 /* If we successfully entered this if condition, addr won't
84 * have been modified since its assignment, so safe to use it */
92 static void sigbus_handler(int sn
, siginfo_t
*si
, void *data
) {
99 if (si
->si_code
!= BUS_ADRERR
|| !si
->si_addr
) {
100 assert_se(sigaction(SIGBUS
, &old_sigaction
, NULL
) >= 0);
101 propagate_signal(sn
, si
);
105 ul
= (unsigned long) si
->si_addr
;
106 ul
= ul
/ page_size();
107 ul
= ul
* page_size();
108 aligned
= (void*) ul
;
110 /* Let's remember which address failed */
111 sigbus_push(aligned
);
113 /* Replace mapping with an anonymous page, so that the
114 * execution can continue, however with a zeroed out page */
115 assert_se(mmap(aligned
, page_size(), PROT_READ
|PROT_WRITE
, MAP_PRIVATE
|MAP_ANONYMOUS
|MAP_FIXED
, -1, 0) == aligned
);
118 void sigbus_install(void) {
119 static const struct sigaction sa
= {
120 .sa_sigaction
= sigbus_handler
,
121 .sa_flags
= SA_SIGINFO
,
124 /* make sure that sysconf() is not called from a signal handler because
125 * it is not guaranteed to be async-signal-safe since POSIX.1-2008 */
130 if (n_installed
== 1)
131 assert_se(sigaction(SIGBUS
, &sa
, &old_sigaction
) >= 0);
136 void sigbus_reset(void) {
138 if (n_installed
<= 0)
143 if (n_installed
== 0)
144 assert_se(sigaction(SIGBUS
, &old_sigaction
, NULL
) >= 0);