diff options
Diffstat (limited to 'arch/x86/kernel/signal_32.c')
-rw-r--r-- | arch/x86/kernel/signal_32.c | 222 |
1 files changed, 134 insertions, 88 deletions
diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c index b21070ea33a4..d6dd057d0f22 100644 --- a/arch/x86/kernel/signal_32.c +++ b/arch/x86/kernel/signal_32.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
28 | #include <asm/i387.h> | 28 | #include <asm/i387.h> |
29 | #include <asm/vdso.h> | 29 | #include <asm/vdso.h> |
30 | #include <asm/syscall.h> | ||
30 | #include <asm/syscalls.h> | 31 | #include <asm/syscalls.h> |
31 | 32 | ||
32 | #include "sigframe.h" | 33 | #include "sigframe.h" |
@@ -112,6 +113,27 @@ asmlinkage int sys_sigaltstack(unsigned long bx) | |||
112 | return do_sigaltstack(uss, uoss, regs->sp); | 113 | return do_sigaltstack(uss, uoss, regs->sp); |
113 | } | 114 | } |
114 | 115 | ||
116 | #define COPY(x) { \ | ||
117 | err |= __get_user(regs->x, &sc->x); \ | ||
118 | } | ||
119 | |||
120 | #define COPY_SEG(seg) { \ | ||
121 | unsigned short tmp; \ | ||
122 | err |= __get_user(tmp, &sc->seg); \ | ||
123 | regs->seg = tmp; \ | ||
124 | } | ||
125 | |||
126 | #define COPY_SEG_STRICT(seg) { \ | ||
127 | unsigned short tmp; \ | ||
128 | err |= __get_user(tmp, &sc->seg); \ | ||
129 | regs->seg = tmp | 3; \ | ||
130 | } | ||
131 | |||
132 | #define GET_SEG(seg) { \ | ||
133 | unsigned short tmp; \ | ||
134 | err |= __get_user(tmp, &sc->seg); \ | ||
135 | loadsegment(seg, tmp); \ | ||
136 | } | ||
115 | 137 | ||
116 | /* | 138 | /* |
117 | * Do a signal return; undo the signal stack. | 139 | * Do a signal return; undo the signal stack. |
@@ -120,28 +142,13 @@ static int | |||
120 | restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, | 142 | restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, |
121 | unsigned long *pax) | 143 | unsigned long *pax) |
122 | { | 144 | { |
145 | void __user *buf; | ||
146 | unsigned int tmpflags; | ||
123 | unsigned int err = 0; | 147 | unsigned int err = 0; |
124 | 148 | ||
125 | /* Always make any pending restarted system calls return -EINTR */ | 149 | /* Always make any pending restarted system calls return -EINTR */ |
126 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | 150 | current_thread_info()->restart_block.fn = do_no_restart_syscall; |
127 | 151 | ||
128 | #define COPY(x) err |= __get_user(regs->x, &sc->x) | ||
129 | |||
130 | #define COPY_SEG(seg) \ | ||
131 | { unsigned short tmp; \ | ||
132 | err |= __get_user(tmp, &sc->seg); \ | ||
133 | regs->seg = tmp; } | ||
134 | |||
135 | #define COPY_SEG_STRICT(seg) \ | ||
136 | { unsigned short tmp; \ | ||
137 | err |= __get_user(tmp, &sc->seg); \ | ||
138 | regs->seg = tmp|3; } | ||
139 | |||
140 | #define GET_SEG(seg) \ | ||
141 | { unsigned short tmp; \ | ||
142 | err |= __get_user(tmp, &sc->seg); \ | ||
143 | loadsegment(seg, tmp); } | ||
144 | |||
145 | GET_SEG(gs); | 152 | GET_SEG(gs); |
146 | COPY_SEG(fs); | 153 | COPY_SEG(fs); |
147 | COPY_SEG(es); | 154 | COPY_SEG(es); |
@@ -151,21 +158,12 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, | |||
151 | COPY_SEG_STRICT(cs); | 158 | COPY_SEG_STRICT(cs); |
152 | COPY_SEG_STRICT(ss); | 159 | COPY_SEG_STRICT(ss); |
153 | 160 | ||
154 | { | 161 | err |= __get_user(tmpflags, &sc->flags); |
155 | unsigned int tmpflags; | 162 | regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); |
156 | 163 | regs->orig_ax = -1; /* disable syscall checks */ | |
157 | err |= __get_user(tmpflags, &sc->flags); | ||
158 | regs->flags = (regs->flags & ~FIX_EFLAGS) | | ||
159 | (tmpflags & FIX_EFLAGS); | ||
160 | regs->orig_ax = -1; /* disable syscall checks */ | ||
161 | } | ||
162 | |||
163 | { | ||
164 | void __user *buf; | ||
165 | 164 | ||
166 | err |= __get_user(buf, &sc->fpstate); | 165 | err |= __get_user(buf, &sc->fpstate); |
167 | err |= restore_i387_xstate(buf); | 166 | err |= restore_i387_xstate(buf); |
168 | } | ||
169 | 167 | ||
170 | err |= __get_user(*pax, &sc->ax); | 168 | err |= __get_user(*pax, &sc->ax); |
171 | return err; | 169 | return err; |
@@ -214,9 +212,8 @@ badframe: | |||
214 | return 0; | 212 | return 0; |
215 | } | 213 | } |
216 | 214 | ||
217 | asmlinkage int sys_rt_sigreturn(unsigned long __unused) | 215 | static long do_rt_sigreturn(struct pt_regs *regs) |
218 | { | 216 | { |
219 | struct pt_regs *regs = (struct pt_regs *)&__unused; | ||
220 | struct rt_sigframe __user *frame; | 217 | struct rt_sigframe __user *frame; |
221 | unsigned long ax; | 218 | unsigned long ax; |
222 | sigset_t set; | 219 | sigset_t set; |
@@ -242,10 +239,17 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused) | |||
242 | return ax; | 239 | return ax; |
243 | 240 | ||
244 | badframe: | 241 | badframe: |
245 | force_sig(SIGSEGV, current); | 242 | signal_fault(regs, frame, "rt_sigreturn"); |
246 | return 0; | 243 | return 0; |
247 | } | 244 | } |
248 | 245 | ||
246 | asmlinkage int sys_rt_sigreturn(unsigned long __unused) | ||
247 | { | ||
248 | struct pt_regs *regs = (struct pt_regs *)&__unused; | ||
249 | |||
250 | return do_rt_sigreturn(regs); | ||
251 | } | ||
252 | |||
249 | /* | 253 | /* |
250 | * Set up a signal frame. | 254 | * Set up a signal frame. |
251 | */ | 255 | */ |
@@ -337,39 +341,29 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, | |||
337 | } | 341 | } |
338 | 342 | ||
339 | static int | 343 | static int |
340 | setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, | 344 | __setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, |
341 | struct pt_regs *regs) | 345 | struct pt_regs *regs) |
342 | { | 346 | { |
343 | struct sigframe __user *frame; | 347 | struct sigframe __user *frame; |
344 | void __user *restorer; | 348 | void __user *restorer; |
345 | int err = 0; | 349 | int err = 0; |
346 | int usig; | ||
347 | void __user *fpstate = NULL; | 350 | void __user *fpstate = NULL; |
348 | 351 | ||
349 | frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); | 352 | frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); |
350 | 353 | ||
351 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 354 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) |
352 | goto give_sigsegv; | 355 | return -EFAULT; |
353 | 356 | ||
354 | usig = current_thread_info()->exec_domain | 357 | if (__put_user(sig, &frame->sig)) |
355 | && current_thread_info()->exec_domain->signal_invmap | 358 | return -EFAULT; |
356 | && sig < 32 | ||
357 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
358 | : sig; | ||
359 | 359 | ||
360 | err = __put_user(usig, &frame->sig); | 360 | if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0])) |
361 | if (err) | 361 | return -EFAULT; |
362 | goto give_sigsegv; | ||
363 | |||
364 | err = setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]); | ||
365 | if (err) | ||
366 | goto give_sigsegv; | ||
367 | 362 | ||
368 | if (_NSIG_WORDS > 1) { | 363 | if (_NSIG_WORDS > 1) { |
369 | err = __copy_to_user(&frame->extramask, &set->sig[1], | 364 | if (__copy_to_user(&frame->extramask, &set->sig[1], |
370 | sizeof(frame->extramask)); | 365 | sizeof(frame->extramask))) |
371 | if (err) | 366 | return -EFAULT; |
372 | goto give_sigsegv; | ||
373 | } | 367 | } |
374 | 368 | ||
375 | if (current->mm->context.vdso) | 369 | if (current->mm->context.vdso) |
@@ -394,7 +388,7 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, | |||
394 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+6)); | 388 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+6)); |
395 | 389 | ||
396 | if (err) | 390 | if (err) |
397 | goto give_sigsegv; | 391 | return -EFAULT; |
398 | 392 | ||
399 | /* Set up registers for signal handler */ | 393 | /* Set up registers for signal handler */ |
400 | regs->sp = (unsigned long)frame; | 394 | regs->sp = (unsigned long)frame; |
@@ -409,38 +403,27 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, | |||
409 | regs->cs = __USER_CS; | 403 | regs->cs = __USER_CS; |
410 | 404 | ||
411 | return 0; | 405 | return 0; |
412 | |||
413 | give_sigsegv: | ||
414 | force_sigsegv(sig, current); | ||
415 | return -EFAULT; | ||
416 | } | 406 | } |
417 | 407 | ||
418 | static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | 408 | static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, |
419 | sigset_t *set, struct pt_regs *regs) | 409 | sigset_t *set, struct pt_regs *regs) |
420 | { | 410 | { |
421 | struct rt_sigframe __user *frame; | 411 | struct rt_sigframe __user *frame; |
422 | void __user *restorer; | 412 | void __user *restorer; |
423 | int err = 0; | 413 | int err = 0; |
424 | int usig; | ||
425 | void __user *fpstate = NULL; | 414 | void __user *fpstate = NULL; |
426 | 415 | ||
427 | frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); | 416 | frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); |
428 | 417 | ||
429 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 418 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) |
430 | goto give_sigsegv; | 419 | return -EFAULT; |
431 | |||
432 | usig = current_thread_info()->exec_domain | ||
433 | && current_thread_info()->exec_domain->signal_invmap | ||
434 | && sig < 32 | ||
435 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
436 | : sig; | ||
437 | 420 | ||
438 | err |= __put_user(usig, &frame->sig); | 421 | err |= __put_user(sig, &frame->sig); |
439 | err |= __put_user(&frame->info, &frame->pinfo); | 422 | err |= __put_user(&frame->info, &frame->pinfo); |
440 | err |= __put_user(&frame->uc, &frame->puc); | 423 | err |= __put_user(&frame->uc, &frame->puc); |
441 | err |= copy_siginfo_to_user(&frame->info, info); | 424 | err |= copy_siginfo_to_user(&frame->info, info); |
442 | if (err) | 425 | if (err) |
443 | goto give_sigsegv; | 426 | return -EFAULT; |
444 | 427 | ||
445 | /* Create the ucontext. */ | 428 | /* Create the ucontext. */ |
446 | if (cpu_has_xsave) | 429 | if (cpu_has_xsave) |
@@ -456,7 +439,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
456 | regs, set->sig[0]); | 439 | regs, set->sig[0]); |
457 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | 440 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); |
458 | if (err) | 441 | if (err) |
459 | goto give_sigsegv; | 442 | return -EFAULT; |
460 | 443 | ||
461 | /* Set up to return from userspace. */ | 444 | /* Set up to return from userspace. */ |
462 | restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn); | 445 | restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn); |
@@ -476,12 +459,12 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
476 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+5)); | 459 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+5)); |
477 | 460 | ||
478 | if (err) | 461 | if (err) |
479 | goto give_sigsegv; | 462 | return -EFAULT; |
480 | 463 | ||
481 | /* Set up registers for signal handler */ | 464 | /* Set up registers for signal handler */ |
482 | regs->sp = (unsigned long)frame; | 465 | regs->sp = (unsigned long)frame; |
483 | regs->ip = (unsigned long)ka->sa.sa_handler; | 466 | regs->ip = (unsigned long)ka->sa.sa_handler; |
484 | regs->ax = (unsigned long)usig; | 467 | regs->ax = (unsigned long)sig; |
485 | regs->dx = (unsigned long)&frame->info; | 468 | regs->dx = (unsigned long)&frame->info; |
486 | regs->cx = (unsigned long)&frame->uc; | 469 | regs->cx = (unsigned long)&frame->uc; |
487 | 470 | ||
@@ -491,15 +474,48 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
491 | regs->cs = __USER_CS; | 474 | regs->cs = __USER_CS; |
492 | 475 | ||
493 | return 0; | 476 | return 0; |
494 | |||
495 | give_sigsegv: | ||
496 | force_sigsegv(sig, current); | ||
497 | return -EFAULT; | ||
498 | } | 477 | } |
499 | 478 | ||
500 | /* | 479 | /* |
501 | * OK, we're invoking a handler: | 480 | * OK, we're invoking a handler: |
502 | */ | 481 | */ |
482 | static int signr_convert(int sig) | ||
483 | { | ||
484 | struct thread_info *info = current_thread_info(); | ||
485 | |||
486 | if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32) | ||
487 | return info->exec_domain->signal_invmap[sig]; | ||
488 | return sig; | ||
489 | } | ||
490 | |||
491 | #define is_ia32 1 | ||
492 | #define ia32_setup_frame __setup_frame | ||
493 | #define ia32_setup_rt_frame __setup_rt_frame | ||
494 | |||
495 | static int | ||
496 | setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
497 | sigset_t *set, struct pt_regs *regs) | ||
498 | { | ||
499 | int usig = signr_convert(sig); | ||
500 | int ret; | ||
501 | |||
502 | /* Set up the stack frame */ | ||
503 | if (is_ia32) { | ||
504 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
505 | ret = ia32_setup_rt_frame(usig, ka, info, set, regs); | ||
506 | else | ||
507 | ret = ia32_setup_frame(usig, ka, set, regs); | ||
508 | } else | ||
509 | ret = __setup_rt_frame(sig, ka, info, set, regs); | ||
510 | |||
511 | if (ret) { | ||
512 | force_sigsegv(sig, current); | ||
513 | return -EFAULT; | ||
514 | } | ||
515 | |||
516 | return ret; | ||
517 | } | ||
518 | |||
503 | static int | 519 | static int |
504 | handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | 520 | handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, |
505 | sigset_t *oldset, struct pt_regs *regs) | 521 | sigset_t *oldset, struct pt_regs *regs) |
@@ -507,9 +523,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
507 | int ret; | 523 | int ret; |
508 | 524 | ||
509 | /* Are we from a system call? */ | 525 | /* Are we from a system call? */ |
510 | if ((long)regs->orig_ax >= 0) { | 526 | if (syscall_get_nr(current, regs) >= 0) { |
511 | /* If so, check system call restarting.. */ | 527 | /* If so, check system call restarting.. */ |
512 | switch (regs->ax) { | 528 | switch (syscall_get_error(current, regs)) { |
513 | case -ERESTART_RESTARTBLOCK: | 529 | case -ERESTART_RESTARTBLOCK: |
514 | case -ERESTARTNOHAND: | 530 | case -ERESTARTNOHAND: |
515 | regs->ax = -EINTR; | 531 | regs->ax = -EINTR; |
@@ -536,15 +552,20 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
536 | likely(test_and_clear_thread_flag(TIF_FORCED_TF))) | 552 | likely(test_and_clear_thread_flag(TIF_FORCED_TF))) |
537 | regs->flags &= ~X86_EFLAGS_TF; | 553 | regs->flags &= ~X86_EFLAGS_TF; |
538 | 554 | ||
539 | /* Set up the stack frame */ | 555 | ret = setup_rt_frame(sig, ka, info, oldset, regs); |
540 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
541 | ret = setup_rt_frame(sig, ka, info, oldset, regs); | ||
542 | else | ||
543 | ret = setup_frame(sig, ka, oldset, regs); | ||
544 | 556 | ||
545 | if (ret) | 557 | if (ret) |
546 | return ret; | 558 | return ret; |
547 | 559 | ||
560 | #ifdef CONFIG_X86_64 | ||
561 | /* | ||
562 | * This has nothing to do with segment registers, | ||
563 | * despite the name. This magic affects uaccess.h | ||
564 | * macros' behavior. Reset it to the normal setting. | ||
565 | */ | ||
566 | set_fs(USER_DS); | ||
567 | #endif | ||
568 | |||
548 | /* | 569 | /* |
549 | * Clear the direction flag as per the ABI for function entry. | 570 | * Clear the direction flag as per the ABI for function entry. |
550 | */ | 571 | */ |
@@ -571,6 +592,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
571 | return 0; | 592 | return 0; |
572 | } | 593 | } |
573 | 594 | ||
595 | #define NR_restart_syscall __NR_restart_syscall | ||
574 | /* | 596 | /* |
575 | * Note that 'init' is a special process: it doesn't get signals it doesn't | 597 | * Note that 'init' is a special process: it doesn't get signals it doesn't |
576 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | 598 | * want to handle. Thus you cannot kill init even with a SIGKILL even by |
@@ -623,9 +645,9 @@ static void do_signal(struct pt_regs *regs) | |||
623 | } | 645 | } |
624 | 646 | ||
625 | /* Did we come from a system call? */ | 647 | /* Did we come from a system call? */ |
626 | if ((long)regs->orig_ax >= 0) { | 648 | if (syscall_get_nr(current, regs) >= 0) { |
627 | /* Restart the system call - no handlers present */ | 649 | /* Restart the system call - no handlers present */ |
628 | switch (regs->ax) { | 650 | switch (syscall_get_error(current, regs)) { |
629 | case -ERESTARTNOHAND: | 651 | case -ERESTARTNOHAND: |
630 | case -ERESTARTSYS: | 652 | case -ERESTARTSYS: |
631 | case -ERESTARTNOINTR: | 653 | case -ERESTARTNOINTR: |
@@ -634,7 +656,7 @@ static void do_signal(struct pt_regs *regs) | |||
634 | break; | 656 | break; |
635 | 657 | ||
636 | case -ERESTART_RESTARTBLOCK: | 658 | case -ERESTART_RESTARTBLOCK: |
637 | regs->ax = __NR_restart_syscall; | 659 | regs->ax = NR_restart_syscall; |
638 | regs->ip -= 2; | 660 | regs->ip -= 2; |
639 | break; | 661 | break; |
640 | } | 662 | } |
@@ -657,6 +679,12 @@ static void do_signal(struct pt_regs *regs) | |||
657 | void | 679 | void |
658 | do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) | 680 | do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) |
659 | { | 681 | { |
682 | #if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE) | ||
683 | /* notify userspace of pending MCEs */ | ||
684 | if (thread_info_flags & _TIF_MCE_NOTIFY) | ||
685 | mce_notify_user(); | ||
686 | #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ | ||
687 | |||
660 | /* deal with pending signal delivery */ | 688 | /* deal with pending signal delivery */ |
661 | if (thread_info_flags & _TIF_SIGPENDING) | 689 | if (thread_info_flags & _TIF_SIGPENDING) |
662 | do_signal(regs); | 690 | do_signal(regs); |
@@ -666,5 +694,23 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) | |||
666 | tracehook_notify_resume(regs); | 694 | tracehook_notify_resume(regs); |
667 | } | 695 | } |
668 | 696 | ||
697 | #ifdef CONFIG_X86_32 | ||
669 | clear_thread_flag(TIF_IRET); | 698 | clear_thread_flag(TIF_IRET); |
699 | #endif /* CONFIG_X86_32 */ | ||
700 | } | ||
701 | |||
702 | void signal_fault(struct pt_regs *regs, void __user *frame, char *where) | ||
703 | { | ||
704 | struct task_struct *me = current; | ||
705 | |||
706 | if (show_unhandled_signals && printk_ratelimit()) { | ||
707 | printk(KERN_INFO | ||
708 | "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx", | ||
709 | me->comm, me->pid, where, frame, | ||
710 | regs->ip, regs->sp, regs->orig_ax); | ||
711 | print_vma_addr(" in ", regs->ip); | ||
712 | printk(KERN_CONT "\n"); | ||
713 | } | ||
714 | |||
715 | force_sig(SIGSEGV, me); | ||
670 | } | 716 | } |