Possible DoS on Linux kernel 2.4 and 2.6 using sigqueue overflow.

2004-04-13T00:00:00
ID SECURITYVULNS:DOC:6047
Type securityvulns
Reporter Securityvulns
Modified 2004-04-13T00:00:00

Description

Hello.

We faced a bug (?) in Linux kernel causing different misbehaviours on our server. After exploration, it seems that we found some security implications of this issue.

When a process exits, it's parent is notified by SIGCHLD, and finished child is kept in process table in "zombie" state until parent process (or init, if parent is already ended) handles child exit.

Similary, with linuxthreads, when a thread exits, another thread in the same process is notified by signal 33 (SIGRT_1), and exitted thread exists in the process table in "zombie" state until the exit is handled.

When a signal that notifies about exit is generated by the kernel, kernel code allocates a "struct sigqueue" object. This object keeps information about the signal until the signal is delivered.

Only a limited number of such objects may be allocated at a time. There is some code in the kernel that still allows signals with numbers less than 32 to be delivered when "struct sigqueue" object can't be allocated. However, for signal 33 signal generation routine just returns -EAGAIN in this case. As the result, process is not notified about thread exits, and ended thread is left in "zombie" state. Details are at http://www.ussg.iu.edu/hypermail/linux/kernel/0404.0/0208.html

For long-living processes that create short-living threads (such as mysqld), this causes process table overflow in several minutes.

"struct sigqueue" overflow may be easily caused from userspace, if a process blocks a signal and then receives a large number of such signals. The following sample code does that:

include <signal.h>

include <unistd.h>

include <stdlib.h>

int main() { sigset_t set; int i; pid_t pid;

    sigemptyset&#40;&set&#41;;
    sigaddset&#40;&set, 40&#41;;
    sigprocmask&#40;SIG_BLOCK, &set, 0&#41;;

    pid = getpid&#40;&#41;;
    for &#40;i = 0; i &lt; 1024; i++&#41;
            kill&#40;pid, 40&#41;;

    while &#40;1&#41;
            sleep&#40;1&#41;;

}

So if a user runs such code (or just runs a buggy program that blocks a signal and then receives 1000 such signals - which happens here), this will cause a DoS againt anything running on the same system that uses linuxthreads, including daemons running as root.

On systems that use NPTL (such as Linux 2.6 kernel) there is no 'thread zombie' problem, because in NPTL another notification mechanism is used. However, DoS is still possible (and really happens - in form of daemon crashes), because when it is not possible to allocatre a "struct sigqueue" object, kernel behaviour in signal-passing changes, causing random hangs and segfaults in different programs.