From 816e436fea63e979f20d56f4555598f70e3b2ac8 Mon Sep 17 00:00:00 2001 From: Alexander Miroshnichenko Date: Tue, 29 Apr 2025 13:30:54 +0300 Subject: [PATCH] openpax: cherry-pick updates from master fb1be96e0a3e Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit Signed-off-by: Alexander Miroshnichenko --- .../admin-guide/kernel-parameters.txt | 3 + arch/Kconfig | 4 +- arch/arm64/include/asm/elf.h | 6 +- arch/x86/include/asm/elf.h | 10 +- arch/x86/kernel/process.c | 4 +- arch/x86/mm/fault.c | 218 ++++++++++++++++++ fs/binfmt_elf.c | 88 ++++++- fs/exec.c | 5 + fs/proc/array.c | 15 ++ fs/xattr.c | 16 ++ include/linux/init.h | 1 + include/linux/mm_types.h | 11 + include/linux/mman.h | 11 +- include/linux/xattr.h | 4 + include/uapi/linux/xattr.h | 5 + init/main.c | 11 + kernel/sysctl.c | 15 ++ mm/mmap.c | 7 + mm/util.c | 4 +- security/Kconfig | 1 + security/Kconfig.openpax | 89 +++++++ 21 files changed, 513 insertions(+), 15 deletions(-) create mode 100644 security/Kconfig.openpax diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index aa7447f8837c..3aed4739af8d 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4585,6 +4585,9 @@ the specified number of seconds. This is to be used if your oopses keep scrolling off the screen. + pax_softmode= + Enables OpenPaX soft mode if set to a non-zero value. + pcbit= [HW,ISDN] pci=option[,option...] [PCI,EARLY] various PCI subsystem options. diff --git a/arch/Kconfig b/arch/Kconfig index b8a4ff365582..9b087f9bb413 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1137,7 +1137,7 @@ config ARCH_MMAP_RND_BITS int "Number of bits to use for ASLR of mmap base address" if EXPERT range ARCH_MMAP_RND_BITS_MIN ARCH_MMAP_RND_BITS_MAX default ARCH_MMAP_RND_BITS_DEFAULT if ARCH_MMAP_RND_BITS_DEFAULT - default ARCH_MMAP_RND_BITS_MIN + default ARCH_MMAP_RND_BITS_MAX depends on HAVE_ARCH_MMAP_RND_BITS help This value can be used to select the number of bits to use to @@ -1171,7 +1171,7 @@ config ARCH_MMAP_RND_COMPAT_BITS int "Number of bits to use for ASLR of mmap base address for compatible applications" if EXPERT range ARCH_MMAP_RND_COMPAT_BITS_MIN ARCH_MMAP_RND_COMPAT_BITS_MAX default ARCH_MMAP_RND_COMPAT_BITS_DEFAULT if ARCH_MMAP_RND_COMPAT_BITS_DEFAULT - default ARCH_MMAP_RND_COMPAT_BITS_MIN + default ARCH_MMAP_RND_COMPAT_BITS_MAX depends on HAVE_ARCH_MMAP_RND_COMPAT_BITS help This value can be used to select the number of bits to use to diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index 3f93f4eef953..575a608be260 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -189,10 +189,10 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm, /* 1GB of VA */ #ifdef CONFIG_COMPAT #define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ? \ - 0x7ff >> (PAGE_SHIFT - 12) : \ - 0x3ffff >> (PAGE_SHIFT - 12)) + ((1UL << mmap_rnd_compat_bits) - 1) >> (PAGE_SHIFT - 12) : \ + ((1UL << mmap_rnd_bits) - 1) >> (PAGE_SHIFT - 12)) #else -#define STACK_RND_MASK (0x3ffff >> (PAGE_SHIFT - 12)) +#define STACK_RND_MASK (((1UL << mmap_rnd_bits) - 1) >> (PAGE_SHIFT - 12)) #endif #ifdef __AARCH64EB__ diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index 1fb83d47711f..ecaafb34d79a 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -318,8 +318,8 @@ extern unsigned long get_sigframe_size(void); #ifdef CONFIG_X86_32 -#define __STACK_RND_MASK(is32bit) (0x7ff) -#define STACK_RND_MASK (0x7ff) +#define __STACK_RND_MASK(is32bit) ((1UL << mmap_rnd_bits) - 1) +#define STACK_RND_MASK ((1UL << mmap_rnd_bits) - 1) #define ARCH_DLINFO ARCH_DLINFO_IA32 @@ -328,7 +328,11 @@ extern unsigned long get_sigframe_size(void); #else /* CONFIG_X86_32 */ /* 1GB for 64bit, 8MB for 32bit */ -#define __STACK_RND_MASK(is32bit) ((is32bit) ? 0x7ff : 0x3fffff) +#ifdef CONFIG_COMPAT +#define __STACK_RND_MASK(is32bit) ((is32bit) ? (1UL << mmap_rnd_compat_bits) - 1 : (1UL << mmap_rnd_bits) - 1) +#else +#define __STACK_RND_MASK(is32bit) ((1UL << mmap_rnd_bits) - 1) +#endif #define STACK_RND_MASK __STACK_RND_MASK(mmap_is_ia32()) #define ARCH_DLINFO \ diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 21561262a821..a81efe58aab3 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -1014,9 +1014,9 @@ unsigned long arch_align_stack(unsigned long sp) unsigned long arch_randomize_brk(struct mm_struct *mm) { if (mmap_is_ia32()) - return randomize_page(mm->brk, SZ_32M); + return mm->brk + get_random_long() % SZ_32M + PAGE_SIZE; - return randomize_page(mm->brk, SZ_1G); + return mm->brk + get_random_long() % SZ_1G + PAGE_SIZE; } /* diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 296d294142c8..65665982e401 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -1198,6 +1198,217 @@ do_kern_addr_fault(struct pt_regs *regs, unsigned long hw_error_code, } NOKPROBE_SYMBOL(do_kern_addr_fault); +#ifdef CONFIG_OPENPAX_EMUTRAMP +/* + * Determine if a fault is possibly caused by an emulatable stack or + * heap trampoline. We return false if trampoline emulation is not + * enabled. + */ +static inline +bool openpax_fault_is_trampoline(unsigned long error_code, + struct pt_regs *regs, + unsigned long address) +{ + struct mm_struct *mm = current->mm; + unsigned long ip = regs->ip; + + if (!test_bit(PAXF_EMUTRAMP, &mm->pax_flags)) + return false; + + if (v8086_mode(regs)) + ip = ((regs->cs & 0xffff) << 4) + (ip & 0xffff); + + if (test_bit(PAXF_PAGEEXEC, &mm->pax_flags)) { + if ((__supported_pte_mask & _PAGE_NX) && (error_code & X86_PF_INSTR)) + return true; + if (!(error_code & (X86_PF_PROT | X86_PF_WRITE)) && ip == address) + return true; + return false; + } + + return false; +} +NOKPROBE_SYMBOL(openpax_fault_is_trampoline); + +static inline +bool openpax_emulate_trampoline_32(struct pt_regs *regs) +{ + int err; + + /* libffi trampoline type 1, gcc trampoline type 2 */ + do { + 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 || mov == 0xB9) && jmp == 0xE9) { + if (mov == 0xB8) + regs->ax = addr1; + else + regs->cx = addr1; + + regs->ip = (unsigned int)(regs->ip + addr2 + 10); + return true; + } + } while (0); + + /* older gcc trampoline type... */ + do { + 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 true; + } + } while (0); + + return false; +} +NOKPROBE_SYMBOL(openpax_emulate_trampoline_32); + +#ifdef CONFIG_X86_64 +static inline +bool openpax_emulate_trampoline_64(struct pt_regs *regs) +{ + int err; + + /* libffi trampoline type 1 */ + do { + 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 true; + } + } while (0); + + /* gcc trampoline type 1 */ + do { + 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 true; + } + } while (0); + + /* gcc trampoline type 2 */ + do { + 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 true; + } + } while (0); + + return false; +} +NOKPROBE_SYMBOL(openpax_emulate_trampoline_64); +#endif + +/* + * Emulate a trampoline. Returns false if emulation failed, meaning + * that the task should be killed. + */ +static inline +bool openpax_emulate_trampoline(struct pt_regs *regs) +{ + if (v8086_mode(regs)) + return false; + + if (regs->cs == __USER32_CS || (regs->cs & SEGMENT_LDT)) + return openpax_emulate_trampoline_32(regs); +#ifdef CONFIG_X86_64 + else + return openpax_emulate_trampoline_64(regs); +#endif + + return false; +} +NOKPROBE_SYMBOL(openpax_emulate_trampoline); +#endif + /* * Handle faults in the user portion of the address space. Nothing in here * should check X86_PF_USER without a specific justification: for almost @@ -1322,6 +1533,13 @@ void do_user_addr_fault(struct pt_regs *regs, } #endif +#ifdef CONFIG_OPENPAX_EMUTRAMP + if (openpax_fault_is_trampoline(error_code, regs, address)) { + if (openpax_emulate_trampoline(regs)) + return; + } +#endif + if (!(flags & FAULT_FLAG_USER)) goto lock_mmap; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 8054f44d39cf..00f436d6d0a8 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -822,6 +823,72 @@ static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr, return ret == -ENOENT ? 0 : ret; } +#ifdef CONFIG_OPENPAX +#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS +static int openpax_parse_xattr_flags(struct file * const file) +{ + ssize_t xattr_size, i; + unsigned char xattr_value[sizeof("pemrs") - 1]; + + xattr_size = pax_getxattr(file, xattr_value, sizeof xattr_value); + if (xattr_size < 0 || xattr_size > sizeof xattr_value) + return -ENOENT; + + for (i = 0; i < xattr_size; i++) + switch (xattr_value[i]) { + default: + return -EINVAL; + +#define parse_flag(option_disable, option_enable, flag) \ + case option_disable: \ + clear_bit(flag, ¤t->mm->pax_flags); \ + break; \ + case option_enable: \ + set_bit(flag, ¤t->mm->pax_flags); \ + break; + + parse_flag('p', 'P', PAXF_PAGEEXEC); + parse_flag('e', 'E', PAXF_EMUTRAMP); + parse_flag('m', 'M', PAXF_MPROTECT); + parse_flag('r', 'R', PAXF_RANDMMAP); + parse_flag('s', 'S', PAXF_SEGMEXEC); +#undef parse_flag + } + + return 0; +} +#endif + +static int openpax_set_flags(struct file * const file, const int snapshot_randomize_va_space) +{ +#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS + int error; +#endif + current->mm->pax_flags = 0; + + if (snapshot_randomize_va_space) { + set_bit(PAXF_RANDMMAP, ¤t->mm->pax_flags); + } + + if (!pax_softmode) { + set_bit(PAXF_PAGEEXEC, ¤t->mm->pax_flags); + set_bit(PAXF_MPROTECT, ¤t->mm->pax_flags); + } + +#ifdef CONFIG_OPENPAX_EMUTRAMP_DEFAULT + set_bit(PAXF_EMUTRAMP, ¤t->mm->pax_flags); +#endif + +#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS + error = openpax_parse_xattr_flags(file); + if (error != -ENOENT) + return error; +#endif + + return 0; +} +#endif + static int load_elf_binary(struct linux_binprm *bprm) { struct file *interpreter = NULL; /* to shut gcc up */ @@ -1006,11 +1073,28 @@ static int load_elf_binary(struct linux_binprm *bprm) /* Do this immediately, since STACK_TOP as used in setup_arg_pages may depend on the personality. */ SET_PERSONALITY2(*elf_ex, &arch_state); + + const int snapshot_randomize_va_space = READ_ONCE(randomize_va_space); + +#ifdef CONFIG_OPENPAX + retval = openpax_set_flags(bprm->file, snapshot_randomize_va_space); + if (retval) + goto out_free_dentry; + + if (test_bit(PAXF_PAGEEXEC, ¤t->mm->pax_flags) || test_bit(PAXF_SEGMEXEC, ¤t->mm->pax_flags)) { + executable_stack = EXSTACK_DISABLE_X; + current->personality &= ~READ_IMPLIES_EXEC; + } else +#endif + if (elf_read_implies_exec(*elf_ex, executable_stack)) current->personality |= READ_IMPLIES_EXEC; - const int snapshot_randomize_va_space = READ_ONCE(randomize_va_space); - if (!(current->personality & ADDR_NO_RANDOMIZE) && snapshot_randomize_va_space) + if (!(current->personality & ADDR_NO_RANDOMIZE) && snapshot_randomize_va_space +#ifdef CONFIG_OPENPAX + && test_bit(PAXF_RANDMMAP, ¤t->mm->pax_flags) +#endif + ) current->flags |= PF_RANDOMIZE; setup_new_exec(bprm); diff --git a/fs/exec.c b/fs/exec.c index 17047210be46..6fa84ac60ed1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -68,6 +68,7 @@ #include #include #include +#include #include #include @@ -285,6 +286,10 @@ static int __bprm_mm_init(struct linux_binprm *bprm) mm->stack_vm = mm->total_vm = 1; mmap_write_unlock(mm); bprm->p = vma->vm_end - sizeof(void *); + + if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) + bprm->p ^= get_random_u32() & ~PAGE_MASK; + return 0; err: ksm_exit(mm); diff --git a/fs/proc/array.c b/fs/proc/array.c index d6a0369caa93..242c8a969400 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -436,6 +436,18 @@ __weak void arch_proc_pid_thread_features(struct seq_file *m, { } +#ifdef CONFIG_OPENPAX +static inline void task_pax(struct seq_file *m, struct mm_struct *mm) +{ + seq_printf(m, "PaX:\t%c%c%c%c%c\n", + test_bit(PAXF_PAGEEXEC, &mm->pax_flags) ? 'P' : 'p', + test_bit(PAXF_EMUTRAMP, &mm->pax_flags) ? 'E' : 'e', + test_bit(PAXF_MPROTECT, &mm->pax_flags) ? 'M' : 'm', + test_bit(PAXF_RANDMMAP, &mm->pax_flags) ? 'R' : 'r', + test_bit(PAXF_SEGMEXEC, &mm->pax_flags) ? 'S' : 's'); +} +#endif + int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { @@ -452,6 +464,9 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, task_core_dumping(m, task); task_thp_status(m, mm); task_untag_mask(m, mm); +#ifdef CONFIG_OPENPAX + task_pax(m, mm); +#endif mmput(mm); } task_sig(m, task); diff --git a/fs/xattr.c b/fs/xattr.c index 02bee149ad96..394af28d3b08 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -424,6 +424,22 @@ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name, } EXPORT_SYMBOL(__vfs_getxattr); +#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS +ssize_t +pax_getxattr(struct file *file, void *value, size_t size) +{ + struct inode *inode = file->f_path.dentry->d_inode; + ssize_t error; + + error = inode_permission(file_mnt_idmap(file), inode, MAY_EXEC); + if (error) + return error; + + return __vfs_getxattr(file->f_path.dentry, inode, XATTR_NAME_USER_PAX_FLAGS, value, size); +} +EXPORT_SYMBOL(pax_getxattr); +#endif + ssize_t vfs_getxattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name, void *value, size_t size) diff --git a/include/linux/init.h b/include/linux/init.h index ee1309473bc6..4abbce4cf60b 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -144,6 +144,7 @@ extern char __initdata boot_command_line[]; extern char *saved_command_line; extern unsigned int saved_command_line_len; extern unsigned int reset_devices; +extern int pax_softmode; /* used by init/main.c */ void setup_arch(char **); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 0234f14f2aa6..fd8bd5517e4d 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -973,6 +973,9 @@ struct mm_struct { mm_context_t context; unsigned long flags; /* Must use atomic bitops to access */ +#ifdef CONFIG_OPENPAX + unsigned long pax_flags; +#endif #ifdef CONFIG_AIO spinlock_t ioctx_lock; @@ -1656,4 +1659,12 @@ static inline unsigned long mmf_init_flags(unsigned long flags) return flags & MMF_INIT_MASK; } +#ifdef CONFIG_OPENPAX +#define PAXF_PAGEEXEC 1 +#define PAXF_EMUTRAMP 2 +#define PAXF_MPROTECT 3 +#define PAXF_RANDMMAP 4 +#define PAXF_SEGMEXEC 5 +#endif + #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/linux/mman.h b/include/linux/mman.h index a842783ffa62..e108371ff12e 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -197,12 +197,21 @@ static inline bool arch_memory_deny_write_exec_supported(void) * we propose to set. * * Return: false if proposed change is OK, true if not ok and should be denied. + * + * Note: If OpenPaX is enabled, it will be assumed that we want to deny + * PROT_WRITE | PROT_EXEC by default, unless the MPROTECT feature bit is + * disabled on a binary. */ static inline bool map_deny_write_exec(unsigned long old, unsigned long new) { /* If MDWE is disabled, we have nothing to deny. */ - if (!test_bit(MMF_HAS_MDWE, ¤t->mm->flags)) + if ( +#ifdef CONFIG_OPENPAX_MPROTECT + !test_bit(PAXF_MPROTECT, ¤t->mm->pax_flags) && +#endif + !test_bit(MMF_HAS_MDWE, ¤t->mm->flags)) { return false; + } /* If the new VMA is not executable, we have nothing to deny. */ if (!(new & VM_EXEC)) diff --git a/include/linux/xattr.h b/include/linux/xattr.h index 86b0d47984a1..c4ad3af7e1a2 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -25,6 +25,7 @@ struct inode; struct dentry; +struct file; static inline bool is_posix_acl_xattr(const char *name) { @@ -75,6 +76,9 @@ struct xattr { size_t value_len; }; +#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS +ssize_t pax_getxattr(struct file *, void *, size_t); +#endif ssize_t __vfs_getxattr(struct dentry *, struct inode *, const char *, void *, size_t); ssize_t vfs_getxattr(struct mnt_idmap *, struct dentry *, const char *, void *, size_t); diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h index 9854f9cff3c6..843787b91ef0 100644 --- a/include/uapi/linux/xattr.h +++ b/include/uapi/linux/xattr.h @@ -88,5 +88,10 @@ struct xattr_args { #define XATTR_POSIX_ACL_DEFAULT "posix_acl_default" #define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT +/* User namespace */ +#define XATTR_PAX_PREFIX "pax." +#define XATTR_PAX_FLAGS_SUFFIX "flags" +#define XATTR_NAME_USER_PAX_FLAGS XATTR_USER_PREFIX XATTR_PAX_PREFIX XATTR_PAX_FLAGS_SUFFIX +#define XATTR_NAME_PAX_FLAGS XATTR_PAX_PREFIX XATTR_PAX_FLAGS_SUFFIX #endif /* _UAPI_LINUX_XATTR_H */ diff --git a/init/main.c b/init/main.c index 2a1757826397..4720dce1a3b9 100644 --- a/init/main.c +++ b/init/main.c @@ -188,6 +188,17 @@ static int __init set_reset_devices(char *str) __setup("reset_devices", set_reset_devices); +int pax_softmode; + +#ifdef CONFIG_OPENPAX_SOFTMODE +static int __init setup_pax_softmode(char *str) +{ + get_option(&str, &pax_softmode); + return 1; +} +__setup("pax_softmode=", setup_pax_softmode); +#endif + static const char *argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; const char *envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; static const char *panic_later, *panic_param; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index cb57da499ebb..e16633ca2eeb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1609,6 +1609,18 @@ int proc_do_static_key(const struct ctl_table *table, int write, return ret; } +#ifdef CONFIG_OPENPAX_SOFTMODE +static const struct ctl_table pax_table[] = { + { + .procname = "softmode", + .data = &pax_softmode, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = proc_dointvec, + }, +}; +#endif + static const struct ctl_table kern_table[] = { { .procname = "panic", @@ -2230,6 +2242,9 @@ int __init sysctl_init_bases(void) { register_sysctl_init("kernel", kern_table); register_sysctl_init("vm", vm_table); +#ifdef CONFIG_OPENPAX_SOFTMODE + register_sysctl_init("kernel/pax", pax_table); +#endif return 0; } diff --git a/mm/mmap.c b/mm/mmap.c index cda01071c7b1..61883423ef52 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -155,6 +155,13 @@ SYSCALL_DEFINE1(brk, unsigned long, brk) newbrk = PAGE_ALIGN(brk); oldbrk = PAGE_ALIGN(mm->brk); + /* properly handle unaligned min_brk as an empty heap */ + if (min_brk & ~PAGE_MASK) { + if (brk == min_brk) + newbrk -= PAGE_SIZE; + if (mm->brk == min_brk) + oldbrk -= PAGE_SIZE; + } if (oldbrk == newbrk) { mm->brk = brk; goto success; diff --git a/mm/util.c b/mm/util.c index 8c965474d329..f766a86f9414 100644 --- a/mm/util.c +++ b/mm/util.c @@ -389,9 +389,9 @@ unsigned long __weak arch_randomize_brk(struct mm_struct *mm) { /* Is the current task 32bit ? */ if (!IS_ENABLED(CONFIG_64BIT) || is_compat_task()) - return randomize_page(mm->brk, SZ_32M); + return mm->brk + get_random_long() % SZ_32M + PAGE_SIZE; - return randomize_page(mm->brk, SZ_1G); + return mm->brk + get_random_long() % SZ_1G + PAGE_SIZE; } unsigned long arch_mmap_rnd(void) diff --git a/security/Kconfig b/security/Kconfig index f10dbf15c294..2d301a36ec49 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -284,6 +284,7 @@ config LSM If unsure, leave this as the default. source "security/Kconfig.hardening" +source "security/Kconfig.openpax" endmenu diff --git a/security/Kconfig.openpax b/security/Kconfig.openpax new file mode 100644 index 000000000000..76ee145094d9 --- /dev/null +++ b/security/Kconfig.openpax @@ -0,0 +1,89 @@ +# +# OpenPaX configuration +# + +menu "OpenPaX options" + +config OPENPAX + bool "Enable OpenPaX features" + default y + help + This configuration setting enables OpenPaX features. + OpenPaX adds memory safety-related defenses to the kernel which + reduce the risks posed by exploitable memory safety bugs. + +config OPENPAX_SOFTMODE + bool "Support PaX soft mode" + default y + help + Enabling this option will allow you to configure OpenPaX + features to run in soft mode. In this mode, OpenPaX features + will be disabled by default, only running on applications + which explicitly enable them. + + Soft mode can be enabled via the kernel.pax.softmode sysctl, + or the pax_softmode=1 kernel command-line option. + +config OPENPAX_XATTR_PAX_FLAGS + bool "Use filesystem extended attributes to modify OpenPaX features" + depends on OPENPAX + default y + help + Enabling this option will allow you to control whether + OpenPaX features are enabled on a per-executable basis via + xattr attributes. + + For compatibility with the original PaX patch, the feature + flags are read from the user.pax.flags extended attribute. + + If you disable this feature, then all applications will run + with OpenPaX enabled by default. + +config OPENPAX_MPROTECT + bool "Enforce W^X for memory mappings" + depends on OPENPAX + default y + help + Enabling this option prevents programs from making pages + executable when they are also writable. In addition, it + also denies transition of writable mappings to executable + mappings. + + This feature is known to break programs which depend on + just-in-time (JIT) compilation. It is advisable to enable + this feature system-wide, but mark programs which have + JIT compilation appropriately so the W^X enforcement is + disabled for them. + +config OPENPAX_EMUTRAMP + bool "Emulate stack and heap trampolines" + depends on OPENPAX + default y + help + Enabling this option allows programs to depend on common + types of stack and heap trampolines (such as the ones + generated by GCC and libffi) to continue working despite + the stack and heap being non-executable memory. + + This option works by intercepting the page faults caused + by executing code in non-executable memory and emulating + the side effects that would have happened from executing + the trampoline. + + Most likely, you should say 'y' here. + +config OPENPAX_EMUTRAMP_DEFAULT + bool "Enable trampoline emulation by default" + depends on OPENPAX_EMUTRAMP + default y + help + Enabling this option allows programs which require + trampolines to be emulated to continue working by default. + + Otherwise, the emulation flag must be enabled in a binary's + PaX marking, e.g. with paxmark -E . + + If you do not say 'y' here, you will have to manually mark + all programs which require trampoline emulation. + +endmenu -- 2.49.0