diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-07-23 19:29:18 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-08-01 09:31:56 -0400 |
commit | 48be69a026b2c17350a5ef18a1959a919f60be7d (patch) | |
tree | 684261ce66ecd1e3f53ec1346dfa675a62477d1f | |
parent | f6f91b0d9fd971c630cef908dde8fe8795aefbf8 (diff) |
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 <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/include/asm/elf.h | 4 | ||||
-rw-r--r-- | arch/arm/include/asm/mmu.h | 1 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 40 | ||||
-rw-r--r-- | arch/arm/kernel/signal.c | 52 | ||||
-rw-r--r-- | arch/arm/kernel/signal.h | 12 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 9 |
6 files changed, 87 insertions, 31 deletions
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index 38050b1c4800..9c9b30717fda 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h | |||
@@ -130,4 +130,8 @@ struct mm_struct; | |||
130 | extern unsigned long arch_randomize_brk(struct mm_struct *mm); | 130 | extern unsigned long arch_randomize_brk(struct mm_struct *mm); |
131 | #define arch_randomize_brk arch_randomize_brk | 131 | #define arch_randomize_brk arch_randomize_brk |
132 | 132 | ||
133 | #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 | ||
134 | struct linux_binprm; | ||
135 | int arch_setup_additional_pages(struct linux_binprm *, int); | ||
136 | |||
133 | #endif | 137 | #endif |
diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h index e3d55547e755..7345e37155d5 100644 --- a/arch/arm/include/asm/mmu.h +++ b/arch/arm/include/asm/mmu.h | |||
@@ -8,6 +8,7 @@ typedef struct { | |||
8 | atomic64_t id; | 8 | atomic64_t id; |
9 | #endif | 9 | #endif |
10 | unsigned int vmalloc_seq; | 10 | unsigned int vmalloc_seq; |
11 | unsigned long sigpage; | ||
11 | } mm_context_t; | 12 | } mm_context_t; |
12 | 13 | ||
13 | #ifdef CONFIG_CPU_HAS_ASID | 14 | #ifdef CONFIG_CPU_HAS_ASID |
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index d3ca4f6915af..566d0d71a1e7 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) | |||
428 | #ifdef CONFIG_MMU | 428 | #ifdef CONFIG_MMU |
429 | /* | 429 | /* |
430 | * The vectors page is always readable from user space for the | 430 | * The vectors page is always readable from user space for the |
431 | * atomic helpers and the signal restart code. Insert it into the | 431 | * atomic helpers. Insert it into the gate_vma so that it is visible |
432 | * gate_vma so that it is visible through ptrace and /proc/<pid>/mem. | 432 | * through ptrace and /proc/<pid>/mem. |
433 | */ | 433 | */ |
434 | static struct vm_area_struct gate_vma = { | 434 | static struct vm_area_struct gate_vma = { |
435 | .vm_start = 0xffff0000, | 435 | .vm_start = 0xffff0000, |
@@ -461,6 +461,40 @@ int in_gate_area_no_mm(unsigned long addr) | |||
461 | 461 | ||
462 | const char *arch_vma_name(struct vm_area_struct *vma) | 462 | const char *arch_vma_name(struct vm_area_struct *vma) |
463 | { | 463 | { |
464 | return (vma == &gate_vma) ? "[vectors]" : NULL; | 464 | return (vma == &gate_vma) ? "[vectors]" : |
465 | (vma->vm_mm && vma->vm_start == vma->vm_mm->context.sigpage) ? | ||
466 | "[sigpage]" : NULL; | ||
467 | } | ||
468 | |||
469 | extern struct page *get_signal_page(void); | ||
470 | |||
471 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | ||
472 | { | ||
473 | struct mm_struct *mm = current->mm; | ||
474 | struct page *page; | ||
475 | unsigned long addr; | ||
476 | int ret; | ||
477 | |||
478 | page = get_signal_page(); | ||
479 | if (!page) | ||
480 | return -ENOMEM; | ||
481 | |||
482 | down_write(&mm->mmap_sem); | ||
483 | addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0); | ||
484 | if (IS_ERR_VALUE(addr)) { | ||
485 | ret = addr; | ||
486 | goto up_fail; | ||
487 | } | ||
488 | |||
489 | ret = install_special_mapping(mm, addr, PAGE_SIZE, | ||
490 | VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, | ||
491 | &page); | ||
492 | |||
493 | if (ret == 0) | ||
494 | mm->context.sigpage = addr; | ||
495 | |||
496 | up_fail: | ||
497 | up_write(&mm->mmap_sem); | ||
498 | return ret; | ||
465 | } | 499 | } |
466 | #endif | 500 | #endif |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 1c16c35c271a..0f17e06d51e6 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | #include <linux/errno.h> | 10 | #include <linux/errno.h> |
11 | #include <linux/random.h> | ||
11 | #include <linux/signal.h> | 12 | #include <linux/signal.h> |
12 | #include <linux/personality.h> | 13 | #include <linux/personality.h> |
13 | #include <linux/uaccess.h> | 14 | #include <linux/uaccess.h> |
@@ -15,12 +16,11 @@ | |||
15 | 16 | ||
16 | #include <asm/elf.h> | 17 | #include <asm/elf.h> |
17 | #include <asm/cacheflush.h> | 18 | #include <asm/cacheflush.h> |
19 | #include <asm/traps.h> | ||
18 | #include <asm/ucontext.h> | 20 | #include <asm/ucontext.h> |
19 | #include <asm/unistd.h> | 21 | #include <asm/unistd.h> |
20 | #include <asm/vfp.h> | 22 | #include <asm/vfp.h> |
21 | 23 | ||
22 | #include "signal.h" | ||
23 | |||
24 | /* | 24 | /* |
25 | * For ARM syscalls, we encode the syscall number into the instruction. | 25 | * For ARM syscalls, we encode the syscall number into the instruction. |
26 | */ | 26 | */ |
@@ -40,11 +40,13 @@ | |||
40 | #define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE)) | 40 | #define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE)) |
41 | #define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE)) | 41 | #define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE)) |
42 | 42 | ||
43 | const unsigned long sigreturn_codes[7] = { | 43 | static const unsigned long sigreturn_codes[7] = { |
44 | MOV_R7_NR_SIGRETURN, SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, | 44 | MOV_R7_NR_SIGRETURN, SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, |
45 | MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, | 45 | MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, |
46 | }; | 46 | }; |
47 | 47 | ||
48 | static unsigned long signal_return_offset; | ||
49 | |||
48 | #ifdef CONFIG_CRUNCH | 50 | #ifdef CONFIG_CRUNCH |
49 | static int preserve_crunch_context(struct crunch_sigframe __user *frame) | 51 | static int preserve_crunch_context(struct crunch_sigframe __user *frame) |
50 | { | 52 | { |
@@ -401,12 +403,15 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, | |||
401 | return 1; | 403 | return 1; |
402 | 404 | ||
403 | if ((cpsr & MODE32_BIT) && !IS_ENABLED(CONFIG_ARM_MPU)) { | 405 | if ((cpsr & MODE32_BIT) && !IS_ENABLED(CONFIG_ARM_MPU)) { |
406 | struct mm_struct *mm = current->mm; | ||
407 | |||
404 | /* | 408 | /* |
405 | * 32-bit code can use the new high-page | 409 | * 32-bit code can use the signal return page |
406 | * signal return code support except when the MPU has | 410 | * except when the MPU has protected the vectors |
407 | * protected the vectors page from PL0 | 411 | * page from PL0 |
408 | */ | 412 | */ |
409 | retcode = KERN_SIGRETURN_CODE + (idx << 2) + thumb; | 413 | retcode = mm->context.sigpage + signal_return_offset + |
414 | (idx << 2) + thumb; | ||
410 | } else { | 415 | } else { |
411 | /* | 416 | /* |
412 | * Ensure that the instruction cache sees | 417 | * Ensure that the instruction cache sees |
@@ -608,3 +613,36 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) | |||
608 | } while (thread_flags & _TIF_WORK_MASK); | 613 | } while (thread_flags & _TIF_WORK_MASK); |
609 | return 0; | 614 | return 0; |
610 | } | 615 | } |
616 | |||
617 | static struct page *signal_page; | ||
618 | |||
619 | struct page *get_signal_page(void) | ||
620 | { | ||
621 | if (!signal_page) { | ||
622 | unsigned long ptr; | ||
623 | unsigned offset; | ||
624 | void *addr; | ||
625 | |||
626 | signal_page = alloc_pages(GFP_KERNEL, 0); | ||
627 | |||
628 | if (!signal_page) | ||
629 | return NULL; | ||
630 | |||
631 | addr = page_address(signal_page); | ||
632 | |||
633 | /* Give the signal return code some randomness */ | ||
634 | offset = 0x200 + (get_random_int() & 0x7fc); | ||
635 | signal_return_offset = offset; | ||
636 | |||
637 | /* | ||
638 | * Copy signal return handlers into the vector page, and | ||
639 | * set sigreturn to be a pointer to these. | ||
640 | */ | ||
641 | memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes)); | ||
642 | |||
643 | ptr = (unsigned long)addr + offset; | ||
644 | flush_icache_range(ptr, ptr + sizeof(sigreturn_codes)); | ||
645 | } | ||
646 | |||
647 | return signal_page; | ||
648 | } | ||
diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h deleted file mode 100644 index 5ff067b7c752..000000000000 --- a/arch/arm/kernel/signal.h +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/kernel/signal.h | ||
3 | * | ||
4 | * Copyright (C) 2005-2009 Russell King. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500) | ||
11 | |||
12 | extern const unsigned long sigreturn_codes[7]; | ||
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index e3ca35ccd38e..ab517fcce21b 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -35,8 +35,6 @@ | |||
35 | #include <asm/tls.h> | 35 | #include <asm/tls.h> |
36 | #include <asm/system_misc.h> | 36 | #include <asm/system_misc.h> |
37 | 37 | ||
38 | #include "signal.h" | ||
39 | |||
40 | static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" }; | 38 | static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" }; |
41 | 39 | ||
42 | void *vectors_page; | 40 | void *vectors_page; |
@@ -850,13 +848,6 @@ void __init early_trap_init(void *vectors_base) | |||
850 | 848 | ||
851 | kuser_init(vectors_base); | 849 | kuser_init(vectors_base); |
852 | 850 | ||
853 | /* | ||
854 | * Copy signal return handlers into the vector page, and | ||
855 | * set sigreturn to be a pointer to these. | ||
856 | */ | ||
857 | memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE), | ||
858 | sigreturn_codes, sizeof(sigreturn_codes)); | ||
859 | |||
860 | flush_icache_range(vectors, vectors + PAGE_SIZE * 2); | 851 | flush_icache_range(vectors, vectors + PAGE_SIZE * 2); |
861 | modify_domain(DOMAIN_USER, DOMAIN_CLIENT); | 852 | modify_domain(DOMAIN_USER, DOMAIN_CLIENT); |
862 | #else /* ifndef CONFIG_CPU_V7M */ | 853 | #else /* ifndef CONFIG_CPU_V7M */ |