LCOV - code coverage report
Current view: top level - basic - sigbus.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 44 57 77.2 %
Date: 2019-08-22 15:41:25 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <signal.h>
       5             : #include <stddef.h>
       6             : #include <sys/mman.h>
       7             : 
       8             : #include "macro.h"
       9             : #include "memory-util.h"
      10             : #include "sigbus.h"
      11             : 
      12             : #define SIGBUS_QUEUE_MAX 64
      13             : 
      14             : static struct sigaction old_sigaction;
      15             : static unsigned n_installed = 0;
      16             : 
      17             : /* We maintain a fixed size list of page addresses that triggered a
      18             :    SIGBUS. We access with list with atomic operations, so that we
      19             :    don't have to deal with locks between signal handler and main
      20             :    programs in possibly multiple threads. */
      21             : 
      22             : static void* volatile sigbus_queue[SIGBUS_QUEUE_MAX];
      23             : static volatile sig_atomic_t n_sigbus_queue = 0;
      24             : 
      25           2 : static void sigbus_push(void *addr) {
      26             :         unsigned u;
      27             : 
      28           2 :         assert(addr);
      29             : 
      30             :         /* Find a free place, increase the number of entries and leave, if we can */
      31           3 :         for (u = 0; u < SIGBUS_QUEUE_MAX; u++)
      32           3 :                 if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) {
      33           2 :                         __sync_fetch_and_add(&n_sigbus_queue, 1);
      34           2 :                         return;
      35             :                 }
      36             : 
      37             :         /* If we can't, make sure the queue size is out of bounds, to
      38             :          * mark it as overflow */
      39           0 :         for (;;) {
      40             :                 unsigned c;
      41             : 
      42           0 :                 __sync_synchronize();
      43           0 :                 c = n_sigbus_queue;
      44             : 
      45           0 :                 if (c > SIGBUS_QUEUE_MAX) /* already overflow */
      46           0 :                         return;
      47             : 
      48           0 :                 if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX))
      49           0 :                         return;
      50             :         }
      51             : }
      52             : 
      53      138218 : int sigbus_pop(void **ret) {
      54      138218 :         assert(ret);
      55             : 
      56           0 :         for (;;) {
      57             :                 unsigned u, c;
      58             : 
      59      138218 :                 __sync_synchronize();
      60      138218 :                 c = n_sigbus_queue;
      61             : 
      62      138218 :                 if (_likely_(c == 0))
      63      138216 :                         return 0;
      64             : 
      65           2 :                 if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
      66           0 :                         return -EOVERFLOW;
      67             : 
      68           3 :                 for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
      69             :                         void *addr;
      70             : 
      71           3 :                         addr = sigbus_queue[u];
      72           3 :                         if (!addr)
      73           1 :                                 continue;
      74             : 
      75           2 :                         if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) {
      76           2 :                                 __sync_fetch_and_sub(&n_sigbus_queue, 1);
      77           2 :                                 *ret = addr;
      78           2 :                                 return 1;
      79             :                         }
      80             :                 }
      81             :         }
      82             : }
      83             : 
      84           2 : static void sigbus_handler(int sn, siginfo_t *si, void *data) {
      85             :         unsigned long ul;
      86             :         void *aligned;
      87             : 
      88           2 :         assert(sn == SIGBUS);
      89           2 :         assert(si);
      90             : 
      91           2 :         if (si->si_code != BUS_ADRERR || !si->si_addr) {
      92           0 :                 assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
      93           0 :                 raise(SIGBUS);
      94           0 :                 return;
      95             :         }
      96             : 
      97           2 :         ul = (unsigned long) si->si_addr;
      98           2 :         ul = ul / page_size();
      99           2 :         ul = ul * page_size();
     100           2 :         aligned = (void*) ul;
     101             : 
     102             :         /* Let's remember which address failed */
     103           2 :         sigbus_push(aligned);
     104             : 
     105             :         /* Replace mapping with an anonymous page, so that the
     106             :          * execution can continue, however with a zeroed out page */
     107           2 :         assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned);
     108             : }
     109             : 
     110          13 : void sigbus_install(void) {
     111          13 :         struct sigaction sa = {
     112             :                 .sa_sigaction = sigbus_handler,
     113             :                 .sa_flags = SA_SIGINFO,
     114             :         };
     115             : 
     116             :         /* make sure that sysconf() is not called from a signal handler because
     117             :         * it is not guaranteed to be async-signal-safe since POSIX.1-2008 */
     118          13 :         (void) page_size();
     119             : 
     120          13 :         n_installed++;
     121             : 
     122          13 :         if (n_installed == 1)
     123          13 :                 assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0);
     124             : 
     125          13 :         return;
     126             : }
     127             : 
     128           1 : void sigbus_reset(void) {
     129             : 
     130           1 :         if (n_installed <= 0)
     131           0 :                 return;
     132             : 
     133           1 :         n_installed--;
     134             : 
     135           1 :         if (n_installed == 0)
     136           1 :                 assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
     137             : 
     138           1 :         return;
     139             : }

Generated by: LCOV version 1.14