diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-08-03 05:30:05 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-08-03 05:30:05 -0400 |
commit | e0d407564b532d978b03ceccebd224a05d02f111 (patch) | |
tree | fe3149db4e412e4f1d1e11941fcc260c0e5d287e /arch/arm/kernel | |
parent | a5463cd3435475386cbbe7b06e01292ac169d36f (diff) |
ARM: fix a cockup in 48be69a02 (ARM: move signal handlers into a vdso-like page)
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>
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 1e6c33d01c05..d03b5bd889c5 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -471,17 +471,18 @@ const char *arch_vma_name(struct vm_area_struct *vma) | |||
471 | "[sigpage]" : NULL; | 471 | "[sigpage]" : NULL; |
472 | } | 472 | } |
473 | 473 | ||
474 | static struct page *signal_page; | ||
474 | extern struct page *get_signal_page(void); | 475 | extern struct page *get_signal_page(void); |
475 | 476 | ||
476 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | 477 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) |
477 | { | 478 | { |
478 | struct mm_struct *mm = current->mm; | 479 | struct mm_struct *mm = current->mm; |
479 | struct page *page; | ||
480 | unsigned long addr; | 480 | unsigned long addr; |
481 | int ret; | 481 | int ret; |
482 | 482 | ||
483 | page = get_signal_page(); | 483 | if (!signal_page) |
484 | if (!page) | 484 | signal_page = get_signal_page(); |
485 | if (!signal_page) | ||
485 | return -ENOMEM; | 486 | return -ENOMEM; |
486 | 487 | ||
487 | down_write(&mm->mmap_sem); | 488 | down_write(&mm->mmap_sem); |
@@ -493,7 +494,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
493 | 494 | ||
494 | ret = install_special_mapping(mm, addr, PAGE_SIZE, | 495 | ret = install_special_mapping(mm, addr, PAGE_SIZE, |
495 | VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, | 496 | VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, |
496 | &page); | 497 | &signal_page); |
497 | 498 | ||
498 | if (ret == 0) | 499 | if (ret == 0) |
499 | mm->context.sigpage = addr; | 500 | mm->context.sigpage = addr; |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 0f17e06d51e6..39e7105a9b70 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -614,35 +614,32 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) | |||
614 | return 0; | 614 | return 0; |
615 | } | 615 | } |
616 | 616 | ||
617 | static struct page *signal_page; | ||
618 | |||
619 | struct page *get_signal_page(void) | 617 | struct page *get_signal_page(void) |
620 | { | 618 | { |
621 | if (!signal_page) { | 619 | unsigned long ptr; |
622 | unsigned long ptr; | 620 | unsigned offset; |
623 | unsigned offset; | 621 | struct page *page; |
624 | void *addr; | 622 | void *addr; |
625 | 623 | ||
626 | signal_page = alloc_pages(GFP_KERNEL, 0); | 624 | page = alloc_pages(GFP_KERNEL, 0); |
627 | 625 | ||
628 | if (!signal_page) | 626 | if (!page) |
629 | return NULL; | 627 | return NULL; |
630 | 628 | ||
631 | addr = page_address(signal_page); | 629 | addr = page_address(page); |
632 | 630 | ||
633 | /* Give the signal return code some randomness */ | 631 | /* Give the signal return code some randomness */ |
634 | offset = 0x200 + (get_random_int() & 0x7fc); | 632 | offset = 0x200 + (get_random_int() & 0x7fc); |
635 | signal_return_offset = offset; | 633 | signal_return_offset = offset; |
636 | 634 | ||
637 | /* | 635 | /* |
638 | * Copy signal return handlers into the vector page, and | 636 | * Copy signal return handlers into the vector page, and |
639 | * set sigreturn to be a pointer to these. | 637 | * set sigreturn to be a pointer to these. |
640 | */ | 638 | */ |
641 | memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes)); | 639 | memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes)); |
642 | 640 | ||
643 | ptr = (unsigned long)addr + offset; | 641 | ptr = (unsigned long)addr + offset; |
644 | flush_icache_range(ptr, ptr + sizeof(sigreturn_codes)); | 642 | flush_icache_range(ptr, ptr + sizeof(sigreturn_codes)); |
645 | } | ||
646 | 643 | ||
647 | return signal_page; | 644 | return page; |
648 | } | 645 | } |