diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-08-03 05:30:05 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-11 21:35:20 -0400 |
commit | 17ef32956a8481fba02751e14c17c648c9f472e6 (patch) | |
tree | 00c310cc365a68606015400ca0927ab53e1fa8a3 /arch/arm/kernel | |
parent | 75bc4446e0d553aceeb632ae05878786d6760e47 (diff) |
ARM: fix a cockup in 48be69a02 (ARM: move signal handlers into a vdso-like page)
commit e0d407564b532d978b03ceccebd224a05d02f111 upstream.
Unfortunately, I never committed the fix to a nasty oops which can
occur as a result of that commit:
------------[ cut here ]------------
kernel BUG at /home/olof/work/batch/include/linux/mm.h:414!
Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM
Modules linked in:
CPU: 0 PID: 490 Comm: killall5 Not tainted 3.11.0-rc3-00288-gabe0308 #53
task: e90acac0 ti: e9be8000 task.ti: e9be8000
PC is at special_mapping_fault+0xa4/0xc4
LR is at __do_fault+0x68/0x48c
This doesn't show up unless you do quite a bit of testing; a simple
boot test does not do this, so all my nightly tests were passing fine.
The reason for this is that install_special_mapping() expects the
page array to stick around, and as this was only inserting one page
which was stored on the kernel stack, that's why this was blowing up.
Reported-by: Olof Johansson <olof@lixom.net>
Tested-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/process.c | 9 | ||||
-rw-r--r-- | arch/arm/kernel/signal.c | 41 |
2 files changed, 24 insertions, 26 deletions
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 1ed29cc22979..5bc2615268d4 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -478,17 +478,18 @@ const char *arch_vma_name(struct vm_area_struct *vma) | |||
478 | "[sigpage]" : NULL; | 478 | "[sigpage]" : NULL; |
479 | } | 479 | } |
480 | 480 | ||
481 | static struct page *signal_page; | ||
481 | extern struct page *get_signal_page(void); | 482 | extern struct page *get_signal_page(void); |
482 | 483 | ||
483 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | 484 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) |
484 | { | 485 | { |
485 | struct mm_struct *mm = current->mm; | 486 | struct mm_struct *mm = current->mm; |
486 | struct page *page; | ||
487 | unsigned long addr; | 487 | unsigned long addr; |
488 | int ret; | 488 | int ret; |
489 | 489 | ||
490 | page = get_signal_page(); | 490 | if (!signal_page) |
491 | if (!page) | 491 | signal_page = get_signal_page(); |
492 | if (!signal_page) | ||
492 | return -ENOMEM; | 493 | return -ENOMEM; |
493 | 494 | ||
494 | down_write(&mm->mmap_sem); | 495 | down_write(&mm->mmap_sem); |
@@ -500,7 +501,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
500 | 501 | ||
501 | ret = install_special_mapping(mm, addr, PAGE_SIZE, | 502 | ret = install_special_mapping(mm, addr, PAGE_SIZE, |
502 | VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, | 503 | VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, |
503 | &page); | 504 | &signal_page); |
504 | 505 | ||
505 | if (ret == 0) | 506 | if (ret == 0) |
506 | mm->context.sigpage = addr; | 507 | mm->context.sigpage = addr; |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 8515dba9534d..8fedf05b06dc 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -609,35 +609,32 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) | |||
609 | return 0; | 609 | return 0; |
610 | } | 610 | } |
611 | 611 | ||
612 | static struct page *signal_page; | ||
613 | |||
614 | struct page *get_signal_page(void) | 612 | struct page *get_signal_page(void) |
615 | { | 613 | { |
616 | if (!signal_page) { | 614 | unsigned long ptr; |
617 | unsigned long ptr; | 615 | unsigned offset; |
618 | unsigned offset; | 616 | struct page *page; |
619 | void *addr; | 617 | void *addr; |
620 | 618 | ||
621 | signal_page = alloc_pages(GFP_KERNEL, 0); | 619 | page = alloc_pages(GFP_KERNEL, 0); |
622 | 620 | ||
623 | if (!signal_page) | 621 | if (!page) |
624 | return NULL; | 622 | return NULL; |
625 | 623 | ||
626 | addr = page_address(signal_page); | 624 | addr = page_address(page); |
627 | 625 | ||
628 | /* Give the signal return code some randomness */ | 626 | /* Give the signal return code some randomness */ |
629 | offset = 0x200 + (get_random_int() & 0x7fc); | 627 | offset = 0x200 + (get_random_int() & 0x7fc); |
630 | signal_return_offset = offset; | 628 | signal_return_offset = offset; |
631 | 629 | ||
632 | /* | 630 | /* |
633 | * Copy signal return handlers into the vector page, and | 631 | * Copy signal return handlers into the vector page, and |
634 | * set sigreturn to be a pointer to these. | 632 | * set sigreturn to be a pointer to these. |
635 | */ | 633 | */ |
636 | memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes)); | 634 | memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes)); |
637 | 635 | ||
638 | ptr = (unsigned long)addr + offset; | 636 | ptr = (unsigned long)addr + offset; |
639 | flush_icache_range(ptr, ptr + sizeof(sigreturn_codes)); | 637 | flush_icache_range(ptr, ptr + sizeof(sigreturn_codes)); |
640 | } | ||
641 | 638 | ||
642 | return signal_page; | 639 | return page; |
643 | } | 640 | } |