c++csigbus

Catch SIGBUS in C and C++


I want to catch SIGBUS, my code is shown below:

#include <stdlib.h>
#include <signal.h>
#include <iostream>
#include <stdio.h>

void catch_sigbus (int sig)
{
    //std::cout << "SIGBUS" << std::endl;
    printf("SIGBUS\n");   
    exit(-1);
}


int main(int argc, char **argv) {

signal (SIGBUS, catch_sigbus);

    int *iptr;
    char *cptr;

#if defined(__GNUC__)
# if defined(__i386__)
    /* Enable Alignment Checking on x86 */
    __asm__("pushf\norl $0x40000,(%esp)\npopf");
# elif defined(__x86_64__) 
     /* Enable Alignment Checking on x86_64 */
    __asm__("pushf\norl $0x40000,(%rsp)\npopf");
# endif
#endif

    /* malloc() always provides aligned memory */
    cptr = (char*)malloc(sizeof(int) + 1);

    /* Increment the pointer by one, making it misaligned */
    iptr = (int *) ++cptr;

    /* Dereference it as an int pointer, causing an unaligned access */

    *iptr = 42;

    return 0;
}

When I use printf, it can catch by calling catch_sigbus, but when I use cout, it cannot. So anybody could help me? I run on Ubuntu 12.04.

I have another question. When I catch SIGBUS, how can I get si_code? BUS_ADRALN/BUS_ADRERR/BUS_OBJERR


Solution

  • You can't use printf or cout in a signal handler. Nor can you call exit. You got lucky with printf this time, but you weren't as lucky with cout. If your program is in a different state maybe cout will work and printf won't. Or maybe neither, or both. Check the documentation of your operating system to see which functions are signal safe (if it exists, it's often very badly documented).

    Your safest bet in this case is to call write to STDERR_FILENO directly and then call _exit (not exit, that one is unsafe in a signal handler). On some systems it's safe to call fprintf to stderr, but I'm not sure if glibc is one of them.

    Edit: To answer your added question, you need to set up your signal handlers with sigaction to get the additional information. This example is as far as I'd go inside a signal handler, I included an alternative method if you want to go advanced. Notice that write is theoretically unsafe because it will break errno, but since we're doing _exit it will be safe in this particular case:

    #include <stdlib.h>
    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    
    void
    bus_handler(int sig, siginfo_t *si, void *vuctx)
    {
            char buf[2];
    #if 1
            /*                                                                                                                           
             * I happen to know that si_code can only be 1, 2 or 3 on this                                                               
             * particular system, so we only need to handle one digit.                                                                   
             */
            buf[0] = '0' + si->si_code;
            buf[1] = '\n';
            write(STDERR_FILENO, buf, sizeof(buf));
    #else
            /*                                                                                                                           
             * This is a trick I sometimes use for debugging , this will                                                                 
             * be visible in strace while not messing with external state too                                                            
             * much except breaking errno.                                                                                                
             */
            write(-1, NULL, si->si_code);
    #endif
            _exit(1);
    }
    
    int
    main(int argc, char **argv)
    {
            struct sigaction sa;
            char *cptr;
            int *iptr;
    
            memset(&sa, 0, sizeof(sa));
    
            sa.sa_sigaction = bus_handler;
            sa.sa_flags = SA_SIGINFO;
            sigfillset(&sa.sa_mask);
            sigaction(SIGBUS, &sa, NULL);
    
    #if defined(__GNUC__)
    # if defined(__i386__)
            /* Enable Alignment Checking on x86 */
            __asm__("pushf\norl $0x40000,(%esp)\npopf");
    # elif defined(__x86_64__)
            /* Enable Alignment Checking on x86_64 */
            __asm__("pushf\norl $0x40000,(%rsp)\npopf");
    # endif
    #endif
    
            /* malloc() always provides aligned memory */
            cptr = (char*)malloc(sizeof(int) + 1);
    
            /* Increment the pointer by one, making it misaligned */
            iptr = (int *) ++cptr;
    
            /* Dereference it as an int pointer, causing an unaligned access */
    
            *iptr = 42;
    
            return 0;
    }