Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
646 views
in Technique[技术] by (71.8m points)

c - About catching the SIGSEGV in multithreaded environment

I'd like to know if it is possible/the recommended way to catch the SIGSEGV signal in multithreaded environment. I am particularly interested in handling the SIGSEGV raised by something like *((int *)0) = 0.

Some reading on this topic led me to signal() and sigaction(), which install a signal handler. While neither seem promising in multithreaded environment. I then tried the sigwaitinfo(), receiving the signals in one thread with a prior pthread_sigmask() call that blocks the signal on the others. It worked to the extent upon which the signal SIGSEGV was raised, using raise(), inside a thread or when it was sent to the process by something like kill -SIGSEGV; however, *((int*)0) = 0 still kills the process. My test program is as follows

void block_signal()
{
        sigset_t set;

        sigemptyset(&set);
        sigaddset(&set, SIGSEGV);
        sigprocmask(SIG_BLOCK, &set, NULL);

        if (pthread_sigmask(SIG_BLOCK, &set, NULL)) {
                fprintf(stderr, "pthread_sigmask failed
");
                exit(EXIT_FAILURE);
        }
    }

void *buggy_thread(void *param)
{
        char *ptr = NULL;
        block_signal();

        printf("Thread %lu created
", pthread_self());

        // Sleep for some random time
        { ... }

        printf("About to raise from %lu
", pthread_self());

        // Raise a SIGSEGV
        *ptr = 0;

        pthread_exit(NULL);
}

void *dispatcher(void *param)
{
        sigset_t set;
        siginfo_t info;
        int sig;

        sigemptyset(&set);
        sigaddset(&set, SIGSEGV);

        for (;;) {
                sig = sigwaitinfo(&set, &info);
                if (sig == -1)
                        fprintf(stderr, "sigwaitinfo failed
");
                else
                        printf("Received signal SIGSEGV from %u
", info.si_pid);
        }
}

int main()
{
        int i;
        pthread_t tid;
        pthread_t disp_tid;

        block_signal();

        if (pthread_create(&disp_tid, NULL, dispatcher, NULL)) {
                fprintf(stderr, "Cannot create dispatcher
");
                exit(EXIT_FAILURE);
        }

        for (i = 0; i < 10; ++i) {
                if (pthread_create(&tid, NULL, buggy_thread, NULL) {
                        fprintf(stderr, "Cannot create thread
");
                        exit(EXIT_FAILURE);
                }
        }

        pause();
}

Unexpectedly, the program dies with a segmentation fault instead of printing the raiser's thread id.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your code does not call sigaction(2), and I believe it should call it. Read also signal(7) and signal-safety(7). And the signal action (thru sa_sigaction field should do something (machine specific) with its siginfo_t to skip the offending machine instruction, or to mmap the offending address, or call siglongjmp, otherwise when returning from the signal handler you'll get the SIGSEGV again since the offending machine instruction is restarted.

You cannot handle the SIGSEGV in another thread, since synchronous signals (such as SIGSEGV or SIGSYS) are thread specific (see this answer), so what you try to achieve with sigwaitinfo cannot work. In particular SIGSEGV is directed to the offending thread.

Read also all about Linux signals.

PS. An example of clever SIGSEGV handling is offered by the no-more maintained (in May 2019) Ravenbrook MPS garbage collector library. Notice also the Linux specific and recent userfaultfd(2) and signalfd(2) system calls.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...