|
Message-ID: <20110721130329.GA7309@albatros> Date: Thu, 21 Jul 2011 17:03:29 +0400 From: Vasiliy Kulikov <segoon@...nwall.com> To: kernel-hardening@...ts.openwall.com Subject: Re: GNU_STACK policy problem On Mon, Jul 18, 2011 at 23:48 +0400, Vasiliy Kulikov wrote: > I've written kernel part of PT_GNU_STACK NX enforcement for binaries and > trampolines emulation (the latter is taken from PaX as-is). It Just > Works for both 32 and 64 bits. If someone wants to test it, the patch is as follows (it doesn't handle DSOs as I've written before): (Some pr_err() should be removed.) diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 2dbf6bf..c6ddb02 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -559,6 +559,152 @@ static int is_f00f_bug(struct pt_regs *regs, unsigned long address) return 0; } +/* + * These hex patterns (both for 32 and 64 bits) for trampolines code are + * extracted directly from gcc sources, so the match shouldn't lead to + * false positive and false negative result. + */ +static bool handle_trampolines_32(struct pt_regs *regs) +{ + int err; + +#ifdef CONFIG_X86_64 + /* If we overflow maximum x86-32 address space, something goes + * really wrong */ + if ((regs->ip + 11) >> 32) + return; +#endif + + do { + unsigned char mov1, mov2; + unsigned short jmp; + unsigned int addr1, addr2; + + 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); + + do { + unsigned char mov, jmp; + unsigned int addr1, addr2; + + 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 true; + } + } while (0); + + /* Not a trampoline, don't block SEGFAULT */ + return false; +} + +#ifdef CONFIG_X86_64 +static bool handle_trampolines_64(struct pt_regs *regs) +{ + int err; + + 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); + + 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); + + /* Not a trampoline, don't block SEGFAULT */ + return false; +} +#endif + +static bool handle_trampolines(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + if ((current->flags & PF_EMULTRAMP) == 0) { + pr_err("tramp: no EMULTRAMP\n"); + return false; + } + + if (v8086_mode(regs)) { + pr_err("tramp: v8086\n"); + return false; + } + if ((error_code & PF_INSTR) == 0) { + pr_err("tramp: not PF_INSTR\n"); + return false; + } + +#ifdef CONFIG_X86_32 + return handle_trampolines_32(regs); +#else + if (regs->cs == __USER32_CS || (regs->cs & SEGMENT_LDT)) + return handle_trampolines_32(regs); + else + return handle_trampolines_64(regs); +#endif +} +EXPORT_SYMBOL_GPL(handle_trampolines); + static const char nx_warning[] = KERN_CRIT "kernel tried to execute NX-protected page - exploit attempt? (uid: %d)\n"; @@ -720,6 +867,9 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code, if (is_errata100(regs, address)) return; + if (handle_trampolines(regs, error_code, address)) + return; + if (unlikely(show_unhandled_signals)) show_signal_msg(regs, error_code, address, tsk); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 303983f..64330e5 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -32,6 +32,7 @@ #include <linux/elf.h> #include <linux/utsname.h> #include <linux/coredump.h> +#include <linux/pid_namespace.h> #include <asm/uaccess.h> #include <asm/param.h> #include <asm/page.h> @@ -556,6 +557,53 @@ static unsigned long randomize_stack_top(unsigned long stack_top) #endif } +static int get_execstack(bool gnu_stack_present, int p_flags) +{ + struct pid_namespace *ns; + int pf_x; + + if (gnu_stack_present) { + pf_x = p_flags & PF_X; + /* GNU_STACK => NX */ + if (!pf_x) + return EXSTACK_DISABLE_X; + + /* GNU_STACK => X */ + ns = current->nsproxy->pid_ns; + if (ns->execstack_mode & GNU_STACK_X_FORCE_NX) + return EXSTACK_DISABLE_X; + + return EXSTACK_ENABLE_X; + } else { + /* GNU_STACK => ? */ + if (ns->execstack_mode & NO_GNU_STACK_FORCE_NX) + return EXSTACK_DISABLE_X; + + return EXSTACK_DEFAULT; + } +} + +static bool need_emultramp(bool gnu_stack_present, int p_flags) +{ + struct pid_namespace *ns; + int pf_x; + + if (gnu_stack_present) { + pf_x = p_flags & PF_X; + /* GNU_STACK => NX */ + if (!pf_x) + return false; + + /* GNU_STACK => X */ + ns = current->nsproxy->pid_ns; + return ns->execstack_mode & GNU_STACK_X_EMULTRAMP; + } else { + /* GNU_STACK => ? */ + return ns->execstack_mode & NO_GNU_STACK_EMULTRAMP; + } + +} + static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) { struct file *interpreter = NULL; /* to shut gcc up */ @@ -571,12 +619,14 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) unsigned long interp_load_addr = 0; unsigned long start_code, end_code, start_data, end_data; unsigned long reloc_func_desc __maybe_unused = 0; - int executable_stack = EXSTACK_DEFAULT; + int executable_stack; unsigned long def_flags = 0; struct { struct elfhdr elf_ex; struct elfhdr interp_elf_ex; } *loc; + bool gnu_stack_present = false; + int gnu_stack_flags = 0; /* unused value */ loc = kmalloc(sizeof(*loc), GFP_KERNEL); if (!loc) { @@ -689,13 +739,16 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) elf_ppnt = elf_phdata; for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) if (elf_ppnt->p_type == PT_GNU_STACK) { - if (elf_ppnt->p_flags & PF_X) - executable_stack = EXSTACK_ENABLE_X; - else - executable_stack = EXSTACK_DISABLE_X; - break; + gnu_stack_present = true; + gnu_stack_flags = elf_ppnt->p_flags; } + executable_stack = get_execstack(gnu_stack_present, gnu_stack_flags); + if (need_emultramp(gnu_stack_present, gnu_stack_flags)) { + pr_err("need emultramp\n"); + current->flags |= PF_EMULTRAMP; + } + /* Some simple consistency checks for the interpreter */ if (elf_interpreter) { retval = -ELIBBAD; diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index 38d1032..c54c43c 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -16,6 +16,13 @@ struct pidmap { struct bsd_acct_struct; +enum execstack_mode { + GNU_STACK_X_FORCE_NX = 1, + GNU_STACK_X_EMULTRAMP = 2, + NO_GNU_STACK_FORCE_NX = 4, + NO_GNU_STACK_EMULTRAMP = 8 +}; + struct pid_namespace { struct kref kref; struct pidmap pidmap[PIDMAP_ENTRIES]; @@ -30,6 +37,7 @@ struct pid_namespace { #ifdef CONFIG_BSD_PROCESS_ACCT struct bsd_acct_struct *bacct; #endif + enum execstack_mode execstack_mode; }; extern struct pid_namespace init_pid_ns; diff --git a/include/linux/sched.h b/include/linux/sched.h index 496770a..1b4a25e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1765,6 +1765,7 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define PF_FROZEN 0x00010000 /* frozen for system suspend */ #define PF_FSTRANS 0x00020000 /* inside a filesystem transaction */ #define PF_KSWAPD 0x00040000 /* I am kswapd */ +#define PF_EMULTRAMP 0x00080000 /* gcc tramplolines must be emulated */ #define PF_LESS_THROTTLE 0x00100000 /* Throttle me less: I clean memory */ #define PF_KTHREAD 0x00200000 /* I am a kernel thread */ #define PF_RANDOMIZE 0x00400000 /* randomize virtual address space */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index f175d98..de049f9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -175,6 +175,8 @@ static int proc_taint(struct ctl_table *table, int write, static int proc_dmesg_restrict(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +static int proc_execstack_mode(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); #ifdef CONFIG_MAGIC_SYSRQ /* Note: sysrq code uses it's own private copy */ @@ -984,6 +986,12 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec, }, #endif + { + .procname = "execstack_mode", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_execstack_mode, + }, { } }; @@ -2426,6 +2434,15 @@ static int proc_dmesg_restrict(struct ctl_table *table, int write, } #endif +static int proc_execstack_mode(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + struct ctl_table tbl = *table; + tbl.data = ¤t->nsproxy->pid_ns->execstack_mode; + + return proc_dointvec_minmax(&tbl, write, buffer, lenp, ppos); +} + struct do_proc_dointvec_minmax_conv_param { int *min; int *max;
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.