]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/sigbus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "memory-util.h"
10 #include "missing_syscall.h"
11 #include "process-util.h"
14 #define SIGBUS_QUEUE_MAX 64
16 static struct sigaction old_sigaction
;
17 static unsigned n_installed
= 0;
19 /* We maintain a fixed size list of page addresses that triggered a
20 SIGBUS. We access with list with atomic operations, so that we
21 don't have to deal with locks between signal handler and main
22 programs in possibly multiple threads. */
24 static void* volatile sigbus_queue
[SIGBUS_QUEUE_MAX
];
25 static volatile sig_atomic_t n_sigbus_queue
= 0;
27 static void sigbus_push(void *addr
) {
30 /* Find a free place, increase the number of entries and leave, if we can */
31 for (size_t u
= 0; u
< SIGBUS_QUEUE_MAX
; u
++) {
32 /* OK to initialize this here since we haven't started the atomic ops yet */
34 if (__atomic_compare_exchange_n(&sigbus_queue
[u
], &tmp
, addr
, false,
35 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
)) {
36 __atomic_fetch_add(&n_sigbus_queue
, 1, __ATOMIC_SEQ_CST
);
41 /* If we can't, make sure the queue size is out of bounds, to
42 * mark it as overflow */
46 __atomic_thread_fence(__ATOMIC_SEQ_CST
);
49 if (c
> SIGBUS_QUEUE_MAX
) /* already overflow */
52 /* OK if we clobber c here, since we either immediately return
53 * or it will be immediately reinitialized on next loop */
54 if (__atomic_compare_exchange_n(&n_sigbus_queue
, &c
, c
+ SIGBUS_QUEUE_MAX
, false,
55 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
))
60 int sigbus_pop(void **ret
) {
66 __atomic_thread_fence(__ATOMIC_SEQ_CST
);
72 if (_unlikely_(c
>= SIGBUS_QUEUE_MAX
))
75 for (u
= 0; u
< SIGBUS_QUEUE_MAX
; u
++) {
78 addr
= sigbus_queue
[u
];
82 /* OK if we clobber addr here, since we either immediately return
83 * or it will be immediately reinitialized on next loop */
84 if (__atomic_compare_exchange_n(&sigbus_queue
[u
], &addr
, NULL
, false,
85 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
)) {
86 __atomic_fetch_sub(&n_sigbus_queue
, 1, __ATOMIC_SEQ_CST
);
87 /* If we successfully entered this if condition, addr won't
88 * have been modified since its assignment, so safe to use it */
96 static void sigbus_handler(int sn
, siginfo_t
*si
, void *data
) {
100 assert(sn
== SIGBUS
);
103 if (si
->si_code
!= BUS_ADRERR
|| !si
->si_addr
) {
104 assert_se(sigaction(SIGBUS
, &old_sigaction
, NULL
) == 0);
105 rt_sigqueueinfo(getpid_cached(), SIGBUS
, si
);
109 ul
= (unsigned long) si
->si_addr
;
110 ul
= ul
/ page_size();
111 ul
= ul
* page_size();
112 aligned
= (void*) ul
;
114 /* Let's remember which address failed */
115 sigbus_push(aligned
);
117 /* Replace mapping with an anonymous page, so that the
118 * execution can continue, however with a zeroed out page */
119 assert_se(mmap(aligned
, page_size(), PROT_READ
|PROT_WRITE
, MAP_PRIVATE
|MAP_ANONYMOUS
|MAP_FIXED
, -1, 0) == aligned
);
122 void sigbus_install(void) {
123 struct sigaction sa
= {
124 .sa_sigaction
= sigbus_handler
,
125 .sa_flags
= SA_SIGINFO
,
128 /* make sure that sysconf() is not called from a signal handler because
129 * it is not guaranteed to be async-signal-safe since POSIX.1-2008 */
134 if (n_installed
== 1)
135 assert_se(sigaction(SIGBUS
, &sa
, &old_sigaction
) == 0);
140 void sigbus_reset(void) {
142 if (n_installed
<= 0)
147 if (n_installed
== 0)
148 assert_se(sigaction(SIGBUS
, &old_sigaction
, NULL
) == 0);