aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m68k
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2010-10-06 14:09:43 -0400
committerGeert Uytterhoeven <geert@linux-m68k.org>2011-01-07 08:01:35 -0500
commitea52b58ccbda49aeb23eb424ce05bba3cb0bc976 (patch)
tree82455ff39095652a80a09457428cf1ed937b71c9 /arch/m68k
parent90731d7537317ad5d9672187f7a1dff90b29bb12 (diff)
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: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Diffstat (limited to 'arch/m68k')
-rw-r--r--arch/m68k/kernel/signal.c173
1 files changed, 61 insertions, 112 deletions
diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c
index 16ea319d1ed5..d5f4a82515f3 100644
--- a/arch/m68k/kernel/signal.c
+++ b/arch/m68k/kernel/signal.c
@@ -286,36 +286,10 @@ out:
286 return err; 286 return err;
287} 287}
288 288
289static inline int 289static int mangle_kernel_stack(struct pt_regs *regs, int formatvec,
290restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp, 290 void __user *fp)
291 int *pd0)
292{ 291{
293 int fsize, formatvec; 292 int fsize = frame_extra_sizes[formatvec >> 12];
294 struct sigcontext context;
295 int err;
296
297 /* Always make any pending restarted system calls return -EINTR */
298 current_thread_info()->restart_block.fn = do_no_restart_syscall;
299
300 /* get previous context */
301 if (copy_from_user(&context, usc, sizeof(context)))
302 goto badframe;
303
304 /* restore passed registers */
305 regs->d1 = context.sc_d1;
306 regs->a0 = context.sc_a0;
307 regs->a1 = context.sc_a1;
308 regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff);
309 regs->pc = context.sc_pc;
310 regs->orig_d0 = -1; /* disable syscall checks */
311 wrusp(context.sc_usp);
312 formatvec = context.sc_formatvec;
313 regs->format = formatvec >> 12;
314 regs->vector = formatvec & 0xfff;
315
316 err = restore_fpu_state(&context);
317
318 fsize = frame_extra_sizes[regs->format];
319 if (fsize < 0) { 293 if (fsize < 0) {
320 /* 294 /*
321 * user process trying to return with weird frame format 295 * 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
323#ifdef DEBUG 297#ifdef DEBUG
324 printk("user process returning with weird frame format\n"); 298 printk("user process returning with weird frame format\n");
325#endif 299#endif
326 goto badframe; 300 return 1;
327 } 301 }
302 if (!fsize) {
303 regs->format = formatvec >> 12;
304 regs->vector = formatvec & 0xfff;
305 } else {
306 struct switch_stack *sw = (struct switch_stack *)regs - 1;
307 unsigned long buf[fsize / 2]; /* yes, twice as much */
328 308
329 /* OK. Make room on the supervisor stack for the extra junk, 309 /* that'll make sure that expansion won't crap over data */
330 * if necessary. 310 if (copy_from_user(buf + fsize / 4, fp, fsize))
331 */ 311 return 1;
332 312
333 if (fsize) { 313 /* point of no return */
334 struct switch_stack *sw = (struct switch_stack *)regs - 1; 314 regs->format = formatvec >> 12;
335 regs->d0 = context.sc_d0; 315 regs->vector = formatvec & 0xfff;
336#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) 316#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
337 __asm__ __volatile__ 317 __asm__ __volatile__
338 (" movel %0,%/a0\n\t" 318 (" movel %0,%/a0\n\t"
@@ -344,30 +324,50 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u
344 " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */ 324 " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */
345 " lsrl #2,%1\n\t" 325 " lsrl #2,%1\n\t"
346 " subql #1,%1\n\t" 326 " subql #1,%1\n\t"
347 "2: movesl %4@+,%2\n\t" 327 /* copy to the gap we'd made */
348 "3: movel %2,%/a0@+\n\t" 328 "2: movel %4@+,%/a0@+\n\t"
349 " dbra %1,2b\n\t" 329 " dbra %1,2b\n\t"
350 " bral ret_from_signal\n" 330 " bral ret_from_signal\n"
351 "4:\n"
352 ".section __ex_table,\"a\"\n"
353 " .align 4\n"
354 " .long 2b,4b\n"
355 " .long 3b,4b\n"
356 ".previous"
357 : /* no outputs, it doesn't ever return */ 331 : /* no outputs, it doesn't ever return */
358 : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), 332 : "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
359 "n" (frame_offset), "a" (fp) 333 "n" (frame_offset), "a" (buf + fsize/4)
360 : "a0"); 334 : "a0");
361#undef frame_offset 335#undef frame_offset
362 /*
363 * If we ever get here an exception occurred while
364 * building the above stack-frame.
365 */
366 goto badframe;
367 } 336 }
337 return 0;
338}
368 339
369 *pd0 = context.sc_d0; 340static inline int
370 return err; 341restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp)
342{
343 int formatvec;
344 struct sigcontext context;
345 int err;
346
347 /* Always make any pending restarted system calls return -EINTR */
348 current_thread_info()->restart_block.fn = do_no_restart_syscall;
349
350 /* get previous context */
351 if (copy_from_user(&context, usc, sizeof(context)))
352 goto badframe;
353
354 /* restore passed registers */
355 regs->d0 = context.sc_d0;
356 regs->d1 = context.sc_d1;
357 regs->a0 = context.sc_a0;
358 regs->a1 = context.sc_a1;
359 regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff);
360 regs->pc = context.sc_pc;
361 regs->orig_d0 = -1; /* disable syscall checks */
362 wrusp(context.sc_usp);
363 formatvec = context.sc_formatvec;
364
365 err = restore_fpu_state(&context);
366
367 if (err || mangle_kernel_stack(regs, formatvec, fp))
368 goto badframe;
369
370 return 0;
371 371
372badframe: 372badframe:
373 return 1; 373 return 1;
@@ -375,9 +375,9 @@ badframe:
375 375
376static inline int 376static inline int
377rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, 377rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,
378 struct ucontext __user *uc, int *pd0) 378 struct ucontext __user *uc)
379{ 379{
380 int fsize, temp; 380 int temp;
381 greg_t __user *gregs = uc->uc_mcontext.gregs; 381 greg_t __user *gregs = uc->uc_mcontext.gregs;
382 unsigned long usp; 382 unsigned long usp;
383 int err; 383 int err;
@@ -411,65 +411,16 @@ rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,
411 regs->sr = (regs->sr & 0xff00) | (temp & 0xff); 411 regs->sr = (regs->sr & 0xff00) | (temp & 0xff);
412 regs->orig_d0 = -1; /* disable syscall checks */ 412 regs->orig_d0 = -1; /* disable syscall checks */
413 err |= __get_user(temp, &uc->uc_formatvec); 413 err |= __get_user(temp, &uc->uc_formatvec);
414 regs->format = temp >> 12;
415 regs->vector = temp & 0xfff;
416 414
417 err |= rt_restore_fpu_state(uc); 415 err |= rt_restore_fpu_state(uc);
418 416
419 if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) 417 if (err || do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT)
420 goto badframe; 418 goto badframe;
421 419
422 fsize = frame_extra_sizes[regs->format]; 420 if (mangle_kernel_stack(regs, temp, &uc->uc_extra))
423 if (fsize < 0) {
424 /*
425 * user process trying to return with weird frame format
426 */
427#ifdef DEBUG
428 printk("user process returning with weird frame format\n");
429#endif
430 goto badframe; 421 goto badframe;
431 }
432 422
433 /* OK. Make room on the supervisor stack for the extra junk, 423 return 0;
434 * if necessary.
435 */
436
437 if (fsize) {
438#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
439 __asm__ __volatile__
440 (" movel %0,%/a0\n\t"
441 " subl %1,%/a0\n\t" /* make room on stack */
442 " movel %/a0,%/sp\n\t" /* set stack pointer */
443 /* move switch_stack and pt_regs */
444 "1: movel %0@+,%/a0@+\n\t"
445 " dbra %2,1b\n\t"
446 " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */
447 " lsrl #2,%1\n\t"
448 " subql #1,%1\n\t"
449 "2: movesl %4@+,%2\n\t"
450 "3: movel %2,%/a0@+\n\t"
451 " dbra %1,2b\n\t"
452 " bral ret_from_signal\n"
453 "4:\n"
454 ".section __ex_table,\"a\"\n"
455 " .align 4\n"
456 " .long 2b,4b\n"
457 " .long 3b,4b\n"
458 ".previous"
459 : /* no outputs, it doesn't ever return */
460 : "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
461 "n" (frame_offset), "a" (&uc->uc_extra)
462 : "a0");
463#undef frame_offset
464 /*
465 * If we ever get here an exception occurred while
466 * building the above stack-frame.
467 */
468 goto badframe;
469 }
470
471 *pd0 = regs->d0;
472 return err;
473 424
474badframe: 425badframe:
475 return 1; 426 return 1;
@@ -482,7 +433,6 @@ asmlinkage int do_sigreturn(unsigned long __unused)
482 unsigned long usp = rdusp(); 433 unsigned long usp = rdusp();
483 struct sigframe __user *frame = (struct sigframe __user *)(usp - 4); 434 struct sigframe __user *frame = (struct sigframe __user *)(usp - 4);
484 sigset_t set; 435 sigset_t set;
485 int d0;
486 436
487 if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) 437 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
488 goto badframe; 438 goto badframe;
@@ -496,9 +446,9 @@ asmlinkage int do_sigreturn(unsigned long __unused)
496 current->blocked = set; 446 current->blocked = set;
497 recalc_sigpending(); 447 recalc_sigpending();
498 448
499 if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0)) 449 if (restore_sigcontext(regs, &frame->sc, frame + 1))
500 goto badframe; 450 goto badframe;
501 return d0; 451 return regs->d0;
502 452
503badframe: 453badframe:
504 force_sig(SIGSEGV, current); 454 force_sig(SIGSEGV, current);
@@ -512,7 +462,6 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused)
512 unsigned long usp = rdusp(); 462 unsigned long usp = rdusp();
513 struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4); 463 struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4);
514 sigset_t set; 464 sigset_t set;
515 int d0;
516 465
517 if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) 466 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
518 goto badframe; 467 goto badframe;
@@ -523,9 +472,9 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused)
523 current->blocked = set; 472 current->blocked = set;
524 recalc_sigpending(); 473 recalc_sigpending();
525 474
526 if (rt_restore_ucontext(regs, sw, &frame->uc, &d0)) 475 if (rt_restore_ucontext(regs, sw, &frame->uc))
527 goto badframe; 476 goto badframe;
528 return d0; 477 return regs->d0;
529 478
530badframe: 479badframe:
531 force_sig(SIGSEGV, current); 480 force_sig(SIGSEGV, current);