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

Kevin Buhr (buhr@stat.wisc.edu)
12 Feb 1997 23:24:48 -0600


--Multipart_Wed_Feb_12_23:24:48_1997-1
Content-Type: text/plain; charset=US-ASCII

Alan:

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 <buhr@stat.wisc.edu>

--Multipart_Wed_Feb_12_23:24:48_1997-1
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>

void
handler()
{
fprintf(stderr, "signal handled!\n");
}

main()
{
int child, rc;

signal(SIGUSR2, handler);

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

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

kill(child, SIGUSR2);

sleep(5);
wait(&rc);
printf("Status = %d\n", rc);
}

--Multipart_Wed_Feb_12_23:24:48_1997-1
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->tss.ss = KERNEL_DS;
p->tss.ds = KERNEL_DS;
p->tss.fs = USER_DS;
- p->tss.gs = KERNEL_DS;
+ p->tss.gs = USER_DS;
p->tss.ss0 = KERNEL_DS;
p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE;
p->tss.tr = _TSS(nr);

--Multipart_Wed_Feb_12_23:24:48_1997-1--