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.


---

 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/linux/binfmts.h             |    8 +++++++-
 25-akpm/include/linux/elf.h                 |    2 ++
 14 files changed, 63 insertions(+), 25 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-03-31 23:28:35.789117688 -0800
+++ 25-akpm/arch/ia64/ia32/binfmt_elf32.c	2004-03-31 23:28:35.810114496 -0800
@@ -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-03-31 23:28:35.790117536 -0800
+++ 25-akpm/arch/ia64/ia32/ia32priv.h	2004-03-31 23:28:35.811114344 -0800
@@ -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-03-31 23:28:35.791117384 -0800
+++ 25-akpm/arch/mips/kernel/irixelf.c	2004-03-31 23:28:35.812114192 -0800
@@ -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-03-31 23:28:35.793117080 -0800
+++ 25-akpm/arch/s390/kernel/binfmt_elf32.c	2004-03-31 23:28:35.812114192 -0800
@@ -115,7 +115,7 @@ static inline int dump_regs32(struct pt_
 #include <linux/binfmts.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
 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-03-31 23:28:35.795116776 -0800
+++ 25-akpm/arch/s390/kernel/compat_exec.c	2004-03-31 23:28:35.813114040 -0800
@@ -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-03-31 23:28:35.796116624 -0800
+++ 25-akpm/arch/sparc64/kernel/binfmt_aout32.c	2004-03-31 23:28:35.813114040 -0800
@@ -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-03-31 23:28:35.797116472 -0800
+++ 25-akpm/arch/x86_64/ia32/ia32_aout.c	2004-03-31 23:28:35.814113888 -0800
@@ -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-03-31 23:28:35.799116168 -0800
+++ 25-akpm/arch/x86_64/ia32/ia32_binfmt.c	2004-03-31 23:28:35.814113888 -0800
@@ -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-03-31 23:28:35.800116016 -0800
+++ 25-akpm/fs/binfmt_aout.c	2004-03-31 23:28:35.815113736 -0800
@@ -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-03-31 23:28:35.801115864 -0800
+++ 25-akpm/fs/binfmt_elf.c	2004-03-31 23:28:35.817113432 -0800
@@ -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-03-31 23:28:35.803115560 -0800
+++ 25-akpm/fs/binfmt_som.c	2004-03-31 23:28:35.817113432 -0800
@@ -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-03-31 23:28:35.804115408 -0800
+++ 25-akpm/fs/exec.c	2004-03-31 23:28:35.818113280 -0800
@@ -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-03-31 23:28:35.806115104 -0800
+++ 25-akpm/include/linux/binfmts.h	2004-03-31 23:28:35.819113128 -0800
@@ -62,7 +62,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-03-31 23:28:35.807114952 -0800
+++ 25-akpm/include/linux/elf.h	2004-03-31 23:28:35.819113128 -0800
@@ -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

_