This is the mail archive of the libc-help@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: determine whether code is running in a signal handler context


2017-10-19 2:34 GMT+08:00 Carlos O'Donell <carlos@redhat.com>:
> On 10/18/2017 07:18 AM, Yubin Ruan wrote:
>> Hi,
>>
>> I am writing to see if this is any util functions in libc that can
>> help to determine it is currently running in a signal.
>>
>> I wrote some library and provide a function which will be used in many
>> client code. However this function is not async-signal safe (it calls
>> malloc(3)) so when it is called, I want to detect whether it is
>> currently running in a signal handler. If it is, I can avoid calling
>> those not-async-signal-safe functions which might cause deadlock.
>>
>> that is, I want a `in_signal_handler_context()' utility that can be
>> used as this:
>>
>> int mylibfunc( void ) {
>>     if( in_signal_handler_context() ) { return(-1) }
>>     // rest of function goes here
>>     return( 0 );
>> }
>
> Only (b).
>
> (a) The kernel knows, but doesn't tell you.
>
> (b) Only via architecture-specific checks which involve inspecting the
>     call frame to determine if there is a signal frame on the stack.

Is (b) deterministic? How can we know whether a call frame is a signal
frame and how can we know at which point we should stop when
inspecting the frame?

Somebody pointed out that on Linux all signal handlers are called from
vdso, so I come up with the following code to "inspect" the call frame
and see whether there is any address which points at some place inside
the VDSO:

===============================
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>

#include <unistd.h>

uintmax_t vdso_start = 0;
uintmax_t vdso_end = 0;         /* actually, next byte */

int check_stack_for_vdso(uint64_t rsp, size_t len)
{
    size_t i;
    /* printf("rsp = %jx\n", rsp); */

    for (i = 0; i < len; i++, rsp++) {
        /* printf("*rsp is: %jx\n", *(uint64_t *)rsp); */
        if ((*(uint64_t *)rsp) >= vdso_start && (*(uint64_t *)rsp) < vdso_end)
                return 1;
    }

    return 0;
}

void handler(int signo)
{
    uint64_t rsp;

    __asm__ __volatile__ ("movq %%rsp, %0" : "=r"(rsp));
    /* XXX only for demonstration, don't call printf from a signal handler */
    printf("handler: check_stack_for_vdso() = %d\n",
check_stack_for_vdso(rsp, 200));
    /* I am hoping that `check_stack_for_vdso()' here would return 1.
     * But that is not the case */
}

void parse_maps()
{
    FILE *maps;
    char buf[256];
    char path[7];
    uintmax_t start, end, offset, inode;
    char r, w, x, p;
    unsigned major, minor;

    maps = fopen("/proc/self/maps", "rt");
    if (maps == NULL)
        return;

    while (!feof(maps) && !ferror(maps)) {
        if (fgets(buf, 256, maps) != NULL) {
                if (sscanf(buf, "%jx-%jx %c%c%c%c %jx %u:%u %ju %6s",
                                &start, &end, &r, &w, &x, &p, &offset,
                                &major, &minor, &inode, path) == 11) {
                        if (!strcmp(path, "[vdso]")) {
                                vdso_start = start;
                                vdso_end = end;
                                break;
                        }
                }
        }
    }

    fclose(maps);

    printf("[vdso] at %jx-%jx\n", vdso_start, vdso_end);
}

int main()
{
    struct sigaction sa;
    uint64_t rsp;

    parse_maps();
    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;

    if (sigaction(SIGUSR1, &sa, NULL) < 0) {
        perror("sigaction");
        exit(1);
    }

    __asm__ __volatile__ ("movq %%rsp, %0" : "=r"(rsp));
    printf("before kill: check_stack_for_vdso() = %d\n",
check_stack_for_vdso(rsp, 200));

    kill(getpid(), SIGUSR1);

    __asm__ __volatile__ ("movq %%rsp, %0" : "=r"(rsp));
    printf("after kill: check_stack_for_vdso() = %d\n",
check_stack_for_vdso(rsp, 200));

    return 0;
}
==============================================

I am using a 64 bit Linux. I try to go back 200 step long when inside
a signal handler, but I cannot find any address inside the vdso area.

(sorry if the code above get auto-wrapped by the mail agent)

Yubin

[1]: https://stackoverflow.com/questions/4832743/howto-determine-if-code-is-running-in-signal-handler-context
(I use some of its code)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]