LCOV - code coverage report
Current view: top level - basic - sigbus.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 43 56 76.8 %
Date: 2015-07-29 18:47:03 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2014 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <signal.h>
      23             : #include <sys/mman.h>
      24             : 
      25             : #include "macro.h"
      26             : #include "util.h"
      27             : #include "sigbus.h"
      28             : 
      29             : #define SIGBUS_QUEUE_MAX 64
      30             : 
      31             : static struct sigaction old_sigaction;
      32             : static unsigned n_installed = 0;
      33             : 
      34             : /* We maintain a fixed size list of page addresses that triggered a
      35             :    SIGBUS. We access with list with atomic operations, so that we
      36             :    don't have to deal with locks between signal handler and main
      37             :    programs in possibly multiple threads. */
      38             : 
      39             : static void* volatile sigbus_queue[SIGBUS_QUEUE_MAX];
      40             : static volatile sig_atomic_t n_sigbus_queue = 0;
      41             : 
      42           2 : static void sigbus_push(void *addr) {
      43             :         unsigned u;
      44             : 
      45           2 :         assert(addr);
      46             : 
      47             :         /* Find a free place, increase the number of entries and leave, if we can */
      48           3 :         for (u = 0; u < SIGBUS_QUEUE_MAX; u++)
      49           3 :                 if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) {
      50           2 :                         __sync_fetch_and_add(&n_sigbus_queue, 1);
      51           2 :                         return;
      52             :                 }
      53             : 
      54             :         /* If we can't, make sure the queue size is out of bounds, to
      55             :          * mark it as overflow */
      56             :         for (;;) {
      57             :                 unsigned c;
      58             : 
      59           0 :                 __sync_synchronize();
      60           0 :                 c = n_sigbus_queue;
      61             : 
      62           0 :                 if (c > SIGBUS_QUEUE_MAX) /* already overflow */
      63           0 :                         return;
      64             : 
      65           0 :                 if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX))
      66           0 :                         return;
      67           0 :         }
      68             : }
      69             : 
      70       84366 : int sigbus_pop(void **ret) {
      71       84366 :         assert(ret);
      72             : 
      73             :         for (;;) {
      74             :                 unsigned u, c;
      75             : 
      76       84366 :                 __sync_synchronize();
      77       84366 :                 c = n_sigbus_queue;
      78             : 
      79       84366 :                 if (_likely_(c == 0))
      80       84364 :                         return 0;
      81             : 
      82           2 :                 if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
      83           0 :                         return -EOVERFLOW;
      84             : 
      85           3 :                 for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
      86             :                         void *addr;
      87             : 
      88           3 :                         addr = sigbus_queue[u];
      89           3 :                         if (!addr)
      90           1 :                                 continue;
      91             : 
      92           2 :                         if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) {
      93           2 :                                 __sync_fetch_and_sub(&n_sigbus_queue, 1);
      94           2 :                                 *ret = addr;
      95           2 :                                 return 1;
      96             :                         }
      97             :                 }
      98           0 :         }
      99             : }
     100             : 
     101           2 : static void sigbus_handler(int sn, siginfo_t *si, void *data) {
     102             :         unsigned long ul;
     103             :         void *aligned;
     104             : 
     105           2 :         assert(sn == SIGBUS);
     106           2 :         assert(si);
     107             : 
     108           2 :         if (si->si_code != BUS_ADRERR || !si->si_addr) {
     109           0 :                 assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
     110           0 :                 raise(SIGBUS);
     111           0 :                 return;
     112             :         }
     113             : 
     114           2 :         ul = (unsigned long) si->si_addr;
     115           2 :         ul = ul / page_size();
     116           2 :         ul = ul * page_size();
     117           2 :         aligned = (void*) ul;
     118             : 
     119             :         /* Let's remember which address failed */
     120           2 :         sigbus_push(aligned);
     121             : 
     122             :         /* Replace mapping with an anonymous page, so that the
     123             :          * execution can continue, however with a zeroed out page */
     124           2 :         assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned);
     125             : }
     126             : 
     127           1 : void sigbus_install(void) {
     128           1 :         struct sigaction sa = {
     129             :                 .sa_sigaction = sigbus_handler,
     130             :                 .sa_flags = SA_SIGINFO,
     131             :         };
     132             : 
     133           1 :         n_installed++;
     134             : 
     135           1 :         if (n_installed == 1)
     136           1 :                 assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0);
     137             : 
     138           1 :         return;
     139             : }
     140             : 
     141           1 : void sigbus_reset(void) {
     142             : 
     143           1 :         if (n_installed <= 0)
     144           0 :                 return;
     145             : 
     146           1 :         n_installed--;
     147             : 
     148           1 :         if (n_installed == 0)
     149           1 :                 assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
     150             : 
     151           1 :         return;
     152             : }

Generated by: LCOV version 1.11