aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2013-07-23 19:29:18 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2013-08-01 09:31:56 -0400
commit48be69a026b2c17350a5ef18a1959a919f60be7d (patch)
tree684261ce66ecd1e3f53ec1346dfa675a62477d1f
parentf6f91b0d9fd971c630cef908dde8fe8795aefbf8 (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.h4
-rw-r--r--arch/arm/include/asm/mmu.h1
-rw-r--r--arch/arm/kernel/process.c40
-rw-r--r--arch/arm/kernel/signal.c52
-rw-r--r--arch/arm/kernel/signal.h12
-rw-r--r--arch/arm/kernel/traps.c9
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;
130extern unsigned long arch_randomize_brk(struct mm_struct *mm); 130extern 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
134struct linux_binprm;
135int 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 */
434static struct vm_area_struct gate_vma = { 434static 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
462const char *arch_vma_name(struct vm_area_struct *vma) 462const 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
469extern struct page *get_signal_page(void);
470
471int 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
43const unsigned long sigreturn_codes[7] = { 43static 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
48static unsigned long signal_return_offset;
49
48#ifdef CONFIG_CRUNCH 50#ifdef CONFIG_CRUNCH
49static int preserve_crunch_context(struct crunch_sigframe __user *frame) 51static 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
617static struct page *signal_page;
618
619struct 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
12extern 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
40static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" }; 38static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
41 39
42void *vectors_page; 40void *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 */