sys-kernel/openpax-kernel: bump new package

Signed-off-by: Alexander Miroshnichenko <alex@millerson.name>
This commit is contained in:
2025-04-29 18:45:14 +03:00
parent 64289aa160
commit 67b1755e81
6 changed files with 40240 additions and 0 deletions

View File

@@ -0,0 +1,868 @@
From 816e436fea63e979f20d56f4555598f70e3b2ac8 Mon Sep 17 00:00:00 2001
From: Alexander Miroshnichenko <alex@millerson.name>
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 <alex@millerson.name>
---
.../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=<int>
+ 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 <linux/dax.h>
#include <linux/uaccess.h>
#include <linux/rseq.h>
+#include <linux/xattr.h>
#include <asm/param.h>
#include <asm/page.h>
@@ -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, &current->mm->pax_flags); \
+ break; \
+ case option_enable: \
+ set_bit(flag, &current->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, &current->mm->pax_flags);
+ }
+
+ if (!pax_softmode) {
+ set_bit(PAXF_PAGEEXEC, &current->mm->pax_flags);
+ set_bit(PAXF_MPROTECT, &current->mm->pax_flags);
+ }
+
+#ifdef CONFIG_OPENPAX_EMUTRAMP_DEFAULT
+ set_bit(PAXF_EMUTRAMP, &current->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, &current->mm->pax_flags) || test_bit(PAXF_SEGMEXEC, &current->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, &current->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 <linux/user_events.h>
#include <linux/rseq.h>
#include <linux/ksm.h>
+#include <linux/random.h>
#include <linux/uaccess.h>
#include <asm/mmu_context.h>
@@ -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, &current->mm->flags))
+ if (
+#ifdef CONFIG_OPENPAX_MPROTECT
+ !test_bit(PAXF_MPROTECT, &current->mm->pax_flags) &&
+#endif
+ !test_bit(MMF_HAS_MDWE, &current->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 <binary>.
+
+ If you do not say 'y' here, you will have to manually mark
+ all programs which require trampoline emulation.
+
+endmenu
--
2.49.0