Commit 01cc53b2 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Non-Exec stack support

From: Kurt Garloff <garloff@suse.de>

A patch to parse the elf binaries for a PT_GNU_STACK section to set the stack
non-executable if possible.  Most parts have been shamelessly stolen from
Ingo Molnar's more ambitious stackshield
http://people.redhat.com/mingo/exec-shield/exec-shield-2.6.4-C9

The toolchain has meanwhile support for marking the binaries with a
PT_GNU_STACK section wwithout x bit as needed.

If no such section is found, we leave the stack to whatever the arch defaults
to.  If there is one, we explicitly disabled the VM_EXEC bit if no x bit is
found, otherwise explicitly enable.
parent 492361a6
...@@ -35,7 +35,7 @@ extern void ia64_elf32_init (struct pt_regs *regs); ...@@ -35,7 +35,7 @@ extern void ia64_elf32_init (struct pt_regs *regs);
static void elf32_set_personality (void); static void elf32_set_personality (void);
#define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm) #define setup_arg_pages(bprm,exec) ia32_setup_arg_pages(bprm,exec)
#define elf_map elf32_map #define elf_map elf32_map
#undef SET_PERSONALITY #undef SET_PERSONALITY
...@@ -149,7 +149,7 @@ ia64_elf32_init (struct pt_regs *regs) ...@@ -149,7 +149,7 @@ ia64_elf32_init (struct pt_regs *regs)
} }
int int
ia32_setup_arg_pages (struct linux_binprm *bprm) ia32_setup_arg_pages (struct linux_binprm *bprm, int executable_stack)
{ {
unsigned long stack_base; unsigned long stack_base;
struct vm_area_struct *mpnt; struct vm_area_struct *mpnt;
...@@ -178,8 +178,14 @@ ia32_setup_arg_pages (struct linux_binprm *bprm) ...@@ -178,8 +178,14 @@ ia32_setup_arg_pages (struct linux_binprm *bprm)
mpnt->vm_mm = current->mm; mpnt->vm_mm = current->mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = IA32_STACK_TOP; mpnt->vm_end = IA32_STACK_TOP;
mpnt->vm_page_prot = PAGE_COPY; if (executable_stack == EXSTACK_ENABLE_X)
mpnt->vm_flags = VM_STACK_FLAGS; mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC;
else if (executable_stack == EXSTACK_DISABLE_X)
mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC;
else
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC)?
PAGE_COPY_EXEC: PAGE_COPY;
mpnt->vm_ops = NULL; mpnt->vm_ops = NULL;
mpnt->vm_pgoff = 0; mpnt->vm_pgoff = 0;
mpnt->vm_file = NULL; mpnt->vm_file = NULL;
...@@ -192,7 +198,7 @@ ia32_setup_arg_pages (struct linux_binprm *bprm) ...@@ -192,7 +198,7 @@ ia32_setup_arg_pages (struct linux_binprm *bprm)
struct page *page = bprm->page[i]; struct page *page = bprm->page[i];
if (page) { if (page) {
bprm->page[i] = NULL; bprm->page[i] = NULL;
put_dirty_page(current, page, stack_base, PAGE_COPY); put_dirty_page(current, page, stack_base, mpnt->vm_page_prot);
} }
stack_base += PAGE_SIZE; stack_base += PAGE_SIZE;
} }
......
...@@ -494,7 +494,7 @@ struct ia32_user_desc { ...@@ -494,7 +494,7 @@ struct ia32_user_desc {
struct linux_binprm; struct linux_binprm;
extern void ia32_init_addr_space (struct pt_regs *regs); extern void ia32_init_addr_space (struct pt_regs *regs);
extern int ia32_setup_arg_pages (struct linux_binprm *bprm); extern int ia32_setup_arg_pages (struct linux_binprm *bprm, int exec_stack);
extern unsigned long ia32_do_mmap (struct file *, unsigned long, unsigned long, int, int, loff_t); extern unsigned long ia32_do_mmap (struct file *, unsigned long, unsigned long, int, int, loff_t);
extern void ia32_load_segment_descriptors (struct task_struct *task); extern void ia32_load_segment_descriptors (struct task_struct *task);
......
...@@ -688,7 +688,7 @@ static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -688,7 +688,7 @@ static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs)
* change some of these later. * change some of these later.
*/ */
current->mm->rss = 0; current->mm->rss = 0;
setup_arg_pages(bprm); setup_arg_pages(bprm, EXSTACK_DEFAULT);
current->mm->start_stack = bprm->p; current->mm->start_stack = bprm->p;
/* At this point, we assume that the image should be loaded at /* At this point, we assume that the image should be loaded at
......
...@@ -115,7 +115,7 @@ static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs) ...@@ -115,7 +115,7 @@ static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs)
#include <linux/binfmts.h> #include <linux/binfmts.h>
#include <linux/compat.h> #include <linux/compat.h>
int setup_arg_pages32(struct linux_binprm *bprm); int setup_arg_pages32(struct linux_binprm *bprm, int executable_stack);
#define elf_prstatus elf_prstatus32 #define elf_prstatus elf_prstatus32
struct elf_prstatus32 struct elf_prstatus32
...@@ -166,7 +166,7 @@ struct elf_prpsinfo32 ...@@ -166,7 +166,7 @@ struct elf_prpsinfo32
#undef start_thread #undef start_thread
#define start_thread start_thread31 #define start_thread start_thread31
#define setup_arg_pages(bprm) setup_arg_pages32(bprm) #define setup_arg_pages(bprm, exec) setup_arg_pages32(bprm, exec)
#define elf_map elf_map32 #define elf_map elf_map32
MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries," MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries,"
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
#undef STACK_TOP #undef STACK_TOP
#define STACK_TOP TASK31_SIZE #define STACK_TOP TASK31_SIZE
int setup_arg_pages32(struct linux_binprm *bprm) int setup_arg_pages32(struct linux_binprm *bprm, int executable_stack)
{ {
unsigned long stack_base; unsigned long stack_base;
struct vm_area_struct *mpnt; struct vm_area_struct *mpnt;
...@@ -66,6 +66,7 @@ int setup_arg_pages32(struct linux_binprm *bprm) ...@@ -66,6 +66,7 @@ int setup_arg_pages32(struct linux_binprm *bprm)
mpnt->vm_mm = mm; mpnt->vm_mm = mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = STACK_TOP; mpnt->vm_end = STACK_TOP;
/* executable stack setting would be applied here */
mpnt->vm_page_prot = PAGE_COPY; mpnt->vm_page_prot = PAGE_COPY;
mpnt->vm_flags = VM_STACK_FLAGS; mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_ops = NULL; mpnt->vm_ops = NULL;
......
...@@ -310,7 +310,7 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -310,7 +310,7 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
orig_thr_flags = current_thread_info()->flags; orig_thr_flags = current_thread_info()->flags;
current_thread_info()->flags |= _TIF_32BIT; current_thread_info()->flags |= _TIF_32BIT;
retval = setup_arg_pages(bprm); retval = setup_arg_pages(bprm, EXSTACK_DEFAULT);
if (retval < 0) { if (retval < 0) {
current_thread_info()->flags = orig_thr_flags; current_thread_info()->flags = orig_thr_flags;
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#undef WARN_OLD #undef WARN_OLD
#undef CORE_DUMP /* probably broken */ #undef CORE_DUMP /* probably broken */
extern int ia32_setup_arg_pages(struct linux_binprm *bprm); extern int ia32_setup_arg_pages(struct linux_binprm *bprm, int exec_stack);
static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs); static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
static int load_aout_library(struct file*); static int load_aout_library(struct file*);
...@@ -395,7 +395,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -395,7 +395,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
set_brk(current->mm->start_brk, current->mm->brk); set_brk(current->mm->start_brk, current->mm->brk);
retval = ia32_setup_arg_pages(bprm); retval = ia32_setup_arg_pages(bprm, EXSTACK_DEFAULT);
if (retval < 0) { if (retval < 0) {
/* Someone check-me: is this error path enough? */ /* Someone check-me: is this error path enough? */
send_sig(SIGKILL, current, 0); send_sig(SIGKILL, current, 0);
......
...@@ -272,8 +272,8 @@ do { \ ...@@ -272,8 +272,8 @@ do { \
#define load_elf_binary load_elf32_binary #define load_elf_binary load_elf32_binary
#define ELF_PLAT_INIT(r, load_addr) elf32_init(r) #define ELF_PLAT_INIT(r, load_addr) elf32_init(r)
#define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm) #define setup_arg_pages(bprm, exec_stack) ia32_setup_arg_pages(bprm, exec_stack)
int ia32_setup_arg_pages(struct linux_binprm *bprm); int ia32_setup_arg_pages(struct linux_binprm *bprm, int executable_stack);
#undef start_thread #undef start_thread
#define start_thread(regs,new_rip,new_rsp) do { \ #define start_thread(regs,new_rip,new_rsp) do { \
...@@ -325,7 +325,7 @@ static void elf32_init(struct pt_regs *regs) ...@@ -325,7 +325,7 @@ static void elf32_init(struct pt_regs *regs)
me->thread.es = __USER_DS; me->thread.es = __USER_DS;
} }
int setup_arg_pages(struct linux_binprm *bprm) int setup_arg_pages(struct linux_binprm *bprm, int executable_stack)
{ {
unsigned long stack_base; unsigned long stack_base;
struct vm_area_struct *mpnt; struct vm_area_struct *mpnt;
...@@ -354,7 +354,12 @@ int setup_arg_pages(struct linux_binprm *bprm) ...@@ -354,7 +354,12 @@ int setup_arg_pages(struct linux_binprm *bprm)
mpnt->vm_mm = mm; mpnt->vm_mm = mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = IA32_STACK_TOP; mpnt->vm_end = IA32_STACK_TOP;
mpnt->vm_flags = vm_stack_flags32; if (executable_stack == EXSTACK_ENABLE_X)
mpnt->vm_flags = vm_stack_flags32 | VM_EXEC;
else if (executable_stack == EXSTACK_DISABLE_X)
mpnt->vm_flags = vm_stack_flags32 & ~VM_EXEC;
else
mpnt->vm_flags = vm_stack_flags32;
mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ?
PAGE_COPY_EXEC : PAGE_COPY; PAGE_COPY_EXEC : PAGE_COPY;
mpnt->vm_ops = NULL; mpnt->vm_ops = NULL;
...@@ -370,7 +375,7 @@ int setup_arg_pages(struct linux_binprm *bprm) ...@@ -370,7 +375,7 @@ int setup_arg_pages(struct linux_binprm *bprm)
struct page *page = bprm->page[i]; struct page *page = bprm->page[i];
if (page) { if (page) {
bprm->page[i] = NULL; bprm->page[i] = NULL;
put_dirty_page(current,page,stack_base,PAGE_COPY_EXEC); put_dirty_page(current,page,stack_base,mpnt->vm_page_prot);
} }
stack_base += PAGE_SIZE; stack_base += PAGE_SIZE;
} }
......
...@@ -413,7 +413,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -413,7 +413,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
set_brk(current->mm->start_brk, current->mm->brk); set_brk(current->mm->start_brk, current->mm->brk);
retval = setup_arg_pages(bprm); retval = setup_arg_pages(bprm, EXSTACK_DEFAULT);
if (retval < 0) { if (retval < 0) {
/* Someone check-me: is this error path enough? */ /* Someone check-me: is this error path enough? */
send_sig(SIGKILL, current, 0); send_sig(SIGKILL, current, 0);
......
...@@ -476,6 +476,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -476,6 +476,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
struct exec interp_ex; struct exec interp_ex;
char passed_fileno[6]; char passed_fileno[6];
struct files_struct *files; struct files_struct *files;
int executable_stack = EXSTACK_DEFAULT;
/* Get the exec-header */ /* Get the exec-header */
elf_ex = *((struct elfhdr *) bprm->buf); elf_ex = *((struct elfhdr *) bprm->buf);
...@@ -599,6 +600,15 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -599,6 +600,15 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
elf_ppnt++; elf_ppnt++;
} }
elf_ppnt = elf_phdata;
for (i = 0; i < 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;
}
/* Some simple consistency checks for the interpreter */ /* Some simple consistency checks for the interpreter */
if (elf_interpreter) { if (elf_interpreter) {
interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
...@@ -674,7 +684,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -674,7 +684,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
change some of these later */ change some of these later */
current->mm->rss = 0; current->mm->rss = 0;
current->mm->free_area_cache = TASK_UNMAPPED_BASE; current->mm->free_area_cache = TASK_UNMAPPED_BASE;
retval = setup_arg_pages(bprm); retval = setup_arg_pages(bprm, executable_stack);
if (retval < 0) { if (retval < 0) {
send_sig(SIGKILL, current, 0); send_sig(SIGKILL, current, 0);
goto out_free_dentry; goto out_free_dentry;
......
...@@ -254,7 +254,7 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -254,7 +254,7 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
set_binfmt(&som_format); set_binfmt(&som_format);
compute_creds(bprm); compute_creds(bprm);
setup_arg_pages(bprm); setup_arg_pages(bprm, EXSTACK_DEFAULT);
create_som_tables(bprm); create_som_tables(bprm);
......
...@@ -342,7 +342,7 @@ void put_dirty_page(struct task_struct *tsk, struct page *page, ...@@ -342,7 +342,7 @@ void put_dirty_page(struct task_struct *tsk, struct page *page,
return; return;
} }
int setup_arg_pages(struct linux_binprm *bprm) int setup_arg_pages(struct linux_binprm *bprm, int executable_stack)
{ {
unsigned long stack_base; unsigned long stack_base;
struct vm_area_struct *mpnt; struct vm_area_struct *mpnt;
...@@ -425,8 +425,16 @@ int setup_arg_pages(struct linux_binprm *bprm) ...@@ -425,8 +425,16 @@ int setup_arg_pages(struct linux_binprm *bprm)
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = STACK_TOP; mpnt->vm_end = STACK_TOP;
#endif #endif
mpnt->vm_page_prot = protection_map[VM_STACK_FLAGS & 0x7]; /* Adjust stack execute permissions; explicitly enable
mpnt->vm_flags = VM_STACK_FLAGS; * for EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X
* and leave alone (arch default) otherwise. */
if (unlikely(executable_stack == EXSTACK_ENABLE_X))
mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC;
else if (executable_stack == EXSTACK_DISABLE_X)
mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC;
else
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7];
mpnt->vm_ops = NULL; mpnt->vm_ops = NULL;
mpnt->vm_pgoff = 0; mpnt->vm_pgoff = 0;
mpnt->vm_file = NULL; mpnt->vm_file = NULL;
......
...@@ -119,7 +119,8 @@ ...@@ -119,7 +119,8 @@
#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_A) #define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_A)
#define PAGE_SHARED __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RW) #define PAGE_SHARED __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RW)
#define PAGE_READONLY __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_R) #define PAGE_READONLY __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_R)
#define PAGE_COPY __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RX) #define PAGE_COPY __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_R)
#define PAGE_COPY_EXEC __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RX)
#define PAGE_GATE __pgprot(__ACCESS_BITS | _PAGE_PL_0 | _PAGE_AR_X_RX) #define PAGE_GATE __pgprot(__ACCESS_BITS | _PAGE_PL_0 | _PAGE_AR_X_RX)
#define PAGE_KERNEL __pgprot(__DIRTY_BITS | _PAGE_PL_0 | _PAGE_AR_RWX) #define PAGE_KERNEL __pgprot(__DIRTY_BITS | _PAGE_PL_0 | _PAGE_AR_RWX)
#define PAGE_KERNELRX __pgprot(__ACCESS_BITS | _PAGE_PL_0 | _PAGE_AR_RX) #define PAGE_KERNELRX __pgprot(__ACCESS_BITS | _PAGE_PL_0 | _PAGE_AR_RX)
......
...@@ -58,7 +58,13 @@ extern int prepare_binprm(struct linux_binprm *); ...@@ -58,7 +58,13 @@ extern int prepare_binprm(struct linux_binprm *);
extern void remove_arg_zero(struct linux_binprm *); extern void remove_arg_zero(struct linux_binprm *);
extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); extern int search_binary_handler(struct linux_binprm *,struct pt_regs *);
extern int flush_old_exec(struct linux_binprm * bprm); extern int flush_old_exec(struct linux_binprm * bprm);
extern int setup_arg_pages(struct linux_binprm * bprm);
/* Stack area protections */
#define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */
#define EXSTACK_DISABLE_X 1 /* Disable executable stacks */
#define EXSTACK_ENABLE_X 2 /* Enable executable stacks */
extern int setup_arg_pages(struct linux_binprm * bprm, int executable_stack);
extern int copy_strings(int argc,char __user * __user * argv,struct linux_binprm *bprm); extern int copy_strings(int argc,char __user * __user * argv,struct linux_binprm *bprm);
extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm); extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm);
extern void compute_creds(struct linux_binprm *binprm); extern void compute_creds(struct linux_binprm *binprm);
......
...@@ -35,6 +35,8 @@ typedef __s64 Elf64_Sxword; ...@@ -35,6 +35,8 @@ typedef __s64 Elf64_Sxword;
#define PT_HIPROC 0x7fffffff #define PT_HIPROC 0x7fffffff
#define PT_GNU_EH_FRAME 0x6474e550 #define PT_GNU_EH_FRAME 0x6474e550
#define PT_GNU_STACK (PT_LOOS + 0x474e551)
/* These constants define the different elf file types */ /* These constants define the different elf file types */
#define ET_NONE 0 #define ET_NONE 0
#define ET_REL 1 #define ET_REL 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment