PATCH for 2.1.26: newly forked processes killed by "handled signals"

Kevin Buhr (
12 Feb 1997 23:24:48 -0600

Content-Type: text/plain; charset=US-ASCII


After your patch fixed the "socketpair" behaviour, I got "dump"
working and managed to discover another 2.1.26 kernel bug. The
changes to "sys_sigreturn" in "arch/i386/kernel/signal.c" that added
more rigorous checking of segment register values---I don't know when
they were added---have the unintended side effect of causing crashes
in certain very rare circumstances.

Basically, the "copy_thread" function of "arch/i386/kernel/signal.c"
initializes the TSS's %gs with KERNEL_DS. If the newly forked child
is signaled immediately *and* if the child has a handler for that
signal, then the pseudobogus %gs value will be saved by "setup_frame"
(in "arch/i386/kernel/signal.c") and, when returning from the handler,
the "GET_SEG(gs)" in "sys_sigreturn" will barf and "do_exit(SIGSEGV)"
the child.

For reasons I still don't fully understand, if the child is allowed to
return from the "fork" call and begin execution before receiving the
signal, the %gs register is automagically "fixed" (but I can't figure
out where), and we never notice the problem. The enclosed
"signaltest.c" illustrates the bug: on a vanilla 2.1.26 kernel, the
child quiety dies immediately after handling the signal. A hacked up
kernel verifies that "GET_SEG(gs)" is the culprit.

The enclosed patch appears to fix the problem by having "copy_thread"
initialize %gs with "USER_DS" instead, as is done in other, similar
contexts. Does this break anything else?

Kevin <>

Content-Type: application/octet-stream
Content-Disposition: attachment; filename="signaltest.c"
Content-Transfer-Encoding: 7bit

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>

fprintf(stderr, "signal handled!\n");

int child, rc;

signal(SIGUSR2, handler);

child = fork();
if (child == -1) {
if (child == 0) {
fprintf(stderr, "before sleep\n");
fprintf(stderr, "after sleep\n");

* uncomment the next line, and the child will survive
/* sleep(1); */

kill(child, SIGUSR2);

printf("Status = %d\n", rc);

Content-Type: application/octet-stream
Content-Disposition: attachment; filename="patch-2.1.26-signalbug"
Content-Transfer-Encoding: 7bit

--- linux/arch/i386/kernel/process.c 1997/02/13 04:20:30 1.1
+++ linux/arch/i386/kernel/process.c 1997/02/13 04:20:44 1.2
@@ -486,7 +486,7 @@
p-> = KERNEL_DS;
p->tss.ds = KERNEL_DS;
p->tss.fs = USER_DS;
- p-> = KERNEL_DS;
+ p-> = USER_DS;
p->tss.ss0 = KERNEL_DS;
p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE;
p-> = _TSS(nr);