This is the mail archive of the glibc-bugs@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]

[Bug nptl/21108] New: multithreaded setuid can silently fail on rt signal exhaustion


https://sourceware.org/bugzilla/show_bug.cgi?id=21108

            Bug ID: 21108
           Summary: multithreaded setuid can silently fail on rt signal
                    exhaustion
           Product: glibc
           Version: 2.19
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: nptl
          Assignee: unassigned at sourceware dot org
          Reporter: jannh at google dot com
                CC: drepper.fsp at gmail dot com
  Target Milestone: ---
             Flags: security?

__nptl_setxid() signals threads using setxid_signal_thread(), which assumes
that the tgkill() syscall with the real-time signal SIGSETXID can only fail if
the target thread has not started yet or has exited. Actually, for realtime
signals, tgkill() can also fail if the per-user limit on pending signals has
been reached. This can be seen in the kernel sources, where tgkill() is handled
through the call chain
do_tkill->do_send_specific->do_send_sig_info->send_signal->__send_signal.
__send_signal sets the override_rlimit flag to 0 because the signal number is
>=SIGRTMIN, then passes that flag to __sigqueue_alloc(), which then refuses to
allocate a signal if RLIMIT_SIGPENDING has been reached by the current user (as
determined using the real uid). __send_signal() fails with error code -EAGAIN.

To reproduce:

preparation:
===========================================================
$ cat starve.c
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <err.h>

int main(void) {
        sigset_t rtmin_set;
        sigemptyset(&rtmin_set);
        sigaddset(&rtmin_set, SIGRTMIN);
        if (sigprocmask(SIG_BLOCK, &rtmin_set, NULL))
                err(1, "sigprocmask");

        pid_t me = getpid();
        int count = 0;
        const union sigval val = {.sival_int = 0};
        while (1) {
                if (sigqueue(me, SIGRTMIN, val))
                        break;
                count++;
        }
        if (errno == EAGAIN) {
                printf("EAGAIN at %d\n", count);
                while (1) pause();
        }
        errx(1, "sigqueue");
}
$ cat threaded_setuid.c
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <err.h>

void *thread_fn(void *arg) {
        while (1) pause();
}

int main(void) {
        pthread_t thread;
        pthread_create(&thread, NULL, thread_fn, NULL);
        if (setuid(getuid()))
                err(1, "setuid");
        puts("done");
        while (1) pause();
$ gcc -Wall -o starve starve.c -std=gnu99 && gcc -pthread -Wall -o
threaded_setuid threaded_setuid.c -std=gnu99 && sudo chown root:root
threaded_setuid && sudo chmod 4755 threaded_setuid
===========================================================


normal behavior:
===========================================================
$ ./threaded_setuid &
[1] 61208
done
$ cat /proc/$(pgrep threaded_setuid)/task/*/status | grep ^Uid:
Uid:    379777  379777  379777  379777
Uid:    379777  379777  379777  379777
$ fg
./threaded_setuid
^C

===========================================================


buggy behavior:
===========================================================
$ ./starve &
[1] 61239
EAGAIN at 256342
$ ./threaded_setuid &
[2] 61620
done
$ cat /proc/$(pgrep threaded_setuid)/task/*/status | grep ^Uid:
Uid:    379777  379777  379777  379777
Uid:    379777  0       0       0
$ fg
./threaded_setuid
^C
$ fg
./starve
^C
===========================================================

-- 
You are receiving this mail because:
You are on the CC list for the bug.

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