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/signal.c | |
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/signal.c')
-rw-r--r-- | arch/arm/kernel/signal.c | 41 |
1 files changed, 19 insertions, 22 deletions
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 | } |