setup_frame() failure

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 07 Dec 2000, Carsten Langgaard posted a question about failure of
setup_frame() function. (and there was no response on ML)

I found that if setup_frame() fails in certain conditions the process
which caused the signal grabs CPU and never be killed.

Here is a sample test program.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sighandler(int sig)
{
	printf("SIGNAL %d!\n", sig);
	exit(2);
}
void setup_signal(int sig)
{
	struct sigaction act;
	memset(&act, 0, sizeof(act));
	act.sa_handler = sighandler;
	act.sa_flags = SA_NOMASK | SA_RESTART;
	sigaction(sig, &act, 0);
}

int main(int argc, char **argv)
{
	setup_signal(SIGILL);
	setup_signal(SIGQUIT);
	setup_signal(SIGINT);

	__asm__ __volatile__("move $29,$0");
	__asm__ __volatile__("mfc0 $0,$0");
	printf("done!\n");
	return 0;
}


This program setups signal handlers and causes Coprocessor Unusable
Exception with $sp == 0.

If we run this program,

1.  "mfc1" instruction raises a exception.
2.  The exception handler queues SIGILL(4).
3.  do_signal() dequeue a signal with LOWEST number.
4.  setup_frame() fails and queues SIGSEGV(11).
5.  returns to user process.
6.  again from 1. (forever)

So, even SIGKILL can not kill the process.  (SIGHUP can do it).

I make a change for do_signal() to check failure of setup_frame() and
continue processing pending signals.  It seems work for me.  Here is
the patch.  Any comments are welcome.

---
Atsushi Nemoto
diff -ur linux.sgi/arch/mips/kernel/signal.c linux/arch/mips/kernel/signal.c
--- linux.sgi/arch/mips/kernel/signal.c	Mon Jun 25 22:56:56 2001
+++ linux/arch/mips/kernel/signal.c	Fri Sep  7 18:52:23 2001
@@ -382,7 +382,7 @@
 	return (void *)((sp - frame_size) & ALMASK);
 }
 
-static void inline
+static int inline
 setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
             int signr, sigset_t *set)
 {
@@ -437,15 +437,16 @@
 	printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n",
 	       current->comm, current->pid, frame, regs->cp0_epc, frame->code);
 #endif
-        return;
+	return 0;
 
 give_sigsegv:
 	if (signr == SIGSEGV)
 		ka->sa.sa_handler = SIG_DFL;
 	force_sig(SIGSEGV, current);
+	return SIGSEGV;
 }
 
-static void inline
+static int inline
 setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
                int signr, sigset_t *set, siginfo_t *info)
 {
@@ -513,22 +514,26 @@
 	printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n",
 	       current->comm, current->pid, frame, regs->cp0_epc, frame->code);
 #endif
-	return;
+	return 0;
 
 give_sigsegv:
 	if (signr == SIGSEGV)
 		ka->sa.sa_handler = SIG_DFL;
 	force_sig(SIGSEGV, current);
+	return SIGSEGV;
 }
 
-static inline void
+static inline int
 handle_signal(unsigned long sig, struct k_sigaction *ka,
 	siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
 {
+	int newsig;
 	if (ka->sa.sa_flags & SA_SIGINFO)
-		setup_rt_frame(ka, regs, sig, oldset, info);
+		newsig = setup_rt_frame(ka, regs, sig, oldset, info);
 	else
-		setup_frame(ka, regs, sig, oldset);
+		newsig = setup_frame(ka, regs, sig, oldset);
+	if (newsig)
+		return newsig;
 
 	if (ka->sa.sa_flags & SA_ONESHOT)
 		ka->sa.sa_handler = SIG_DFL;
@@ -539,6 +544,7 @@
 		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 	}
+	return 0;
 }
 
 static inline void
@@ -672,8 +678,9 @@
 		if (regs->regs[0])
 			syscall_restart(regs, ka);
 		/* Whee!  Actually deliver the signal.  */
-		handle_signal(signr, ka, &info, oldset, regs);
-		return 1;
+		if (handle_signal(signr, ka, &info, oldset, regs) == 0)
+			return 1;
+		/* another signal queued.  continue. */
 	}
 
 	/*

[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux