From: Kurt Garloff 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. --- 25-akpm/arch/ia64/ia32/binfmt_elf32.c | 16 +++++++++++----- 25-akpm/arch/ia64/ia32/ia32priv.h | 2 +- 25-akpm/arch/mips/kernel/irixelf.c | 2 +- 25-akpm/arch/s390/kernel/binfmt_elf32.c | 4 ++-- 25-akpm/arch/s390/kernel/compat_exec.c | 3 ++- 25-akpm/arch/sparc64/kernel/binfmt_aout32.c | 2 +- 25-akpm/arch/x86_64/ia32/ia32_aout.c | 4 ++-- 25-akpm/arch/x86_64/ia32/ia32_binfmt.c | 15 ++++++++++----- 25-akpm/fs/binfmt_aout.c | 2 +- 25-akpm/fs/binfmt_elf.c | 12 +++++++++++- 25-akpm/fs/binfmt_som.c | 2 +- 25-akpm/fs/exec.c | 14 +++++++++++--- 25-akpm/include/asm-ia64/pgtable.h | 3 ++- 25-akpm/include/linux/binfmts.h | 8 +++++++- 25-akpm/include/linux/elf.h | 2 ++ 15 files changed, 65 insertions(+), 26 deletions(-) diff -puN arch/ia64/ia32/binfmt_elf32.c~noexec-stack arch/ia64/ia32/binfmt_elf32.c --- 25/arch/ia64/ia32/binfmt_elf32.c~noexec-stack 2004-04-06 20:48:39.394495672 -0700 +++ 25-akpm/arch/ia64/ia32/binfmt_elf32.c 2004-04-06 20:48:39.417492176 -0700 @@ -35,7 +35,7 @@ extern void ia64_elf32_init (struct pt_r 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 #undef SET_PERSONALITY @@ -149,7 +149,7 @@ ia64_elf32_init (struct pt_regs *regs) } int -ia32_setup_arg_pages (struct linux_binprm *bprm) +ia32_setup_arg_pages (struct linux_binprm *bprm, int executable_stack) { unsigned long stack_base; struct vm_area_struct *mpnt; @@ -178,8 +178,14 @@ ia32_setup_arg_pages (struct linux_binpr mpnt->vm_mm = current->mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = IA32_STACK_TOP; - mpnt->vm_page_prot = PAGE_COPY; - mpnt->vm_flags = VM_STACK_FLAGS; + if (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 = (mpnt->vm_flags & VM_EXEC)? + PAGE_COPY_EXEC: PAGE_COPY; mpnt->vm_ops = NULL; mpnt->vm_pgoff = 0; mpnt->vm_file = NULL; @@ -192,7 +198,7 @@ ia32_setup_arg_pages (struct linux_binpr struct page *page = bprm->page[i]; if (page) { 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; } diff -puN arch/ia64/ia32/ia32priv.h~noexec-stack arch/ia64/ia32/ia32priv.h --- 25/arch/ia64/ia32/ia32priv.h~noexec-stack 2004-04-06 20:48:39.396495368 -0700 +++ 25-akpm/arch/ia64/ia32/ia32priv.h 2004-04-06 20:48:39.418492024 -0700 @@ -494,7 +494,7 @@ struct ia32_user_desc { struct linux_binprm; 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 void ia32_load_segment_descriptors (struct task_struct *task); diff -puN arch/mips/kernel/irixelf.c~noexec-stack arch/mips/kernel/irixelf.c --- 25/arch/mips/kernel/irixelf.c~noexec-stack 2004-04-06 20:48:39.397495216 -0700 +++ 25-akpm/arch/mips/kernel/irixelf.c 2004-04-06 20:48:39.419491872 -0700 @@ -688,7 +688,7 @@ static int load_irix_binary(struct linux * change some of these later. */ current->mm->rss = 0; - setup_arg_pages(bprm); + setup_arg_pages(bprm, EXSTACK_DEFAULT); current->mm->start_stack = bprm->p; /* At this point, we assume that the image should be loaded at diff -puN arch/s390/kernel/binfmt_elf32.c~noexec-stack arch/s390/kernel/binfmt_elf32.c --- 25/arch/s390/kernel/binfmt_elf32.c~noexec-stack 2004-04-06 20:48:39.399494912 -0700 +++ 25-akpm/arch/s390/kernel/binfmt_elf32.c 2004-04-06 20:48:39.419491872 -0700 @@ -115,7 +115,7 @@ static inline int dump_regs32(struct pt_ #include #include -int setup_arg_pages32(struct linux_binprm *bprm); +int setup_arg_pages32(struct linux_binprm *bprm, int executable_stack); #define elf_prstatus elf_prstatus32 struct elf_prstatus32 @@ -166,7 +166,7 @@ struct elf_prpsinfo32 #undef start_thread #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 MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries," diff -puN arch/s390/kernel/compat_exec.c~noexec-stack arch/s390/kernel/compat_exec.c --- 25/arch/s390/kernel/compat_exec.c~noexec-stack 2004-04-06 20:48:39.400494760 -0700 +++ 25-akpm/arch/s390/kernel/compat_exec.c 2004-04-06 20:48:39.419491872 -0700 @@ -37,7 +37,7 @@ #undef STACK_TOP #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; struct vm_area_struct *mpnt; @@ -66,6 +66,7 @@ int setup_arg_pages32(struct linux_binpr mpnt->vm_mm = mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = STACK_TOP; + /* executable stack setting would be applied here */ mpnt->vm_page_prot = PAGE_COPY; mpnt->vm_flags = VM_STACK_FLAGS; mpnt->vm_ops = NULL; diff -puN arch/sparc64/kernel/binfmt_aout32.c~noexec-stack arch/sparc64/kernel/binfmt_aout32.c --- 25/arch/sparc64/kernel/binfmt_aout32.c~noexec-stack 2004-04-06 20:48:39.402494456 -0700 +++ 25-akpm/arch/sparc64/kernel/binfmt_aout32.c 2004-04-06 20:48:39.420491720 -0700 @@ -310,7 +310,7 @@ beyond_if: orig_thr_flags = current_thread_info()->flags; current_thread_info()->flags |= _TIF_32BIT; - retval = setup_arg_pages(bprm); + retval = setup_arg_pages(bprm, EXSTACK_DEFAULT); if (retval < 0) { current_thread_info()->flags = orig_thr_flags; diff -puN arch/x86_64/ia32/ia32_aout.c~noexec-stack arch/x86_64/ia32/ia32_aout.c --- 25/arch/x86_64/ia32/ia32_aout.c~noexec-stack 2004-04-06 20:48:39.403494304 -0700 +++ 25-akpm/arch/x86_64/ia32/ia32_aout.c 2004-04-06 20:48:39.421491568 -0700 @@ -35,7 +35,7 @@ #undef WARN_OLD #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_library(struct file*); @@ -395,7 +395,7 @@ beyond_if: 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) { /* Someone check-me: is this error path enough? */ send_sig(SIGKILL, current, 0); diff -puN arch/x86_64/ia32/ia32_binfmt.c~noexec-stack arch/x86_64/ia32/ia32_binfmt.c --- 25/arch/x86_64/ia32/ia32_binfmt.c~noexec-stack 2004-04-06 20:48:39.404494152 -0700 +++ 25-akpm/arch/x86_64/ia32/ia32_binfmt.c 2004-04-06 20:48:39.421491568 -0700 @@ -272,8 +272,8 @@ do { \ #define load_elf_binary load_elf32_binary #define ELF_PLAT_INIT(r, load_addr) elf32_init(r) -#define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm) -int ia32_setup_arg_pages(struct linux_binprm *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 executable_stack); #undef start_thread #define start_thread(regs,new_rip,new_rsp) do { \ @@ -325,7 +325,7 @@ static void elf32_init(struct pt_regs *r 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; struct vm_area_struct *mpnt; @@ -354,7 +354,12 @@ int setup_arg_pages(struct linux_binprm mpnt->vm_mm = mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; 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) ? PAGE_COPY_EXEC : PAGE_COPY; mpnt->vm_ops = NULL; @@ -370,7 +375,7 @@ int setup_arg_pages(struct linux_binprm struct page *page = bprm->page[i]; if (page) { 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; } diff -puN fs/binfmt_aout.c~noexec-stack fs/binfmt_aout.c --- 25/fs/binfmt_aout.c~noexec-stack 2004-04-06 20:48:39.405494000 -0700 +++ 25-akpm/fs/binfmt_aout.c 2004-04-06 20:48:39.422491416 -0700 @@ -413,7 +413,7 @@ beyond_if: set_brk(current->mm->start_brk, current->mm->brk); - retval = setup_arg_pages(bprm); + retval = setup_arg_pages(bprm, EXSTACK_DEFAULT); if (retval < 0) { /* Someone check-me: is this error path enough? */ send_sig(SIGKILL, current, 0); diff -puN fs/binfmt_elf.c~noexec-stack fs/binfmt_elf.c --- 25/fs/binfmt_elf.c~noexec-stack 2004-04-06 20:48:39.407493696 -0700 +++ 25-akpm/fs/binfmt_elf.c 2004-04-06 20:48:39.423491264 -0700 @@ -476,6 +476,7 @@ static int load_elf_binary(struct linux_ struct exec interp_ex; char passed_fileno[6]; struct files_struct *files; + int executable_stack = EXSTACK_DEFAULT; /* Get the exec-header */ elf_ex = *((struct elfhdr *) bprm->buf); @@ -599,6 +600,15 @@ static int load_elf_binary(struct linux_ 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 */ if (elf_interpreter) { interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; @@ -674,7 +684,7 @@ static int load_elf_binary(struct linux_ change some of these later */ current->mm->rss = 0; current->mm->free_area_cache = TASK_UNMAPPED_BASE; - retval = setup_arg_pages(bprm); + retval = setup_arg_pages(bprm, executable_stack); if (retval < 0) { send_sig(SIGKILL, current, 0); goto out_free_dentry; diff -puN fs/binfmt_som.c~noexec-stack fs/binfmt_som.c --- 25/fs/binfmt_som.c~noexec-stack 2004-04-06 20:48:39.409493392 -0700 +++ 25-akpm/fs/binfmt_som.c 2004-04-06 20:48:39.424491112 -0700 @@ -254,7 +254,7 @@ load_som_binary(struct linux_binprm * bp set_binfmt(&som_format); compute_creds(bprm); - setup_arg_pages(bprm); + setup_arg_pages(bprm, EXSTACK_DEFAULT); create_som_tables(bprm); diff -puN fs/exec.c~noexec-stack fs/exec.c --- 25/fs/exec.c~noexec-stack 2004-04-06 20:48:39.410493240 -0700 +++ 25-akpm/fs/exec.c 2004-04-06 20:48:39.425490960 -0700 @@ -342,7 +342,7 @@ out_sig: return; } -int setup_arg_pages(struct linux_binprm *bprm) +int setup_arg_pages(struct linux_binprm *bprm, int executable_stack) { unsigned long stack_base; struct vm_area_struct *mpnt; @@ -425,8 +425,16 @@ int setup_arg_pages(struct linux_binprm mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = STACK_TOP; #endif - mpnt->vm_page_prot = protection_map[VM_STACK_FLAGS & 0x7]; - mpnt->vm_flags = VM_STACK_FLAGS; + /* Adjust stack execute permissions; explicitly enable + * 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_pgoff = 0; mpnt->vm_file = NULL; diff -puN include/linux/binfmts.h~noexec-stack include/linux/binfmts.h --- 25/include/linux/binfmts.h~noexec-stack 2004-04-06 20:48:39.411493088 -0700 +++ 25-akpm/include/linux/binfmts.h 2004-04-06 20:48:39.425490960 -0700 @@ -58,7 +58,13 @@ extern int prepare_binprm(struct linux_b extern void remove_arg_zero(struct linux_binprm *); extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); 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_kernel(int argc,char ** argv,struct linux_binprm *bprm); extern void compute_creds(struct linux_binprm *binprm); diff -puN include/linux/elf.h~noexec-stack include/linux/elf.h --- 25/include/linux/elf.h~noexec-stack 2004-04-06 20:48:39.412492936 -0700 +++ 25-akpm/include/linux/elf.h 2004-04-06 20:48:39.426490808 -0700 @@ -35,6 +35,8 @@ typedef __s64 Elf64_Sxword; #define PT_HIPROC 0x7fffffff #define PT_GNU_EH_FRAME 0x6474e550 +#define PT_GNU_STACK (PT_LOOS + 0x474e551) + /* These constants define the different elf file types */ #define ET_NONE 0 #define ET_REL 1 diff -puN include/asm-ia64/pgtable.h~noexec-stack include/asm-ia64/pgtable.h --- 25/include/asm-ia64/pgtable.h~noexec-stack 2004-04-06 20:48:39.414492632 -0700 +++ 25-akpm/include/asm-ia64/pgtable.h 2004-04-06 20:48:39.427490656 -0700 @@ -119,7 +119,8 @@ #define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_A) #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_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_KERNEL __pgprot(__DIRTY_BITS | _PAGE_PL_0 | _PAGE_AR_RWX) #define PAGE_KERNELRX __pgprot(__ACCESS_BITS | _PAGE_PL_0 | _PAGE_AR_RX) _