[PATCH 2.5.73] Fix broken signal optimization for i386

Jörn Engel (joern@wohnheim.fh-wedel.de)
Thu, 3 Jul 2003 22:24:10 +0200


Hi!

I guess it is an optimizasion that there is no flag to indicate
whether a process is operating on the signal stack or not. That
information is instead generated by checking whether the stack pointer
is inside the signal stack or not.

But the stack pointer is under the control of the userspace. Thus, a
broken program can cause the kernel to have utterly unexpected
behaviour. You can recreate this by running the program below, which
I would expect to cause a segmentation fault and dump a core in the
process.

This patch fixes the problem for i386. It could still some work, but
at least, it is better than the current behaviour. One possible
improvement may be to cause the final SIGSEGV a bit sooner, another to
fold the flag into an unused bit somewhere in struct task_struct,
possibly in the low bits of the two sas_ss_* fields.

Two more patches for ppc are in the queue, ppc64 should be quite
similar. I guess those should go to Paul and Anton, right?

[ My fscking email is still broken for some, including kernel.org.
The admin that thought DNS entries should be cached for a full week
to save some network bandwidth ought to be shot after some very
cruel torture. Sorry, folks. ]

---<snip>---
#define __USE_BSD
#include <signal.h>

#if defined(i386)
# define killstack() asm("mov %ebx,0")
#elif defined(PPC)
# define killstack() asm("lis 1,0")
#else
# define killstack() fprintf(stderr, "cannot kill stack on this arch\n")
#endif

char stack[8192];

struct sigaltstack ss = {
.ss_sp = stack,
.ss_flags = 0,
.ss_size = sizeof(stack),
};

void null_handler(int signal)
{
printf("SIGNAL .... %d\n", signal);
killstack();
}

int main(int argc, char** argv)
{
struct sigaction sa = {
.sa_handler = null_handler,
.sa_flags = SA_ONSTACK,
};

sigaltstack(&ss, 0);
sigaction(SIGSEGV, &sa, 0);
killstack();
return 0; /* yeah, right! */
}
---<snap>---

Jörn

-- 
"Error protection by error detection and correction."
-- from a university class

--- linux-2.5.73/arch/i386/kernel/signal.c~signal_i386 2003-07-03 19:17:21.000000000 +0200 +++ linux-2.5.73/arch/i386/kernel/signal.c 2003-07-03 22:04:30.000000000 +0200 @@ -181,6 +181,9 @@ } } + if (sas_ss_flags(regs->esp) == 0) + current->sas_ss_inuse = 0; + err |= __get_user(*peax, &sc->eax); return err; @@ -317,9 +320,22 @@ esp = regs->esp; /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa.sa_flags & SA_ONSTACK) { - if (sas_ss_flags(esp) == 0) - esp = current->sas_ss_sp + current->sas_ss_size; + if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(esp) == 0)) { + /* If we have switches to the signal stack before, + * something bad has happened to it, asking for a + * segmentation fault. + * If not, remember it for the next time + */ + if (current->sas_ss_inuse) { + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); + /* XXX would it be simpler to return some broken + * value like NULL and have the calling function + * signal the segv? + */ + } + current->sas_ss_inuse = 1; + esp = current->sas_ss_sp + current->sas_ss_size; } /* This is the legacy signal stack switching. */ --- linux-2.5.73/include/linux/sched.h~signal_i386 2003-07-03 19:17:21.000000000 +0200 +++ linux-2.5.73/include/linux/sched.h 2003-07-03 19:17:58.000000000 +0200 @@ -425,6 +425,7 @@ unsigned long sas_ss_sp; size_t sas_ss_size; + int sas_ss_inuse; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/