LCOV - code coverage report
Current view: top level - basic - sigbus.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 44 57 77.2 %
Date: 2019-08-23 13:36:53 Functions: 5 5 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 22 44 50.0 %

           Branch data     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                 :          8 : static void sigbus_push(void *addr) {
      26                 :            :         unsigned u;
      27                 :            : 
      28         [ -  + ]:          8 :         assert(addr);
      29                 :            : 
      30                 :            :         /* Find a free place, increase the number of entries and leave, if we can */
      31         [ +  - ]:         12 :         for (u = 0; u < SIGBUS_QUEUE_MAX; u++)
      32         [ +  + ]:         12 :                 if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) {
      33                 :          8 :                         __sync_fetch_and_add(&n_sigbus_queue, 1);
      34                 :          8 :                         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                 :     552872 : int sigbus_pop(void **ret) {
      54         [ -  + ]:     552872 :         assert(ret);
      55                 :            : 
      56                 :          0 :         for (;;) {
      57                 :            :                 unsigned u, c;
      58                 :            : 
      59                 :     552872 :                 __sync_synchronize();
      60                 :     552872 :                 c = n_sigbus_queue;
      61                 :            : 
      62         [ +  + ]:     552872 :                 if (_likely_(c == 0))
      63                 :     552864 :                         return 0;
      64                 :            : 
      65         [ -  + ]:          8 :                 if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
      66                 :          0 :                         return -EOVERFLOW;
      67                 :            : 
      68         [ +  - ]:         12 :                 for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
      69                 :            :                         void *addr;
      70                 :            : 
      71                 :         12 :                         addr = sigbus_queue[u];
      72         [ +  + ]:         12 :                         if (!addr)
      73                 :          4 :                                 continue;
      74                 :            : 
      75         [ +  - ]:          8 :                         if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) {
      76                 :          8 :                                 __sync_fetch_and_sub(&n_sigbus_queue, 1);
      77                 :          8 :                                 *ret = addr;
      78                 :          8 :                                 return 1;
      79                 :            :                         }
      80                 :            :                 }
      81                 :            :         }
      82                 :            : }
      83                 :            : 
      84                 :          8 : static void sigbus_handler(int sn, siginfo_t *si, void *data) {
      85                 :            :         unsigned long ul;
      86                 :            :         void *aligned;
      87                 :            : 
      88         [ -  + ]:          8 :         assert(sn == SIGBUS);
      89         [ -  + ]:          8 :         assert(si);
      90                 :            : 
      91   [ +  -  -  + ]:          8 :         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                 :          8 :         ul = (unsigned long) si->si_addr;
      98                 :          8 :         ul = ul / page_size();
      99                 :          8 :         ul = ul * page_size();
     100                 :          8 :         aligned = (void*) ul;
     101                 :            : 
     102                 :            :         /* Let's remember which address failed */
     103                 :          8 :         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         [ -  + ]:          8 :         assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned);
     108                 :            : }
     109                 :            : 
     110                 :         52 : void sigbus_install(void) {
     111                 :         52 :         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                 :         52 :         (void) page_size();
     119                 :            : 
     120                 :         52 :         n_installed++;
     121                 :            : 
     122         [ +  - ]:         52 :         if (n_installed == 1)
     123         [ -  + ]:         52 :                 assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0);
     124                 :            : 
     125                 :         52 :         return;
     126                 :            : }
     127                 :            : 
     128                 :          4 : void sigbus_reset(void) {
     129                 :            : 
     130         [ -  + ]:          4 :         if (n_installed <= 0)
     131                 :          0 :                 return;
     132                 :            : 
     133                 :          4 :         n_installed--;
     134                 :            : 
     135         [ +  - ]:          4 :         if (n_installed == 0)
     136         [ -  + ]:          4 :                 assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
     137                 :            : 
     138                 :          4 :         return;
     139                 :            : }

Generated by: LCOV version 1.14