Commit ea52b58c authored by Al Viro's avatar Al Viro Committed by Geert Uytterhoeven
Browse files

m68k: Fix stack mangling logics in sigreturn



a) we should hold modifying regs->format until we know we *will* be
doing stack expansion; otherwise attacker can modify sigframe to
have wrong ->sc_formatvec and install SIGSEGV handler.

b) we should *not* mix copying saved extra stuff from userland with
expanding the stack; once we'd done that manual memmove, we'd better
not return to C, so cleanup is very hard to do.  The easiest way
is to copy it on stack first, making sure we won't overwrite on stack
expansion.  Fortunately that's easy to do...

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
parent 90731d75
Loading
Loading
Loading
Loading
+61 −112
Original line number Original line Diff line number Diff line
@@ -286,36 +286,10 @@ out:
	return err;
	return err;
}
}


static inline int
static int mangle_kernel_stack(struct pt_regs *regs, int formatvec,
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp,
			       void __user *fp)
		   int *pd0)
{
{
	int fsize, formatvec;
	int fsize = frame_extra_sizes[formatvec >> 12];
	struct sigcontext context;
	int err;

	/* Always make any pending restarted system calls return -EINTR */
	current_thread_info()->restart_block.fn = do_no_restart_syscall;

	/* get previous context */
	if (copy_from_user(&context, usc, sizeof(context)))
		goto badframe;

	/* restore passed registers */
	regs->d1 = context.sc_d1;
	regs->a0 = context.sc_a0;
	regs->a1 = context.sc_a1;
	regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff);
	regs->pc = context.sc_pc;
	regs->orig_d0 = -1;		/* disable syscall checks */
	wrusp(context.sc_usp);
	formatvec = context.sc_formatvec;
	regs->format = formatvec >> 12;
	regs->vector = formatvec & 0xfff;

	err = restore_fpu_state(&context);

	fsize = frame_extra_sizes[regs->format];
	if (fsize < 0) {
	if (fsize < 0) {
		/*
		/*
		 * user process trying to return with weird frame format
		 * user process trying to return with weird frame format
@@ -323,16 +297,22 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u
#ifdef DEBUG
#ifdef DEBUG
		printk("user process returning with weird frame format\n");
		printk("user process returning with weird frame format\n");
#endif
#endif
		goto badframe;
		return 1;
	}
	}
	if (!fsize) {
		regs->format = formatvec >> 12;
		regs->vector = formatvec & 0xfff;
	} else {
		struct switch_stack *sw = (struct switch_stack *)regs - 1;
		unsigned long buf[fsize / 2]; /* yes, twice as much */


	/* OK.	Make room on the supervisor stack for the extra junk,
		/* that'll make sure that expansion won't crap over data */
	 * if necessary.
		if (copy_from_user(buf + fsize / 4, fp, fsize))
	 */
			return 1;


	if (fsize) {
		/* point of no return */
		struct switch_stack *sw = (struct switch_stack *)regs - 1;
		regs->format = formatvec >> 12;
		regs->d0 = context.sc_d0;
		regs->vector = formatvec & 0xfff;
#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
		__asm__ __volatile__
		__asm__ __volatile__
			("   movel %0,%/a0\n\t"
			("   movel %0,%/a0\n\t"
@@ -344,30 +324,50 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u
			 "   lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */
			 "   lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */
			 "   lsrl  #2,%1\n\t"
			 "   lsrl  #2,%1\n\t"
			 "   subql #1,%1\n\t"
			 "   subql #1,%1\n\t"
			 "2: movesl %4@+,%2\n\t"
			 /* copy to the gap we'd made */
			 "3: movel %2,%/a0@+\n\t"
			 "2: movel %4@+,%/a0@+\n\t"
			 "   dbra %1,2b\n\t"
			 "   dbra %1,2b\n\t"
			 "   bral ret_from_signal\n"
			 "   bral ret_from_signal\n"
			 "4:\n"
			 ".section __ex_table,\"a\"\n"
			 "   .align 4\n"
			 "   .long 2b,4b\n"
			 "   .long 3b,4b\n"
			 ".previous"
			 : /* no outputs, it doesn't ever return */
			 : /* no outputs, it doesn't ever return */
			 : "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
			 : "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
			   "n" (frame_offset), "a" (fp)
			   "n" (frame_offset), "a" (buf + fsize/4)
			 : "a0");
			 : "a0");
#undef frame_offset
#undef frame_offset
		/*
	}
		 * If we ever get here an exception occurred while
	return 0;
		 * building the above stack-frame.
		 */
		goto badframe;
}
}


	*pd0 = context.sc_d0;
static inline int
	return err;
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp)
{
	int formatvec;
	struct sigcontext context;
	int err;

	/* Always make any pending restarted system calls return -EINTR */
	current_thread_info()->restart_block.fn = do_no_restart_syscall;

	/* get previous context */
	if (copy_from_user(&context, usc, sizeof(context)))
		goto badframe;

	/* restore passed registers */
	regs->d0 = context.sc_d0;
	regs->d1 = context.sc_d1;
	regs->a0 = context.sc_a0;
	regs->a1 = context.sc_a1;
	regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff);
	regs->pc = context.sc_pc;
	regs->orig_d0 = -1;		/* disable syscall checks */
	wrusp(context.sc_usp);
	formatvec = context.sc_formatvec;

	err = restore_fpu_state(&context);

	if (err || mangle_kernel_stack(regs, formatvec, fp))
		goto badframe;

	return 0;


badframe:
badframe:
	return 1;
	return 1;
@@ -375,9 +375,9 @@ badframe:


static inline int
static inline int
rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,
rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,
		    struct ucontext __user *uc, int *pd0)
		    struct ucontext __user *uc)
{
{
	int fsize, temp;
	int temp;
	greg_t __user *gregs = uc->uc_mcontext.gregs;
	greg_t __user *gregs = uc->uc_mcontext.gregs;
	unsigned long usp;
	unsigned long usp;
	int err;
	int err;
@@ -411,65 +411,16 @@ rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,
	regs->sr = (regs->sr & 0xff00) | (temp & 0xff);
	regs->sr = (regs->sr & 0xff00) | (temp & 0xff);
	regs->orig_d0 = -1;		/* disable syscall checks */
	regs->orig_d0 = -1;		/* disable syscall checks */
	err |= __get_user(temp, &uc->uc_formatvec);
	err |= __get_user(temp, &uc->uc_formatvec);
	regs->format = temp >> 12;
	regs->vector = temp & 0xfff;


	err |= rt_restore_fpu_state(uc);
	err |= rt_restore_fpu_state(uc);


	if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT)
	if (err || do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT)
		goto badframe;

	fsize = frame_extra_sizes[regs->format];
	if (fsize < 0) {
		/*
		 * user process trying to return with weird frame format
		 */
#ifdef DEBUG
		printk("user process returning with weird frame format\n");
#endif
		goto badframe;
		goto badframe;
	}

	/* OK.	Make room on the supervisor stack for the extra junk,
	 * if necessary.
	 */


	if (fsize) {
	if (mangle_kernel_stack(regs, temp, &uc->uc_extra))
#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
		__asm__ __volatile__
			("   movel %0,%/a0\n\t"
			 "   subl %1,%/a0\n\t"     /* make room on stack */
			 "   movel %/a0,%/sp\n\t"  /* set stack pointer */
			 /* move switch_stack and pt_regs */
			 "1: movel %0@+,%/a0@+\n\t"
			 "   dbra %2,1b\n\t"
			 "   lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */
			 "   lsrl  #2,%1\n\t"
			 "   subql #1,%1\n\t"
			 "2: movesl %4@+,%2\n\t"
			 "3: movel %2,%/a0@+\n\t"
			 "   dbra %1,2b\n\t"
			 "   bral ret_from_signal\n"
			 "4:\n"
			 ".section __ex_table,\"a\"\n"
			 "   .align 4\n"
			 "   .long 2b,4b\n"
			 "   .long 3b,4b\n"
			 ".previous"
			 : /* no outputs, it doesn't ever return */
			 : "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
			   "n" (frame_offset), "a" (&uc->uc_extra)
			 : "a0");
#undef frame_offset
		/*
		 * If we ever get here an exception occurred while
		 * building the above stack-frame.
		 */
		goto badframe;
		goto badframe;
	}


	*pd0 = regs->d0;
	return 0;
	return err;


badframe:
badframe:
	return 1;
	return 1;
@@ -482,7 +433,6 @@ asmlinkage int do_sigreturn(unsigned long __unused)
	unsigned long usp = rdusp();
	unsigned long usp = rdusp();
	struct sigframe __user *frame = (struct sigframe __user *)(usp - 4);
	struct sigframe __user *frame = (struct sigframe __user *)(usp - 4);
	sigset_t set;
	sigset_t set;
	int d0;


	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
		goto badframe;
		goto badframe;
@@ -496,9 +446,9 @@ asmlinkage int do_sigreturn(unsigned long __unused)
	current->blocked = set;
	current->blocked = set;
	recalc_sigpending();
	recalc_sigpending();


	if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0))
	if (restore_sigcontext(regs, &frame->sc, frame + 1))
		goto badframe;
		goto badframe;
	return d0;
	return regs->d0;


badframe:
badframe:
	force_sig(SIGSEGV, current);
	force_sig(SIGSEGV, current);
@@ -512,7 +462,6 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused)
	unsigned long usp = rdusp();
	unsigned long usp = rdusp();
	struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4);
	struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4);
	sigset_t set;
	sigset_t set;
	int d0;


	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
		goto badframe;
		goto badframe;
@@ -523,9 +472,9 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused)
	current->blocked = set;
	current->blocked = set;
	recalc_sigpending();
	recalc_sigpending();


	if (rt_restore_ucontext(regs, sw, &frame->uc, &d0))
	if (rt_restore_ucontext(regs, sw, &frame->uc))
		goto badframe;
		goto badframe;
	return d0;
	return regs->d0;


badframe:
badframe:
	force_sig(SIGSEGV, current);
	force_sig(SIGSEGV, current);