From 48be69a026b2c17350a5ef18a1959a919f60be7d Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 24 Jul 2013 00:29:18 +0100 Subject: ARM: move signal handlers into a vdso-like page Move the signal handlers into a VDSO page rather than keeping them in the vectors page. This allows us to place them randomly within this page, and also map the page at a random location within userspace further protecting these code fragments from ROP attacks. The new VDSO page is also poisoned in the same way as the vector page. Signed-off-by: Russell King --- arch/arm/kernel/process.c | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) (limited to 'arch/arm/kernel/process.c') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index d3ca4f6915a..566d0d71a1e 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -428,8 +428,8 @@ unsigned long arch_randomize_brk(struct mm_struct *mm) #ifdef CONFIG_MMU /* * The vectors page is always readable from user space for the - * atomic helpers and the signal restart code. Insert it into the - * gate_vma so that it is visible through ptrace and /proc//mem. + * atomic helpers. Insert it into the gate_vma so that it is visible + * through ptrace and /proc//mem. */ static struct vm_area_struct gate_vma = { .vm_start = 0xffff0000, @@ -461,6 +461,40 @@ int in_gate_area_no_mm(unsigned long addr) const char *arch_vma_name(struct vm_area_struct *vma) { - return (vma == &gate_vma) ? "[vectors]" : NULL; + return (vma == &gate_vma) ? "[vectors]" : + (vma->vm_mm && vma->vm_start == vma->vm_mm->context.sigpage) ? + "[sigpage]" : NULL; +} + +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) + return -ENOMEM; + + down_write(&mm->mmap_sem); + addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0); + if (IS_ERR_VALUE(addr)) { + ret = addr; + goto up_fail; + } + + ret = install_special_mapping(mm, addr, PAGE_SIZE, + VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, + &page); + + if (ret == 0) + mm->context.sigpage = addr; + + up_fail: + up_write(&mm->mmap_sem); + return ret; } #endif -- cgit v1.2.3-70-g09d2 From a5463cd3435475386cbbe7b06e01292ac169d36f Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 31 Jul 2013 21:58:56 +0100 Subject: ARM: make vectors page inaccessible from userspace If kuser helpers are not provided by the kernel, disable user access to the vectors page. With the kuser helpers gone, there is no reason for this page to be visible to userspace. Signed-off-by: Russell King --- arch/arm/include/asm/page.h | 2 ++ arch/arm/kernel/process.c | 7 ++++++- arch/arm/mm/mmu.c | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'arch/arm/kernel/process.c') diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h index 6363f3d1d50..4355f0ec44d 100644 --- a/arch/arm/include/asm/page.h +++ b/arch/arm/include/asm/page.h @@ -142,7 +142,9 @@ extern void __cpu_copy_user_highpage(struct page *to, struct page *from, #define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) extern void copy_page(void *to, const void *from); +#ifdef CONFIG_KUSER_HELPERS #define __HAVE_ARCH_GATE_AREA 1 +#endif #ifdef CONFIG_ARM_LPAE #include diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 566d0d71a1e..1e6c33d01c0 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -426,6 +426,7 @@ unsigned long arch_randomize_brk(struct mm_struct *mm) } #ifdef CONFIG_MMU +#ifdef CONFIG_KUSER_HELPERS /* * The vectors page is always readable from user space for the * atomic helpers. Insert it into the gate_vma so that it is visible @@ -458,10 +459,14 @@ int in_gate_area_no_mm(unsigned long addr) { return in_gate_area(NULL, addr); } +#define is_gate_vma(vma) ((vma) = &gate_vma) +#else +#define is_gate_vma(vma) 0 +#endif const char *arch_vma_name(struct vm_area_struct *vma) { - return (vma == &gate_vma) ? "[vectors]" : + return is_gate_vma(vma) ? "[vectors]" : (vma->vm_mm && vma->vm_start == vma->vm_mm->context.sigpage) ? "[sigpage]" : NULL; } diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 9ea274d1af6..ca46f413d86 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -1205,7 +1205,11 @@ static void __init devicemaps_init(struct machine_desc *mdesc) map.pfn = __phys_to_pfn(virt_to_phys(vectors)); map.virtual = 0xffff0000; map.length = PAGE_SIZE; +#ifdef CONFIG_KUSER_HELPERS map.type = MT_HIGH_VECTORS; +#else + map.type = MT_LOW_VECTORS; +#endif create_mapping(&map); if (!vectors_high()) { -- cgit v1.2.3-70-g09d2 From 44424c34049f41123a3a8b4853822f47f4ff03a2 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2013 23:09:46 +0100 Subject: ARM: 7803/1: Fix deadlock scenario with smp_send_stop() If one process calls sys_reboot and that process then stops other CPUs while those CPUs are within a spin_lock() region we can potentially encounter a deadlock scenario like below. CPU 0 CPU 1 ----- ----- spin_lock(my_lock) smp_send_stop() handle_IPI() disable_preemption/irqs while(1); spin_lock(my_lock) <--- Waits forever We shouldn't attempt to run any other tasks after we send a stop IPI to a CPU so disable preemption so that this task runs to completion. We use local_irq_disable() here for cross-arch consistency with x86. Reported-by: Sundarajan Srinivasan Signed-off-by: Stephen Boyd Signed-off-by: Russell King --- arch/arm/kernel/process.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'arch/arm/kernel/process.c') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index d3ca4f6915a..08b47ebd314 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -197,6 +197,7 @@ void machine_shutdown(void) */ void machine_halt(void) { + local_irq_disable(); smp_send_stop(); local_irq_disable(); @@ -211,6 +212,7 @@ void machine_halt(void) */ void machine_power_off(void) { + local_irq_disable(); smp_send_stop(); if (pm_power_off) @@ -230,6 +232,7 @@ void machine_power_off(void) */ void machine_restart(char *cmd) { + local_irq_disable(); smp_send_stop(); arm_pm_restart(reboot_mode, cmd); -- cgit v1.2.3-70-g09d2 From e0d407564b532d978b03ceccebd224a05d02f111 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 Aug 2013 10:30:05 +0100 Subject: 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 Tested-by: Olof Johansson Signed-off-by: Russell King --- arch/arm/kernel/process.c | 9 +++++---- arch/arm/kernel/signal.c | 41 +++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 26 deletions(-) (limited to 'arch/arm/kernel/process.c') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 1e6c33d01c0..d03b5bd889c 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) "[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); @@ -493,7 +494,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 0f17e06d51e..39e7105a9b7 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) 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; } -- cgit v1.2.3-70-g09d2