diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-08-03 10:30:05 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-11 18:35:20 -0700 |
commit | 17ef32956a8481fba02751e14c17c648c9f472e6 (patch) | |
tree | 00c310cc365a68606015400ca0927ab53e1fa8a3 /arch/arm | |
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')
-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 1ed29cc2297..5bc2615268d 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) "[sigpage]" : NULL; } +static struct page *signal_page; extern struct page *get_signal_page(void); int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; - struct page *page; unsigned long addr; int ret; - page = get_signal_page(); - if (!page) + if (!signal_page) + signal_page = get_signal_page(); + if (!signal_page) return -ENOMEM; down_write(&mm->mmap_sem); @@ -500,7 +501,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ret = install_special_mapping(mm, addr, PAGE_SIZE, VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, - &page); + &signal_page); if (ret == 0) mm->context.sigpage = addr; diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 8515dba9534..8fedf05b06d 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) return 0; } -static struct page *signal_page; - struct page *get_signal_page(void) { - if (!signal_page) { - unsigned long ptr; - unsigned offset; - void *addr; + unsigned long ptr; + unsigned offset; + struct page *page; + void *addr; - signal_page = alloc_pages(GFP_KERNEL, 0); + page = alloc_pages(GFP_KERNEL, 0); - if (!signal_page) - return NULL; + if (!page) + return NULL; - addr = page_address(signal_page); + addr = page_address(page); - /* Give the signal return code some randomness */ - offset = 0x200 + (get_random_int() & 0x7fc); - signal_return_offset = offset; + /* Give the signal return code some randomness */ + offset = 0x200 + (get_random_int() & 0x7fc); + signal_return_offset = offset; - /* - * Copy signal return handlers into the vector page, and - * set sigreturn to be a pointer to these. - */ - memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes)); + /* + * Copy signal return handlers into the vector page, and + * set sigreturn to be a pointer to these. + */ + memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes)); - ptr = (unsigned long)addr + offset; - flush_icache_range(ptr, ptr + sizeof(sigreturn_codes)); - } + ptr = (unsigned long)addr + offset; + flush_icache_range(ptr, ptr + sizeof(sigreturn_codes)); - return signal_page; + return page; } |