patch-2.2.18 linux/arch/arm/kernel/signal.c

Next file: linux/arch/arm/kernel/sys_arm.c
Previous file: linux/arch/arm/kernel/setup.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c
@@ -28,7 +28,7 @@
 
 asmlinkage int sys_wait4(pid_t pid, unsigned long * stat_addr,
 			 int options, unsigned long *ru);
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs);
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
 extern int ptrace_cancel_bpt (struct task_struct *);
 extern int ptrace_set_bpt (struct task_struct *);
 
@@ -50,7 +50,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs, 0))
 			return regs->ARM_r0;
 	}
 }
@@ -78,7 +78,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs, 0))
 			return regs->ARM_r0;
 	}
 }
@@ -158,12 +158,8 @@
 #ifdef CONFIG_CPU_32
 	err |= __get_user(regs->ARM_cpsr, &sc->arm_cpsr);
 #endif
-	if (!valid_user_regs(regs))
-		return 1;
 
-	/* send SIGTRAP if we're single-stepping */
-	if (ptrace_cancel_bpt (current))
-		send_sig (SIGTRAP, current, 1);
+	err |= !valid_user_regs(regs);
 
 	return err;
 }
@@ -173,6 +169,14 @@
 	struct sigframe *frame;
 	sigset_t set;
 
+	/*
+	 * Since we stacked the signal on a word boundary,
+	 * then 'sp' should be word aligned here.  If it's
+	 * not, then the user is trying to mess with us.
+	 */
+	if (regs->ARM_sp & 3)
+		goto badframe;
+
 	frame = (struct sigframe *)regs->ARM_sp;
 
 	if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
@@ -192,6 +196,10 @@
 	if (restore_sigcontext(regs, &frame->sc))
 		goto badframe;
 
+	/* Send SIGTRAP if we're single-stepping */
+	if (ptrace_cancel_bpt (current))
+		send_sig(SIGTRAP, current, 1);
+
 	return regs->ARM_r0;
 
 badframe:
@@ -204,6 +212,14 @@
 	struct rt_sigframe *frame;
 	sigset_t set;
 
+	/*
+	 * Since we stacked the signal on a word boundary,
+	 * then 'sp' should be word aligned here.  If it's
+	 * not, then the user is trying to mess with us.
+	 */
+	if (regs->ARM_sp & 3)
+		goto badframe;
+
 	frame = (struct rt_sigframe *)regs->ARM_sp;
 
 	if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
@@ -220,6 +236,10 @@
 	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
 		goto badframe;
 
+	/* Send SIGTRAP if we're single-stepping */
+	if (ptrace_cancel_bpt (current))
+		send_sig(SIGTRAP, current, 1);
+
 	return regs->ARM_r0;
 
 badframe:
@@ -260,6 +280,26 @@
 	return err;
 }
 
+static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+				 unsigned long framesize)
+{
+	unsigned long sp = regs->ARM_sp;
+
+	/*
+	 * This is the X/Open sanctioned signal stack switching.
+	 */
+	if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp))
+		sp = current->sas_ss_sp + current->sas_ss_size;
+
+	/*
+	 * No matter what happens, 'sp' must be word
+	 * aligned otherwise nasty things could happen
+	 */
+	sp &= ~3;
+
+	return (void *)(sp - framesize);
+}
+
 static void setup_frame(int sig, struct k_sigaction *ka,
 			sigset_t *set, struct pt_regs *regs)
 {
@@ -267,9 +307,9 @@
 	unsigned long retcode;
 	int err = 0;
 
-	frame = (struct sigframe *)regs->ARM_sp - 1;
+	frame = get_sigframe(ka, regs, sizeof(*frame));
 
-	if (!access_ok(VERIFT_WRITE, frame, sizeof (*frame)))
+	if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
 		goto segv_and_exit;
 
 	err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
@@ -286,7 +326,7 @@
 	} else {
 		retcode = (unsigned long)&frame->retcode;
 		err |= __put_user(SWI_SYS_SIGRETURN, &frame->retcode);
-		__flush_entry_to_ram (&frame->retcode);
+		flush_icache_range(retcode, retcode + 4);
 	}
 
 	if (err)
@@ -299,6 +339,11 @@
 	regs->ARM_sp = (unsigned long)frame;
 	regs->ARM_lr = retcode;
 	regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+	/* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+	if (ka->sa.sa_flags & SA_THIRTYTWO)
+		regs->ARM_cpsr = USR_MODE;
+#endif
 	if (valid_user_regs(regs))
 		return;
 
@@ -315,7 +360,8 @@
 	unsigned long retcode;
 	int err = 0;
 
-	frame = (struct rt_sigframe *)regs->ARM_sp - 1;
+	frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe));
+
 	if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
 		goto segv_and_exit;
 
@@ -337,7 +383,7 @@
 	} else {
 		retcode = (unsigned long)&frame->retcode;
 		err |= __put_user(SWI_SYS_RT_SIGRETURN, &frame->retcode);
-		__flush_entry_to_ram (&frame->retcode);
+		flush_icache_range(retcode, retcode + 4);
 	}
 
 	if (err)
@@ -350,6 +396,11 @@
 	regs->ARM_sp = (unsigned long)frame;
 	regs->ARM_lr = retcode;
 	regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+	/* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+	if (ka->sa.sa_flags & SA_THIRTYTWO)
+		regs->ARM_cpsr = USR_MODE;
+#endif
 	if (valid_user_regs(regs))
 		return;
 
@@ -393,18 +444,25 @@
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
 {
-	unsigned long instr, *pc = (unsigned long *)(instruction_pointer(regs)-4);
 	struct k_sigaction *ka;
 	siginfo_t info;
-	int single_stepping, swi_instr;
+	int single_stepping;
+
+	/*
+	 * We want the common case to go fast, which
+	 * is why we may in certain cases get here from
+	 * kernel mode. Just return without doing anything
+	 * if so.
+	 */
+	if (!user_mode(regs))
+		return 0;
 
 	if (!oldset)
 		oldset = &current->blocked;
 
 	single_stepping = ptrace_cancel_bpt (current);
-	swi_instr = (!get_user (instr, pc) && (instr & 0x0f000000) == 0x0f000000);
 
 	for (;;) {
 		unsigned long signr;
@@ -503,7 +561,7 @@
 		}
 
 		/* Are we from a system call? */
-		if (swi_instr) {
+		if (syscall) {
 			switch (regs->ARM_r0) {
 			case -ERESTARTNOHAND:
 				regs->ARM_r0 = -EINTR;
@@ -527,7 +585,7 @@
 		return 1;
 	}
 
-	if (swi_instr &&
+	if (syscall &&
 	    (regs->ARM_r0 == -ERESTARTNOHAND ||
 	     regs->ARM_r0 == -ERESTARTSYS ||
 	     regs->ARM_r0 == -ERESTARTNOINTR)) {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)