SPECS/linux/0001-NOWRITEEXEC-and-PAX-features-MPROTECT-EMUTRAMP.patch
b3de4416
 From 2f81e15c64fe8ad3732a71fbf1e4053842e6e1b7 Mon Sep 17 00:00:00 2001
c035f9aa
 From: Alexey Makhalov <amakhalov@vmware.com>
b3111e31
 Date: Fri, 3 Feb 2017 07:10:18 -0800
 Subject: [PATCH 1/3] NOWRITEEXEC and PAX features: MPROTECT, EMUTRAMP
c035f9aa
 
 ---
4e703b6f
  arch/x86/mm/fault.c      | 218 +++++++++++++++++++++++++++++++++++++++++++++++
  fs/binfmt_elf.c          |  70 +++++++++++++++
  fs/exec.c                |   5 ++
c035f9aa
  include/linux/binfmts.h  |   3 +
  include/linux/elf.h      |   2 +
  include/linux/mm_types.h |   3 +
  include/linux/sched.h    |   2 +
  include/uapi/linux/elf.h |   3 +
  ipc/shm.c                |   3 +
4e703b6f
  mm/mmap.c                |  25 ++++++
  mm/mprotect.c            |  12 +++
  security/Kconfig         |  78 +++++++++++++++++
c035f9aa
  12 files changed, 424 insertions(+)
 
 diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
b3de4416
 index b0ff3786..e886bf8 100644
c035f9aa
 --- a/arch/x86/mm/fault.c
 +++ b/arch/x86/mm/fault.c
 @@ -244,6 +244,11 @@ force_sig_info_fault(int si_signo, int si_code, unsigned long address,
  	force_sig_info(si_signo, &info, tsk);
  }
  
 +#ifdef CONFIG_PAX_EMUTRAMP
 +static bool pax_is_fetch_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address);
 +static int pax_handle_fetch_fault(struct pt_regs *regs);
 +#endif
 +
  DEFINE_SPINLOCK(pgd_lock);
  LIST_HEAD(pgd_list);
  
b3de4416
 @@ -925,6 +930,13 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
c035f9aa
  				return;
  		}
  #endif
 +#ifdef CONFIG_PAX_EMUTRAMP
 +		if (pax_is_fetch_fault(regs, error_code, address)) {
 +			if (pax_handle_fetch_fault(regs) == 2)
 +				return;
 +			do_group_exit(SIGKILL);
 +		}
 +#endif
  
  		/*
  		 * To avoid leaking information about the kernel page table
b3de4416
 @@ -1531,3 +1543,209 @@ do_page_fault(struct pt_regs *regs, unsigned long error_code)
  	exception_exit(prev_state);
c035f9aa
  }
b3de4416
  NOKPROBE_SYMBOL(do_page_fault);
c035f9aa
 +
 +#ifdef CONFIG_PAX_EMUTRAMP
 +static bool pax_is_fetch_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address)
 +{
 +	unsigned long ip = regs->ip;
 +
 +	if (v8086_mode(regs))
 +		ip = ((regs->cs & 0xffff) << 4) + (ip & 0xffff);
 +
 +	if ((__supported_pte_mask & _PAGE_NX) && (error_code & PF_INSTR))
 +		return true;
 +	if (!(error_code & (PF_PROT | PF_WRITE)) && ip == address)
 +		return true;
 +	return false;
 +}
 +
 +static int pax_handle_fetch_fault_32(struct pt_regs *regs)
 +{
 +	int err;
 +
 +	do { /* PaX: libffi trampoline emulation */
 +		unsigned char mov, jmp;
 +		unsigned int addr1, addr2;
 +
 +#ifdef CONFIG_X86_64
 +		if ((regs->ip + 9) >> 32)
 +			break;
 +#endif
 +
 +		err = get_user(mov, (unsigned char __user *)regs->ip);
 +		err |= get_user(addr1, (unsigned int __user *)(regs->ip + 1));
 +		err |= get_user(jmp, (unsigned char __user *)(regs->ip + 5));
 +		err |= get_user(addr2, (unsigned int __user *)(regs->ip + 6));
 +
 +		if (err)
 +			break;
 +
 +		if (mov == 0xB8 && jmp == 0xE9) {
 +			regs->ax = addr1;
 +			regs->ip = (unsigned int)(regs->ip + addr2 + 10);
 +			return 2;
 +		}
 +	} while (0);
 +
 +	do { /* PaX: gcc trampoline emulation #1 */
 +		unsigned char mov1, mov2;
 +		unsigned short jmp;
 +		unsigned int addr1, addr2;
 +
 +#ifdef CONFIG_X86_64
 +		if ((regs->ip + 11) >> 32)
 +			break;
 +#endif
 +
 +		err = get_user(mov1, (unsigned char __user *)regs->ip);
 +		err |= get_user(addr1, (unsigned int __user *)(regs->ip + 1));
 +		err |= get_user(mov2, (unsigned char __user *)(regs->ip + 5));
 +		err |= get_user(addr2, (unsigned int __user *)(regs->ip + 6));
 +		err |= get_user(jmp, (unsigned short __user *)(regs->ip + 10));
 +
 +		if (err)
 +			break;
 +
 +		if (mov1 == 0xB9 && mov2 == 0xB8 && jmp == 0xE0FF) {
 +			regs->cx = addr1;
 +			regs->ax = addr2;
 +			regs->ip = addr2;
 +			return 2;
 +		}
 +	} while (0);
 +
 +	do { /* PaX: gcc trampoline emulation #2 */
 +		unsigned char mov, jmp;
 +		unsigned int addr1, addr2;
 +
 +#ifdef CONFIG_X86_64
 +		if ((regs->ip + 9) >> 32)
 +			break;
 +#endif
 +
 +		err = get_user(mov, (unsigned char __user *)regs->ip);
 +		err |= get_user(addr1, (unsigned int __user *)(regs->ip + 1));
 +		err |= get_user(jmp, (unsigned char __user *)(regs->ip + 5));
 +		err |= get_user(addr2, (unsigned int __user *)(regs->ip + 6));
 +
 +		if (err)
 +			break;
 +
 +		if (mov == 0xB9 && jmp == 0xE9) {
 +			regs->cx = addr1;
 +			regs->ip = (unsigned int)(regs->ip + addr2 + 10);
 +			return 2;
 +		}
 +	} while (0);
 +
 +	return 1; /* PaX in action */
 +}
 +
 +#ifdef CONFIG_X86_64
 +static int pax_handle_fetch_fault_64(struct pt_regs *regs)
 +{
 +	int err;
 +
 +	do { /* PaX: libffi trampoline emulation */
 +		unsigned short mov1, mov2, jmp1;
 +		unsigned char stcclc, jmp2;
 +		unsigned long addr1, addr2;
 +
 +		err = get_user(mov1, (unsigned short __user *)regs->ip);
 +		err |= get_user(addr1, (unsigned long __user *)(regs->ip + 2));
 +		err |= get_user(mov2, (unsigned short __user *)(regs->ip + 10));
 +		err |= get_user(addr2, (unsigned long __user *)(regs->ip + 12));
 +		err |= get_user(stcclc, (unsigned char __user *)(regs->ip + 20));
 +		err |= get_user(jmp1, (unsigned short __user *)(regs->ip + 21));
 +		err |= get_user(jmp2, (unsigned char __user *)(regs->ip + 23));
 +
 +		if (err)
 +			break;
 +
 +		if (mov1 == 0xBB49 && mov2 == 0xBA49 && (stcclc == 0xF8 || stcclc == 0xF9) && jmp1 == 0xFF49 && jmp2 == 0xE3) {
 +			regs->r11 = addr1;
 +			regs->r10 = addr2;
 +			if (stcclc == 0xF8)
 +				regs->flags &= ~X86_EFLAGS_CF;
 +			else
 +				regs->flags |= X86_EFLAGS_CF;
 +			regs->ip = addr1;
 +			return 2;
 +		}
 +	} while (0);
 +
 +	do { /* PaX: gcc trampoline emulation #1 */
 +		unsigned short mov1, mov2, jmp1;
 +		unsigned char jmp2;
 +		unsigned int addr1;
 +		unsigned long addr2;
 +
 +		err = get_user(mov1, (unsigned short __user *)regs->ip);
 +		err |= get_user(addr1, (unsigned int __user *)(regs->ip + 2));
 +		err |= get_user(mov2, (unsigned short __user *)(regs->ip + 6));
 +		err |= get_user(addr2, (unsigned long __user *)(regs->ip + 8));
 +		err |= get_user(jmp1, (unsigned short __user *)(regs->ip + 16));
 +		err |= get_user(jmp2, (unsigned char __user *)(regs->ip + 18));
 +
 +		if (err)
 +			break;
 +
 +		if (mov1 == 0xBB41 && mov2 == 0xBA49 && jmp1 == 0xFF49 && jmp2 == 0xE3) {
 +			regs->r11 = addr1;
 +			regs->r10 = addr2;
 +			regs->ip = addr1;
 +			return 2;
 +		}
 +	} while (0);
 +
 +	do { /* PaX: gcc trampoline emulation #2 */
 +		unsigned short mov1, mov2, jmp1;
 +		unsigned char jmp2;
 +		unsigned long addr1, addr2;
 +
 +		err = get_user(mov1, (unsigned short __user *)regs->ip);
 +		err |= get_user(addr1, (unsigned long __user *)(regs->ip + 2));
 +		err |= get_user(mov2, (unsigned short __user *)(regs->ip + 10));
 +		err |= get_user(addr2, (unsigned long __user *)(regs->ip + 12));
 +		err |= get_user(jmp1, (unsigned short __user *)(regs->ip + 20));
 +		err |= get_user(jmp2, (unsigned char __user *)(regs->ip + 22));
 +
 +		if (err)
 +			break;
 +
 +		if (mov1 == 0xBB49 && mov2 == 0xBA49 && jmp1 == 0xFF49 && jmp2 == 0xE3) {
 +			regs->r11 = addr1;
 +			regs->r10 = addr2;
 +			regs->ip = addr1;
 +			return 2;
 +		}
 +	} while (0);
 +
 +	return 1; /* PaX in action */
 +}
 +#endif
 +
 +/*
 + * PaX: decide what to do with offenders (regs->ip = fault address)
 + *
 + * returns 1 when task should be killed
 + *         2 when gcc trampoline was detected
 + */
 +static int pax_handle_fetch_fault(struct pt_regs *regs)
 +{
 +	if (v8086_mode(regs))
 +		return 1;
 +
 +	if (!(current->mm->pax_flags & MF_PAX_EMUTRAMP))
 +		return 1;
 +
 +#ifdef CONFIG_X86_32
 +	return pax_handle_fetch_fault_32(regs);
 +#else
 +	if (regs->cs == __USER32_CS || (regs->cs & SEGMENT_LDT))
 +		return pax_handle_fetch_fault_32(regs);
 +	else
 +		return pax_handle_fetch_fault_64(regs);
 +#endif
 +}
 +#endif
 diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
b3de4416
 index 73b01e4..ea8fed3 100644
c035f9aa
 --- a/fs/binfmt_elf.c
 +++ b/fs/binfmt_elf.c
b3de4416
 @@ -40,6 +40,7 @@
  #include <linux/sched/cputime.h>
  #include <linux/cred.h>
c035f9aa
  #include <linux/dax.h>
 +#include <linux/xattr.h>
b3de4416
  #include <linux/uaccess.h>
c035f9aa
  #include <asm/param.h>
  #include <asm/page.h>
b3de4416
 @@ -71,6 +72,10 @@ static int elf_core_dump(struct coredump_params *cprm);
c035f9aa
  #define elf_core_dump	NULL
  #endif
  
 +#ifdef CONFIG_PAX_MPROTECT
 +static void elf_handle_mprotect(struct vm_area_struct *vma, unsigned long newflags);
 +#endif
 +
  #if ELF_EXEC_PAGESIZE > PAGE_SIZE
  #define ELF_MIN_ALIGN	ELF_EXEC_PAGESIZE
  #else
b3de4416
 @@ -90,6 +95,9 @@ static struct linux_binfmt elf_format = {
c035f9aa
  	.load_binary	= load_elf_binary,
  	.load_shlib	= load_elf_library,
  	.core_dump	= elf_core_dump,
 +#ifdef CONFIG_PAX_MPROTECT
 +	.handle_mprotect= elf_handle_mprotect,
 +#endif
  	.min_coredump	= ELF_EXEC_PAGESIZE,
  };
  
b3de4416
 @@ -859,6 +867,18 @@ static int load_elf_binary(struct linux_binprm *bprm)
c035f9aa
  	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
  	   may depend on the personality.  */
  	SET_PERSONALITY2(loc->elf_ex, &arch_state);
 +#if defined(CONFIG_PAX)
 +	current->mm->pax_flags = 0UL;
 +#if defined(CONFIG_PAX_NOWRITEEXEC)
 +	if (executable_stack == EXSTACK_ENABLE_X)
 +	{
 +#if defined(CONFIG_PAX_EMUTRAMP)
 +		executable_stack = EXSTACK_DISABLE_X;
 +		current->mm->pax_flags |= MF_PAX_EMUTRAMP;
 +#endif
 +	}
 +#endif
 +#endif
  	if (elf_read_implies_exec(loc->elf_ex, executable_stack))
  		current->personality |= READ_IMPLIES_EXEC;
  
b3de4416
 @@ -2385,6 +2405,56 @@ out:
c035f9aa
  
  #endif		/* CONFIG_ELF_CORE */
  
 +#ifdef CONFIG_PAX_MPROTECT
 +/* PaX: non-PIC ELF libraries need relocations on their executable segments
 + * therefore we'll grant them VM_MAYWRITE once during their life. Similarly
 + * we'll remove VM_MAYWRITE for good on RELRO segments.
 + *
 + * The checks favour ld-linux.so behaviour which operates on a per ELF segment
 + * basis because we want to allow the common case and not the special ones.
 + */
 +static void elf_handle_mprotect(struct vm_area_struct *vma, unsigned long newflags)
 +{
 +	struct elfhdr elf_h;
 +	struct elf_phdr elf_p;
 +	unsigned long i;
 +	unsigned long oldflags;
 +	bool is_relro;
 +
 +	if (!vma->vm_file)
 +		return;
 +
 +	oldflags = vma->vm_flags & (VM_MAYEXEC | VM_MAYWRITE | VM_MAYREAD | VM_EXEC | VM_WRITE | VM_READ);
 +	newflags &= VM_MAYEXEC | VM_MAYWRITE | VM_MAYREAD | VM_EXEC | VM_WRITE | VM_READ;
 +
 +	/* possible RELRO */
 +	is_relro = vma->anon_vma && oldflags == (VM_MAYWRITE | VM_MAYREAD | VM_READ) && newflags == (VM_MAYWRITE | VM_MAYREAD | VM_READ);
 +
 +	if (!is_relro)
 +		return;
 +
 +	if (sizeof(elf_h) != kernel_read(vma->vm_file, 0UL, (char *)&elf_h, sizeof(elf_h)) ||
 +	    memcmp(elf_h.e_ident, ELFMAG, SELFMAG) ||
 +	    (elf_h.e_type != ET_DYN && elf_h.e_type != ET_EXEC) ||
 +	    !elf_check_arch(&elf_h) ||
 +	    elf_h.e_phentsize != sizeof(struct elf_phdr) ||
 +	    elf_h.e_phnum > 65536UL / sizeof(struct elf_phdr))
 +		return;
 +
 +	for (i = 0UL; i < elf_h.e_phnum; i++) {
 +		if (sizeof(elf_p) != kernel_read(vma->vm_file, elf_h.e_phoff + i*sizeof(elf_p), (char *)&elf_p, sizeof(elf_p)))
 +			return;
 +		if (elf_p.p_type == PT_GNU_RELRO) {
 +			if (!is_relro)
 +				continue;
 +			if ((elf_p.p_offset >> PAGE_SHIFT) == vma->vm_pgoff && ELF_PAGEALIGN(elf_p.p_memsz) == vma->vm_end - vma->vm_start)
 +				vma->vm_flags &= ~VM_MAYWRITE;
 +			is_relro = false;
 +		}
 +	}
 +}
 +#endif
 +
  static int __init init_elf_binfmt(void)
  {
  	register_binfmt(&elf_format);
 diff --git a/fs/exec.c b/fs/exec.c
b3de4416
 index 3e14ba2..2f6ddc2 100644
c035f9aa
 --- a/fs/exec.c
 +++ b/fs/exec.c
b3de4416
 @@ -744,7 +744,12 @@ int setup_arg_pages(struct linux_binprm *bprm,
c035f9aa
  	if (unlikely(executable_stack == EXSTACK_ENABLE_X))
  		vm_flags |= VM_EXEC;
  	else if (executable_stack == EXSTACK_DISABLE_X)
 +	{
  		vm_flags &= ~VM_EXEC;
 +#ifdef CONFIG_PAX_MPROTECT
 +		vm_flags &= ~VM_MAYEXEC;
 +#endif
 +	}
  	vm_flags |= mm->def_flags;
  	vm_flags |= VM_STACK_INCOMPLETE_SETUP;
  
 diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
b3de4416
 index b0abe21..644ca30 100644
c035f9aa
 --- a/include/linux/binfmts.h
 +++ b/include/linux/binfmts.h
b3de4416
 @@ -95,6 +95,9 @@ struct linux_binfmt {
c035f9aa
  	int (*load_binary)(struct linux_binprm *);
  	int (*load_shlib)(struct file *);
  	int (*core_dump)(struct coredump_params *cprm);
 +#ifdef CONFIG_PAX_MPROTECT
 +	void (*handle_mprotect)(struct vm_area_struct *vma, unsigned long newflags);
 +#endif
  	unsigned long min_coredump;	/* minimal dump size */
b3de4416
  } __randomize_layout;
c035f9aa
  
 diff --git a/include/linux/elf.h b/include/linux/elf.h
b3de4416
 index e3649b3..c1e78a3 100644
c035f9aa
 --- a/include/linux/elf.h
 +++ b/include/linux/elf.h
b3de4416
 @@ -31,6 +31,7 @@ extern Elf32_Dyn _DYNAMIC [];
c035f9aa
  #define elf_addr_t	Elf32_Off
  #define Elf_Half	Elf32_Half
b3de4416
  #define Elf_Word	Elf32_Word
c035f9aa
 +#define elf_dyn		Elf32_Dyn
  
  #else
  
b3de4416
 @@ -42,6 +43,7 @@ extern Elf64_Dyn _DYNAMIC [];
c035f9aa
  #define elf_addr_t	Elf64_Off
  #define Elf_Half	Elf64_Half
b3de4416
  #define Elf_Word	Elf64_Word
c035f9aa
 +#define elf_dyn		Elf64_Dyn
  
  #endif
  
 diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
b3de4416
 index c85f11d..9aa42c1 100644
c035f9aa
 --- a/include/linux/mm_types.h
 +++ b/include/linux/mm_types.h
b3de4416
 @@ -508,6 +508,9 @@ struct mm_struct {
fc081194
  	atomic_long_t hugetlb_usage;
c035f9aa
  #endif
fc081194
  	struct work_struct async_put_work;
c035f9aa
 +#if defined(CONFIG_PAX)
 +	unsigned long pax_flags;
 +#endif
  
b3de4416
  #if IS_ENABLED(CONFIG_HMM)
  	/* HMM needs to track a few things per mm */
c035f9aa
 diff --git a/include/linux/sched.h b/include/linux/sched.h
b3de4416
 index fdf74f2..3e4f9ed 100644
c035f9aa
 --- a/include/linux/sched.h
 +++ b/include/linux/sched.h
b3de4416
 @@ -1667,4 +1667,6 @@ extern long sched_getaffinity(pid_t pid, struct cpumask *mask);
  #define TASK_SIZE_OF(tsk)	TASK_SIZE
c035f9aa
  #endif
  
 +#define MF_PAX_EMUTRAMP		0x02000000	/* Emulate trampolines */
 +
b3de4416
  #endif
c035f9aa
 diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
b3de4416
 index c58627c..3c9eda7 100644
c035f9aa
 --- a/include/uapi/linux/elf.h
 +++ b/include/uapi/linux/elf.h
b3de4416
 @@ -38,6 +38,7 @@ typedef __s64	Elf64_Sxword;
c035f9aa
  #define PT_GNU_EH_FRAME		0x6474e550
  
  #define PT_GNU_STACK	(PT_LOOS + 0x474e551)
 +#define PT_GNU_RELRO	(PT_LOOS + 0x474e552)
  
  /*
   * Extended Numbering
b3de4416
 @@ -95,6 +96,8 @@ typedef __s64	Elf64_Sxword;
c035f9aa
  #define DT_DEBUG	21
  #define DT_TEXTREL	22
  #define DT_JMPREL	23
 +#define DT_FLAGS	30
 +  #define DF_TEXTREL  0x00000004
  #define DT_ENCODING	32
  #define OLD_DT_LOOS	0x60000000
  #define DT_LOOS		0x6000000d
 diff --git a/ipc/shm.c b/ipc/shm.c
b3de4416
 index bd65275..f6ff14a 100644
c035f9aa
 --- a/ipc/shm.c
 +++ b/ipc/shm.c
b3de4416
 @@ -1317,6 +1317,9 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg,
c035f9aa
  		f_mode = FMODE_READ | FMODE_WRITE;
  	}
  	if (shmflg & SHM_EXEC) {
 +#ifdef CONFIG_PAX_NOWRITEEXEC
 +		goto out;
 +#endif
  		prot |= PROT_EXEC;
  		acc_mode |= S_IXUGO;
  	}
 diff --git a/mm/mmap.c b/mm/mmap.c
b3de4416
 index 0de87a3..2aaae36 100644
c035f9aa
 --- a/mm/mmap.c
 +++ b/mm/mmap.c
b3de4416
 @@ -1378,6 +1378,17 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
c035f9aa
  	vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) |
  			mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
  
 +#ifdef CONFIG_PAX_NOWRITEEXEC
 +	if ((vm_flags & (VM_WRITE | VM_EXEC)) == (VM_WRITE | VM_EXEC))
 +		return -EPERM;
 +#ifdef CONFIG_PAX_MPROTECT
 +	if (!(vm_flags & VM_EXEC))
 +		vm_flags &= ~VM_MAYEXEC;
 +	else
 +		vm_flags &= ~VM_MAYWRITE;
 +#endif
 +#endif
 +
  	if (flags & MAP_LOCKED)
  		if (!can_do_mlock())
  			return -EPERM;
b3de4416
 @@ -2874,6 +2885,9 @@ static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long
  	if ((flags & (~VM_EXEC)) != 0)
  		return -EINVAL;
  	flags |= VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
c035f9aa
 +#ifdef CONFIG_PAX_MPROTECT
 +	flags &= ~VM_MAYEXEC;
 +#endif
  
  	error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
  	if (offset_in_page(error))
b3de4416
 @@ -3280,6 +3294,17 @@ static struct vm_area_struct *__install_special_mapping(
c035f9aa
  	vma->vm_start = addr;
  	vma->vm_end = addr + len;
  
 +#ifdef CONFIG_PAX_NOWRITEEXEC
 +	if ((vm_flags & (VM_WRITE | VM_EXEC)) == (VM_WRITE | VM_EXEC))
 +		return ERR_PTR(-EPERM);
 +#ifdef CONFIG_PAX_MPROTECT
 +	if (!(vm_flags & VM_EXEC))
 +		vm_flags &= ~VM_MAYEXEC;
 +	else
 +		vm_flags &= ~VM_MAYWRITE;
 +#endif
 +#endif
 +
  	vma->vm_flags = vm_flags | mm->def_flags | VM_DONTEXPAND | VM_SOFTDIRTY;
  	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
  
 diff --git a/mm/mprotect.c b/mm/mprotect.c
b3de4416
 index ec39f73..d1a3fc3 100644
c035f9aa
 --- a/mm/mprotect.c
 +++ b/mm/mprotect.c
b3de4416
 @@ -26,6 +26,10 @@
c035f9aa
  #include <linux/perf_event.h>
  #include <linux/pkeys.h>
fc081194
  #include <linux/ksm.h>
c035f9aa
 +#ifdef CONFIG_PAX_MPROTECT
 +#include <linux/elf.h>
 +#include <linux/binfmts.h>
 +#endif
b3de4416
  #include <linux/uaccess.h>
c035f9aa
  #include <asm/pgtable.h>
  #include <asm/cacheflush.h>
b3de4416
 @@ -360,6 +364,10 @@ success:
c035f9aa
  	 * held in write mode.
  	 */
  	vma->vm_flags = newflags;
 +#ifdef CONFIG_PAX_MPROTECT
 +	if (mm->binfmt && mm->binfmt->handle_mprotect)
 +		mm->binfmt->handle_mprotect(vma, newflags);
 +#endif
fc081194
  	dirty_accountable = vma_wants_writenotify(vma, vma->vm_page_prot);
c035f9aa
  	vma_set_page_prot(vma);
  
b3de4416
 @@ -451,6 +459,10 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
c035f9aa
  	if (start > vma->vm_start)
  		prev = vma;
  
 +#ifdef CONFIG_PAX_MPROTECT
 +	if (current->mm->binfmt && current->mm->binfmt->handle_mprotect)
 +		current->mm->binfmt->handle_mprotect(vma, calc_vm_prot_bits(prot, 0));
 +#endif
  	for (nstart = start ; ; ) {
fc081194
  		unsigned long mask_off_old_flags;
c035f9aa
  		unsigned long newflags;
 diff --git a/security/Kconfig b/security/Kconfig
b3de4416
 index e8e4494..3cef193 100644
c035f9aa
 --- a/security/Kconfig
 +++ b/security/Kconfig
 @@ -4,6 +4,84 @@
  
  menu "Security options"
  
 +menuconfig PAX
 +	bool "Enable various PaX features"
 +	depends on X86
 +	help
 +	  This allows you to enable various PaX features.  PaX adds
 +	  intrusion prevention mechanisms to the kernel that reduce
 +	  the risks posed by exploitable memory corruption bugs.
 +
 +if PAX
 +config PAX_NOWRITEEXEC
 +	bool "Enforce non-executable pages"
 +	depends on X86
 +	help
 +	  Enforces writables pages to be non-executable (such as the stack
 +	  or heap). And enforces executable pages to be non-writable.
 +
 +	  Enabling this option will prevent the injection and execution of
 +	  'foreign' code in a program.
 +
 +	  This will also break programs that rely on the old behaviour and
 +	  expect that dynamically allocated memory via the malloc() family
 +	  of functions is executable (which it is not).  Notable examples
 +	  are the XFree86 4.x server, the java runtime and wine.
 +
 +if PAX_NOWRITEEXEC
 +choice
 +	prompt "Executable stack"
 +
 +	help
 +	  Select the security model for the binaries with executable stack.
 +
 +	config PAX_EMUTRAMP
 +		bool "emulate"
 +		help
 +		  There are some programs and libraries that for one reason or
 +		  another attempt to execute special small code snippets from
 +		  non-executable memory pages.  Most notable examples are the
 +		  signal handler return code generated by the kernel itself and
 +		  the GCC trampolines.
 +
 +		  If you enabled CONFIG_NOWRITEEXEC then such programs will no
 +		  longer work under your kernel.
 +
 +		  As a remedy you can say Y here enable trampoline emulation for
 +		  the affected programs yet still have the protection provided by
 +		  the non-executable pages.
 +
 +		  NOTE: enabling this feature *may* open up a loophole in the
 +		  protection provided by non-executable pages that an attacker
 +		  could abuse.  Therefore the best solution is to not have any
 +		  files on your system that would require this option.  This can
 +		  be achieved by not using libc5 (which relies on the kernel
 +		  signal handler return code) and not using or rewriting programs
 +		  that make use of the nested function implementation of GCC.
 +		  Skilled users can just fix GCC itself so that it implements
 +		  nested function calls in a way that does not interfere with PaX.
 +
 +	config EXECSTACK_DISABLED
 +		bool "disabled"
 +
 +endchoice
 +
 +config PAX_MPROTECT
 +	bool "Restrict mprotect()"
 +	help
 +	  Enabling this option will prevent programs from
 +	   - changing the executable status of memory pages that were
 +	     not originally created as executable,
 +	   - making read-only executable pages writable again,
 +	   - creating executable pages from anonymous memory,
 +	   - making read-only-after-relocations (RELRO) data pages writable again.
 +
 +	  You should say Y here to complete the protection provided by
 +	  the enforcement of non-executable pages.
 +
 +endif
 +endif
 +
  source security/keys/Kconfig
  
  config SECURITY_DMESG_RESTRICT
 -- 
 2.8.1