-1

I was performing a simple demonstration of signal handlers as an exercise when I noticed some odd behaviour. The following code works unchanged on ubuntu 14.04 LTS but not on macOS Sierra 10.12.6. That is on macOS the program hangs forever with no output. However, it works on both platforms with printf commented out in the signal handlers or with an extra printf added in the main function anywhere before the posix_memalign call. I don't understand this behaviour, what's going on?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <signal.h>


//Has to be global for signal handler
char* mem_spot = NULL;
const char* copy_from = "hello";
const int mem_req = 5;

//signal handler prototype
void handle_seg(int fault);

int
main(int argc, char const *argv[])
{
    //On macOS this must be placed uncommented somewhere before
    //posix_memalign or the signal handler printing removed:

    //printf("Strange fixing code!");

    //setup signal handlers
    signal(SIGSEGV,handle_seg);
    signal(SIGBUS,handle_seg);
    //get memory aligned to the page and protect it
    posix_memalign((void**) &mem_spot, getpagesize(), mem_req);
    mprotect(mem_spot,mem_req,PROT_READ);
    //Copying into the memory slot (it's protected so should break!)
    strcpy(mem_spot,copy_from);
    //Output my results
    printf("Write of string '%s' to memory giving '%s' complete!\n",copy_from, mem_spot);
    return 0;
}

void handle_seg(int fault)
{
    if (fault == SIGSEGV){
        printf(" (Handled a segmentation fault!) ");
        mprotect(mem_spot,mem_req,PROT_READ | PROT_WRITE);
    }
    else if (fault == SIGBUS){
        printf(" (Handled a bus fault!) ");
        mprotect(mem_spot,mem_req,PROT_READ | PROT_WRITE);
    }
}
J-S
  • 973
  • 2
  • 8
  • 13
  • 1
    Well, `printf()` and `mprotect()` are not functions defined as safe to use in a signal handler function. Plus the behavior of a program that gets a SIGSEGV that's not caused by `raise()` or `kill()` and doesn't halt is undefined. So your program has multiple issues that stop it from being valid or predictable. – Shawn Sep 22 '18 at 07:07
  • @Shawn While it might not be defined by ANSI C, I think POSIX may specify it predictably enough. I'm talking about the behavior of SIGSEGV. – Barmar Sep 22 '18 at 07:15
  • @Barmar I *am* talking about POSIX behavior. See http://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html (Which can actually be read to say that it's undefined if the signal handler returns from *any* source of a SIGSEGV). – Shawn Sep 22 '18 at 07:19
  • 1
    @Barmar Also http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 which is more precise. – Shawn Sep 22 '18 at 07:22

1 Answers1

3

So, to give an actual answer: Your program triggers undefined behavior in a number of ways, which means it can do anything. Printing out something, or hanging, or crashing, are all equally valid results of running it.

Your first problem: Calling a non-async-signal-safe function from a signal handler. There are a limited number of functions that can safely be called from handlers. You can see the list here, or on linux, with man signal-safety.

Your second problem: Returning from a signal handler triggered by a SIGSEGV that wasn't triggered by a function like kill() that explicitly raises a given signal. To quote from the above documentation:

The behavior of a process is undefined after it returns normally from a signal-catching function for a SIGBUS, SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(), sigqueue(), or raise().

To summarize: What you're doing is inherently broken and not something that can be predicted or reasoned about or understood. Don't do it.

Shawn
  • 47,241
  • 3
  • 26
  • 60