Skip to contentSkip to conversation

exec-shield patch for 2.6.7-rc2-bk2, integrated with NX

PATCH In review Ingo Molnar 3 participants 4 messages Jun 2, 2004 – Jun 4, 2004

Subsystems (10)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 exec-shield patch for 2.6.7-rc2-bk2, integrated with NX

Here's the latest exec-shield patch for 2.6.7-rc2-bk2, integrated with the 'NX' feature (see the announcement from earlier today):

http://redhat.com/~mingo/exec-shield/exec-shield-on-nx-2.6.7-rc2-bk2-A7

you first have to apply the NX patch, which can be found at:

http://redhat.com/~mingo/nx-patches/nx-2.6.7-rc2-bk2-AE

prebuild kernel RPMs for Fedora Core 2, with this latest version of exec-shield, are available at:

    http://redhat.com/~arjanv/2.6/RPMS.kernel/

(kernel-2.6.6-1.411 has this latest, NX-aware exec-shield.)

if the CPU supports NX (and the kernel has been compiled with CONFIG_HIGHMEM64G) then exec-shield will use NX to provide page-level finegrained control over execution. On legacy CPUs that dont support NX the segment-limit method is used to control execution (in a coarser way). In the NX case the segment-limit is turned off altogether.

e.g. on an Athlon64 box the boot message looks:

NX (Execute Disable) protection: active

on a CPU without NX the boot message is:

NX (Execute Disable) protection: not present! Using x86 segment limits to approximate NX protection

note: the NX patch will also protect against kernel-space code injection.

all the other components of exec-shield are identical between NX and
non-NX: the brk area is non-executable, libraries and PIE binaries are moved into the ascii-shield as much as possible, and all aspects of the address space are randomized.

Ingo

#2 lore raw
Quoted 8 lines · Christoph Hellwig
Quoted 4 lines

Here's the latest exec-shield patch for 2.6.7-rc2-bk2, integrated with the 'NX' feature (see the announcement from earlier today):

http://redhat.com/~mingo/exec-shield/exec-shield-on-nx-2.6.7-rc2-bk2-A7

Any chance to split this up a bit? Having the pure non-exec stack (and maybe heap) would be really nice while the randomization feature are a litte bit too much security by obscurity for my taste.

below is a version of the -A9 exec-shield that has all randomization code removed (ontop of the -AF version of the NX patch). The address-space layout changes are still needed to 1) get near the maximum possible amount for data mmap() and malloc() space and to 2) move all executable mappings close to each other to get a maximum effect from the segment limit.

(but i still think randomization is useful as a last-resort barrier, against worm-alike mass attacks. There's a huge difference between a 1-packet infection and a 2-hour brute-force search over broadband, in terms of the 'economy' of worms.)

Ingo

drivers/char/drm/ffb_drv.c renamed from drivers/char/drm/ffb_drv.c.orig +4 -4
@@ -285,19 +285,19 @@ static unsigned long ffb_get_unmapped_ar
 	unsigned long addr = -ENOMEM;
 
 	if (!map)
-		return get_unmapped_area(NULL, hint, len, pgoff, flags);
+		return get_unmapped_area(NULL, hint, len, pgoff, flags, 0);
 
 	if (map->type == _DRM_FRAME_BUFFER ||
 	    map->type == _DRM_REGISTERS) {
 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
 		addr = get_fb_unmapped_area(filp, hint, len, pgoff, flags);
 #else
-		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags, 0);
 #endif
 	} else if (map->type == _DRM_SHM && SHMLBA > PAGE_SIZE) {
 		unsigned long slack = SHMLBA - PAGE_SIZE;
 
-		addr = get_unmapped_area(NULL, hint, len + slack, pgoff, flags);
+		addr = get_unmapped_area(NULL, hint, len + slack, pgoff, flags, 0);
 		if (!(addr & ~PAGE_MASK)) {
 			unsigned long kvirt = (unsigned long) map->handle;
 
@@ -313,7 +313,7 @@ static unsigned long ffb_get_unmapped_ar
 			}
 		}
 	} else {
-		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags, 0);
 	}
 
 	return addr;
arch/sparc64/kernel/sys_sparc32.c renamed from arch/sparc64/kernel/sys_sparc32.c.orig +1 -1
@@ -1731,7 +1731,7 @@ asmlinkage unsigned long sys32_mremap(un
 		/* MREMAP_FIXED checked above. */
 		new_addr = get_unmapped_area(file, addr, new_len,
 				    vma ? vma->vm_pgoff : 0,
-				    map_flags);
+				    map_flags, vma->vm_flags & VM_EXEC);
 		ret = new_addr;
 		if (new_addr & ~PAGE_MASK)
 			goto out_sem;
arch/sparc64/kernel/sys_sparc.c renamed from arch/sparc64/kernel/sys_sparc.c.orig +4 -4
@@ -127,7 +127,7 @@ unsigned long get_fb_unmapped_area(struc
 
 	if (flags & MAP_FIXED) {
 		/* Ok, don't mess with it. */
-		return get_unmapped_area(NULL, addr, len, pgoff, flags);
+		return get_unmapped_area(NULL, addr, len, pgoff, flags, 0);
 	}
 	flags &= ~MAP_SHARED;
 
@@ -140,7 +140,7 @@ unsigned long get_fb_unmapped_area(struc
 		align_goal = (64UL * 1024);
 
 	do {
-		addr = get_unmapped_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags);
+		addr = get_unmapped_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags, 0);
 		if (!(addr & ~PAGE_MASK)) {
 			addr = (addr + (align_goal - 1UL)) & ~(align_goal - 1UL);
 			break;
@@ -158,7 +158,7 @@ unsigned long get_fb_unmapped_area(struc
 	 * be obtained.
 	 */
 	if (addr & ~PAGE_MASK)
-		addr = get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+		addr = get_unmapped_area(NULL, orig_addr, len, pgoff, flags, 0);
 
 	return addr;
 }
@@ -394,7 +394,7 @@ asmlinkage unsigned long sys64_mremap(un
 		/* MREMAP_FIXED checked above. */
 		new_addr = get_unmapped_area(file, addr, new_len,
 				    vma ? vma->vm_pgoff : 0,
-				    map_flags);
+				    map_flags, vma->vm_flags & VM_EXEC);
 		ret = new_addr;
 		if (new_addr & ~PAGE_MASK)
 			goto out_sem;
arch/i386/kernel/entry.S renamed from arch/i386/kernel/entry.S.orig +6 -2
@@ -238,8 +238,12 @@ sysenter_past_esp:
 	pushl %ebp
 	pushfl
 	pushl $(__USER_CS)
-	pushl $SYSENTER_RETURN
-
+	/*
+	 * Push current_thread_info()->sysenter_return to the stack.
+	 * A tiny bit of offset fixup is necessary - 4*4 means the 4 words
+	 * pushed above, and the word being pushed now:
+	 */
+	pushl (TI_sysenter_return-THREAD_SIZE+4*4)(%esp)
 /*
  * Load the potential sixth argument from user stack.
  * Careful about security.
arch/i386/kernel/asm-offsets.c renamed from arch/i386/kernel/asm-offsets.c.orig +1 -0
@@ -52,6 +52,7 @@ void foo(void)
 	OFFSET(TI_preempt_count, thread_info, preempt_count);
 	OFFSET(TI_addr_limit, thread_info, addr_limit);
 	OFFSET(TI_restart_block, thread_info, restart_block);
+	OFFSET(TI_sysenter_return, thread_info, sysenter_return);
 	BLANK();
 
 	OFFSET(EXEC_DOMAIN_handler, exec_domain, handler);
arch/i386/kernel/process.c renamed from arch/i386/kernel/process.c.orig +235 -0

250 lines not on this page · raw message

arch/i386/kernel/signal.c renamed from arch/i386/kernel/signal.c.orig +4 -3
@@ -333,7 +333,7 @@ get_sigframe(struct k_sigaction *ka, str
 
 /* These symbols are defined with the addresses in the vsyscall page.
    See vsyscall-sigreturn.S.  */
-extern void __kernel_sigreturn, __kernel_rt_sigreturn;
+extern char __kernel_sigreturn, __kernel_rt_sigreturn, SYSENTER_RETURN;
 
 static void setup_frame(int sig, struct k_sigaction *ka,
 			sigset_t *set, struct pt_regs * regs)
@@ -367,7 +367,7 @@ static void setup_frame(int sig, struct 
 	if (err)
 		goto give_sigsegv;
 
-	restorer = &__kernel_sigreturn;
+	restorer = current->mm->context.vdso + (long)&__kernel_sigreturn;
 	if (ka->sa.sa_flags & SA_RESTORER)
 		restorer = ka->sa.sa_restorer;
 
@@ -450,9 +450,10 @@ static void setup_rt_frame(int sig, stru
 		goto give_sigsegv;
 
 	/* Set up to return from userspace.  */
-	restorer = &__kernel_rt_sigreturn;
+	restorer = current->mm->context.vdso + (long)&__kernel_rt_sigreturn;
 	if (ka->sa.sa_flags & SA_RESTORER)
 		restorer = ka->sa.sa_restorer;
+
 	err |= __put_user(restorer, &frame->pretcode);
 	 
 	/*
arch/i386/kernel/traps.c renamed from arch/i386/kernel/traps.c.orig +44 -0

56 lines not on this page · raw message

arch/i386/kernel/sysenter.c renamed from arch/i386/kernel/sysenter.c.orig +49 -1

73 lines not on this page · raw message

arch/i386/kernel/vsyscall.lds renamed from arch/i386/kernel/vsyscall.lds.orig +4 -7
@@ -1,15 +1,12 @@
 /*
  * Linker script for vsyscall DSO.  The vsyscall page is an ELF shared
- * object prelinked to its virtual address, and with only one read-only
- * segment (that fits in one page).  This script controls its layout.
+ * object with only one read-only segment (that fits in one page).
+ * This script controls its layout.
  */
 
-/* This must match <asm/fixmap.h>.  */
-VSYSCALL_BASE = 0xffffe000;
-
 SECTIONS
 {
-  . = VSYSCALL_BASE + SIZEOF_HEADERS;
+  . = SIZEOF_HEADERS;
 
   .hash           : { *(.hash) }		:text
   .dynsym         : { *(.dynsym) }
@@ -22,7 +19,7 @@ SECTIONS
      For the layouts to match, we need to skip more than enough
      space for the dynamic symbol table et al.  If this amount
      is insufficient, ld -shared will barf.  Just increase it here.  */
-  . = VSYSCALL_BASE + 0x400;
+  . = 0x400;
 
   .text           : { *(.text) }		:text =0x90909090
 
arch/i386/kernel/vsyscall-sysenter.S renamed from arch/i386/kernel/vsyscall-sysenter.S.orig +3 -3
@@ -24,11 +24,11 @@ __kernel_vsyscall:
 	/* 7: align return point with nop's to make disassembly easier */
 	.space 7,0x90
 
-	/* 14: System call restart point is here! (SYSENTER_RETURN - 2) */
+	/* 14: System call restart point is here! (SYSENTER_RETURN_OFFSET-2) */
 	jmp .Lenter_kernel
 	/* 16: System call normal return point is here! */
-	.globl SYSENTER_RETURN	/* Symbol used by entry.S.  */
-SYSENTER_RETURN:
+	.globl SYSENTER_RETURN_OFFSET	/* Symbol used by sysenter.c  */
+SYSENTER_RETURN_OFFSET:
 	pop %ebp
 .Lpop_ebp:
 	pop %edx
arch/i386/mm/init.c renamed from arch/i386/mm/init.c.orig +7 -3
@@ -414,6 +414,7 @@ extern void zone_sizes_init(void);
 
 static int disable_nx __initdata = 0;
 u64 __supported_pte_mask = ~_PAGE_NX;
+int use_nx = 0;
 
 /*
  * noexec = on|off
@@ -421,7 +422,7 @@ u64 __supported_pte_mask = ~_PAGE_NX;
  * Control non executable mappings.
  *
  * on      Enable
- * off     Disable
+ * off     Disable (disables exec-shield too)
  */
 static int __init noexec_setup(char *str)
 {
@@ -431,6 +432,7 @@ static int __init noexec_setup(char *str
 	} else if (!strncmp(str,"off",3)) {
 		disable_nx = 1;
 		__supported_pte_mask &= ~_PAGE_NX;
+		exec_shield = 0;
 	}
 	return 1;
 }
@@ -438,7 +440,6 @@ static int __init noexec_setup(char *str
 __setup("noexec=", noexec_setup);
 
 #ifdef CONFIG_X86_PAE
-static int use_nx = 0;
 
 static void __init set_nx(void)
 {
@@ -471,8 +472,11 @@ void __init paging_init(void)
 	set_nx();
 	if (use_nx)
 		printk("NX (Execute Disable) protection: active\n");
-	else
+	else {
 		printk("NX (Execute Disable) protection: not present!\n");
+		if (exec_shield)
+			printk("Using x86 segment limits to approximate NX protection\n");
+	}
 #endif
 
 	pagetable_init();
arch/sparc/kernel/sys_sparc.c renamed from arch/sparc/kernel/sys_sparc.c.orig +1 -1
@@ -332,7 +332,7 @@ asmlinkage unsigned long sparc_mremap(un
 
 		new_addr = get_unmapped_area(file, addr, new_len,
 				     vma ? vma->vm_pgoff : 0,
-				     map_flags);
+				     map_flags, vma->vm_flags & VM_EXEC);
 		ret = new_addr;
 		if (new_addr & ~PAGE_MASK)
 			goto out_sem;
arch/ia64/kernel/perfmon.c renamed from arch/ia64/kernel/perfmon.c.orig +1 -1
@@ -599,7 +599,7 @@ pfm_do_munmap(struct mm_struct *mm, unsi
 static inline unsigned long 
 pfm_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags, unsigned long exec)
 {
-	return get_unmapped_area(file, addr, len, pgoff, flags);
+	return get_unmapped_area(file, addr, len, pgoff, flags, 0);
 }
 
 
arch/ia64/ia32/binfmt_elf32.c renamed from arch/ia64/ia32/binfmt_elf32.c.orig +1 -1
@@ -211,7 +211,7 @@ elf32_set_personality (void)
 }
 
 static unsigned long
-elf32_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type)
+elf32_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type, unsigned long unused)
 {
 	unsigned long pgoff = (eppnt->p_vaddr) & ~IA32_PAGE_MASK;
 
arch/x86_64/ia32/ia32_binfmt.c renamed from arch/x86_64/ia32/ia32_binfmt.c.orig +1 -1
@@ -382,7 +382,7 @@ int setup_arg_pages(struct linux_binprm 
 }
 
 static unsigned long
-elf32_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type)
+elf32_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type, unsigned long unused)
 {
 	unsigned long map_addr;
 	struct task_struct *me = current; 
include/asm-x86_64/pgalloc.h renamed from include/asm-x86_64/pgalloc.h.orig +5 -0
@@ -7,6 +7,11 @@
 #include <linux/threads.h>
 #include <linux/mm.h>
 
+#define arch_add_exec_range(mm, limit) do { ; } while (0)
+#define arch_flush_exec_range(mm)      do { ; } while (0)
+#define arch_remove_exec_range(mm, limit) do { ; } while (0)
+
+
 #define pmd_populate_kernel(mm, pmd, pte) \
 		set_pmd(pmd, __pmd(_PAGE_TABLE | __pa(pte)))
 #define pgd_populate(mm, pgd, pmd) \
include/linux/mm.h renamed from include/linux/mm.h.orig +1 -1
@@ -630,7 +630,7 @@ extern struct vm_area_struct *copy_vma(s
 	unsigned long addr, unsigned long len, pgoff_t pgoff);
 extern void exit_mmap(struct mm_struct *);
 
-extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
 
 extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 	unsigned long len, unsigned long prot,
include/linux/resource.h renamed from include/linux/resource.h.orig +4 -1
@@ -52,8 +52,11 @@ struct rlimit {
 /*
  * Limit the stack by to some sane default: root can always
  * increase this limit if needed..  8MB seems reasonable.
+ *
+ * (2MB more to cover randomization effects.)
  */
-#define _STK_LIM	(8*1024*1024)
+#define _STK_LIM	(10*1024*1024)
+#define EXEC_STACK_BIAS	(2*1024*1024)
 
 /*
  * Due to binary compatibility, the actual resource numbers
include/linux/sched.h renamed from include/linux/sched.h.orig +6 -0
@@ -31,6 +31,8 @@
 #include <linux/percpu.h>
 
 struct exec_domain;
+extern int exec_shield;
+extern int print_fatal_signals;
 
 /*
  * cloning flags:
@@ -196,6 +198,8 @@ struct mm_struct {
 	struct rb_root mm_rb;
 	struct vm_area_struct * mmap_cache;	/* last find_vma result */
 	unsigned long free_area_cache;		/* first hole */
+	unsigned long non_executable_cache;	/* last hole top */
+	unsigned long mmap_top;			/* top of mmap area */
 	pgd_t * pgd;
 	atomic_t mm_users;			/* How many users with user space? */
 	atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) */
@@ -549,6 +553,8 @@ do { if (atomic_dec_and_test(&(tsk)->usa
 #define PF_SWAPOFF	0x00080000	/* I am in swapoff */
 #define PF_LESS_THROTTLE 0x00100000	/* Throttle me less: I clean memory */
 #define PF_SYNCWRITE	0x00200000	/* I am doing a sync write */
+#define PF_RELOCEXEC	0x00400000	/* relocate shared libraries */
+
 
 #ifdef CONFIG_SMP
 #define SCHED_LOAD_SCALE	128UL	/* increase resolution of load */
include/asm-ppc64/pgalloc.h renamed from include/asm-ppc64/pgalloc.h.orig +5 -0
@@ -10,6 +10,11 @@
 
 extern kmem_cache_t *zero_cache;
 
+/* Dummy functions since we don't support execshield on ppc */
+#define arch_add_exec_range(mm, limit) do { ; } while (0)
+#define arch_flush_exec_range(mm)      do { ; } while (0)
+#define arch_remove_exec_range(mm, limit) do { ; } while (0)
+
 /*
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
include/asm-ia64/pgalloc.h renamed from include/asm-ia64/pgalloc.h.orig +4 -0
@@ -23,6 +23,10 @@
 #include <asm/mmu_context.h>
 #include <asm/processor.h>
 
+#define arch_add_exec_range(mm, limit)		do { ; } while (0)
+#define arch_flush_exec_range(mm)		do { ; } while (0)
+#define arch_remove_exec_range(mm, limit)	do { ; } while (0)
+
 /*
  * Very stupidly, we used to get new pgd's and pmd's, init their contents
  * to point to the NULL versions of the next level page table, later on
include/asm-ppc/pgalloc.h renamed from include/asm-ppc/pgalloc.h.orig +5 -0
@@ -40,5 +40,10 @@ extern void pte_free(struct page *pte);
 
 #define check_pgt_cache()	do { } while (0)
 
+#define arch_add_exec_range(mm, limit)         do { ; } while (0)
+#define arch_flush_exec_range(mm)              do { ; } while (0)
+#define arch_remove_exec_range(mm, limit)      do { ; } while (0)
+
+
 #endif /* _PPC_PGALLOC_H */
 #endif /* __KERNEL__ */
include/asm-sparc/pgalloc.h renamed from include/asm-sparc/pgalloc.h.orig +4 -0
@@ -66,4 +66,8 @@ BTFIXUPDEF_CALL(void, pte_free, struct p
 #define pte_free(pte)		BTFIXUP_CALL(pte_free)(pte)
 #define __pte_free_tlb(tlb, pte)	pte_free(pte)
 
+#define arch_add_exec_range(mm, limit)		do { ; } while (0)
+#define arch_flush_exec_range(mm)		do { ; } while (0)
+#define arch_remove_exec_range(mm, limit)	do { ; } while (0)
+
 #endif /* _SPARC_PGALLOC_H */
include/asm-s390/pgalloc.h renamed from include/asm-s390/pgalloc.h.orig +4 -0
@@ -19,6 +19,10 @@
 #include <linux/gfp.h>
 #include <linux/mm.h>
 
+#define arch_add_exec_range(mm, limit) do { ; } while (0)
+#define arch_flush_exec_range(mm)      do { ; } while (0)
+#define arch_remove_exec_range(mm, limit) do { ; } while (0)
+
 #define check_pgt_cache()	do {} while (0)
 
 extern void diag10(unsigned long addr);
include/asm-i386/desc.h renamed from include/asm-i386/desc.h.orig +14 -0
@@ -123,6 +123,20 @@ static inline void load_LDT(mm_context_t
 	put_cpu();
 }
 
+static inline void set_user_cs(struct desc_struct *desc, unsigned long limit)
+{
+	limit = (limit - 1) / PAGE_SIZE;
+	desc->a = limit & 0xffff;
+	desc->b = (limit & 0xf0000) | 0x00c0fb00;
+}
+
+#define load_user_cs_desc(cpu, mm) \
+    	cpu_gdt_table[(cpu)][GDT_ENTRY_DEFAULT_USER_CS] = (mm)->context.user_cs
+
+extern void arch_add_exec_range(struct mm_struct *mm, unsigned long limit);
+extern void arch_remove_exec_range(struct mm_struct *mm, unsigned long limit);
+extern void arch_flush_exec_range(struct mm_struct *mm);
+
 #endif /* !__ASSEMBLY__ */
 
 #endif
include/asm-i386/elf.h renamed from include/asm-i386/elf.h.orig +27 -15

84 lines not on this page · raw message

include/asm-i386/mmu.h renamed from include/asm-i386/mmu.h.orig +6 -0
@@ -7,11 +7,17 @@
  * we put the segment information here.
  *
  * cpu_vm_mask is used to optimize ldt flushing.
+ *
+ * exec_limit is used to track the range PROT_EXEC
+ * mappings span.
  */
 typedef struct { 
 	int size;
 	struct semaphore sem;
 	void *ldt;
+	struct desc_struct user_cs;
+	unsigned long exec_limit;
+	void *vdso;
 } mm_context_t;
 
 #endif
include/asm-i386/page.h renamed from include/asm-i386/page.h.orig +4 -2
@@ -138,8 +138,10 @@ static __inline__ int get_order(unsigned
 
 #define virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
 
-#define VM_DATA_DEFAULT_FLAGS	(VM_READ | VM_WRITE | VM_EXEC | \
-				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+#define VM_DATA_DEFAULT_FLAGS \
+		(VM_READ | VM_WRITE | \
+			((current->flags & PF_RELOCEXEC) ? 0 : VM_EXEC) | \
+				VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #endif /* __KERNEL__ */
 
include/asm-i386/pgalloc.h renamed from include/asm-i386/pgalloc.h.orig +3 -0
@@ -4,6 +4,7 @@
 #include <linux/config.h>
 #include <asm/processor.h>
 #include <asm/fixmap.h>
+#include <asm/desc.h>
 #include <linux/threads.h>
 #include <linux/mm.h>		/* for struct page */
 
@@ -52,4 +53,6 @@ static inline void pte_free(struct page 
 
 #define check_pgt_cache()	do { } while (0)
 
+#define HAVE_ARCH_UNMAPPED_AREA 1
+
 #endif /* _I386_PGALLOC_H */
include/asm-i386/processor.h renamed from include/asm-i386/processor.h.orig +12 -1
@@ -300,7 +300,15 @@ extern unsigned int mca_pentium_flag;
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
  */
-#define TASK_UNMAPPED_BASE	(PAGE_ALIGN(TASK_SIZE / 3))
+#define TASK_UNMAPPED_BASE	PAGE_ALIGN(TASK_SIZE/3)
+
+#define SHLIB_BASE		0x00111000
+ 
+#define __HAVE_ARCH_ALIGN_STACK
+extern unsigned long arch_align_stack(unsigned long sp);
+
+#define __HAVE_ARCH_MMAP_TOP
+extern unsigned long mmap_top(void);
 
 /*
  * Size of io_bitmap, covering ports 0 to 0x3ff.
@@ -462,6 +470,8 @@ static inline void load_esp0(struct tss_
 	}
 }
 
+extern int use_nx;
+
 #define start_thread(regs, new_eip, new_esp) do {		\
 	__asm__("movl %0,%%fs ; movl %0,%%gs": :"r" (0));	\
 	set_fs(USER_DS);					\
@@ -471,6 +481,7 @@ static inline void load_esp0(struct tss_
 	regs->xcs = __USER_CS;					\
 	regs->eip = new_eip;					\
 	regs->esp = new_esp;					\
+	load_user_cs_desc(smp_processor_id(), current->mm);	\
 } while (0)
 
 /* Forward declaration, a strange C thing */
include/asm-i386/thread_info.h renamed from include/asm-i386/thread_info.h.orig +1 -0
@@ -37,6 +37,7 @@ struct thread_info {
 					 	   0-0xBFFFFFFF for user-thead
 						   0-0xFFFFFFFF for kernel-thread
 						*/
+	void			*sysenter_return;
 	struct restart_block    restart_block;
 
 	unsigned long           previous_esp;   /* ESP of the previous stack in case
include/asm-sparc64/pgalloc.h renamed from include/asm-sparc64/pgalloc.h.orig +4 -0
@@ -236,4 +236,8 @@ static __inline__ void free_pte_slow(pte
 #define pgd_free(pgd)		free_pgd_fast(pgd)
 #define pgd_alloc(mm)		get_pgd_fast()
 
+#define arch_add_exec_range(mm, limit)		do { ; } while (0)
+#define arch_flush_exec_range(mm)		do { ; } while (0)
+#define arch_remove_exec_range(mm, limit)	do { ; } while (0)
+
 #endif /* _SPARC64_PGALLOC_H */
fs/proc/array.c renamed from fs/proc/array.c.orig +4 -1
@@ -324,7 +324,10 @@ int proc_pid_stat(struct task_struct *ta
 		up_read(&mm->mmap_sem);
 	}
 
-	wchan = get_wchan(task);
+	wchan = 0;
+	if (current->uid == task->uid || current->euid == task->uid ||
+							capable(CAP_SYS_NICE))
+		wchan = get_wchan(task);
 
 	sigemptyset(&sigign);
 	sigemptyset(&sigcatch);
fs/proc/base.c renamed from fs/proc/base.c.orig +2 -2
@@ -111,7 +111,7 @@ static struct pid_entry tgid_base_stuff[
 	E(PROC_TGID_CMDLINE,   "cmdline", S_IFREG|S_IRUGO),
 	E(PROC_TGID_STAT,      "stat",    S_IFREG|S_IRUGO),
 	E(PROC_TGID_STATM,     "statm",   S_IFREG|S_IRUGO),
-	E(PROC_TGID_MAPS,      "maps",    S_IFREG|S_IRUGO),
+	E(PROC_TGID_MAPS,      "maps",    S_IFREG|S_IRUSR),
 	E(PROC_TGID_MEM,       "mem",     S_IFREG|S_IRUSR|S_IWUSR),
 	E(PROC_TGID_CWD,       "cwd",     S_IFLNK|S_IRWXUGO),
 	E(PROC_TGID_ROOT,      "root",    S_IFLNK|S_IRWXUGO),
@@ -133,7 +133,7 @@ static struct pid_entry tid_base_stuff[]
 	E(PROC_TID_CMDLINE,    "cmdline", S_IFREG|S_IRUGO),
 	E(PROC_TID_STAT,       "stat",    S_IFREG|S_IRUGO),
 	E(PROC_TID_STATM,      "statm",   S_IFREG|S_IRUGO),
-	E(PROC_TID_MAPS,       "maps",    S_IFREG|S_IRUGO),
+	E(PROC_TID_MAPS,       "maps",    S_IFREG|S_IRUSR),
 	E(PROC_TID_MEM,        "mem",     S_IFREG|S_IRUSR|S_IWUSR),
 	E(PROC_TID_CWD,        "cwd",     S_IFLNK|S_IRWXUGO),
 	E(PROC_TID_ROOT,       "root",    S_IFLNK|S_IRWXUGO),
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c.orig +13 -2
@@ -34,12 +34,23 @@ char *task_mem(struct mm_struct *mm, cha
 		"VmData:\t%8lu kB\n"
 		"VmStk:\t%8lu kB\n"
 		"VmExe:\t%8lu kB\n"
-		"VmLib:\t%8lu kB\n",
+		"VmLib:\t%8lu kB\n"
+		"StaBrk:\t%08lx kB\n"
+		"Brk:\t%08lx kB\n"
+		"StaStk:\t%08lx kB\n"
+#if __i386__
+		"ExecLim:\t%08lx\n"
+#endif
+		,
 		mm->total_vm << (PAGE_SHIFT-10),
 		mm->locked_vm << (PAGE_SHIFT-10),
 		mm->rss << (PAGE_SHIFT-10),
 		data - stack, stack,
-		exec - lib, lib);
+		exec - lib, lib, mm->start_brk, mm->brk, mm->start_stack
+#if __i386__
+		, mm->context.exec_limit
+#endif
+		);
 	up_read(&mm->mmap_sem);
 	return buffer;
 }
fs/binfmt_aout.c renamed from fs/binfmt_aout.c.orig +2 -1
@@ -308,7 +308,8 @@ static int load_aout_binary(struct linux
 	current->mm->brk = ex.a_bss +
 		(current->mm->start_brk = N_BSSADDR(ex));
 	current->mm->free_area_cache = TASK_UNMAPPED_BASE;
-
+	/* unlimited stack is larger than TASK_SIZE */
+	current->mm->non_executable_cache = current->mm->mmap_top;
 	current->mm->rss = 0;
 	current->mm->mmap = NULL;
 	compute_creds(bprm);
fs/exec.c renamed from fs/exec.c.orig +18 -2
@@ -390,7 +390,12 @@ int setup_arg_pages(struct linux_binprm 
 	while (i < MAX_ARG_PAGES)
 		bprm->page[i++] = NULL;
 #else
+#ifdef __HAVE_ARCH_ALIGN_STACK
+	stack_base = arch_align_stack(STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE);
+	stack_base = PAGE_ALIGN(stack_base);
+#else
 	stack_base = STACK_TOP - MAX_ARG_PAGES * PAGE_SIZE;
+#endif
 	mm->arg_start = bprm->p + stack_base;
 	arg_size = STACK_TOP - (PAGE_MASK & (unsigned long) mm->arg_start);
 #endif
@@ -836,6 +841,7 @@ int flush_old_exec(struct linux_binprm *
 	}
 	current->comm[i] = '\0';
 
+	current->flags &= ~PF_RELOCEXEC;
 	flush_thread();
 
 	if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || 
@@ -886,8 +892,13 @@ int prepare_binprm(struct linux_binprm *
 
 	if(!(bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)) {
 		/* Set-uid? */
-		if (mode & S_ISUID)
+		if (mode & S_ISUID) {
 			bprm->e_uid = inode->i_uid;
+#ifdef __i386__
+			/* reset personality */
+			current->personality = PER_LINUX;
+#endif
+		}
 
 		/* Set-gid? */
 		/*
@@ -895,8 +906,13 @@ int prepare_binprm(struct linux_binprm *
 		 * is a candidate for mandatory locking, not a setgid
 		 * executable.
 		 */
-		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
+		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
 			bprm->e_gid = inode->i_gid;
+#ifdef __i386__
+			/* reset personality */
+			current->personality = PER_LINUX;
+#endif
+		}
 	}
 
 	/* fill in binprm security blob */
fs/binfmt_elf.c renamed from fs/binfmt_elf.c.orig +116 -34

274 lines not on this page · raw message

kernel/fork.c renamed from kernel/fork.c.orig +3 -0
@@ -416,6 +416,9 @@ static struct mm_struct * mm_init(struct
 
 	if (likely(!mm_alloc_pgd(mm))) {
 		mm->def_flags = 0;
+#ifdef __HAVE_ARCH_MMAP_TOP
+		mm->mmap_top = mmap_top();
+#endif
 		return mm;
 	}
 	free_mm(mm);
kernel/signal.c renamed from kernel/signal.c.orig +35 -0
@@ -1546,6 +1546,34 @@ do_notify_parent_cldstop(struct task_str
 	spin_unlock_irqrestore(&sighand->siglock, flags);
 }
 
+int print_fatal_signals = 0;
+
+static void print_fatal_signal(struct pt_regs *regs, int signr)
+{
+	int i;
+	unsigned char insn;
+	printk("%s/%d: potentially unexpected fatal signal %d.\n",
+		current->comm, current->pid, signr);
+		
+#ifdef __i386__
+	printk("code at %08lx: ", regs->eip);
+	for (i = 0; i < 16; i++) {
+		__get_user(insn, (unsigned char *)(regs->eip + i));
+		printk("%02x ", insn);
+	}
+#endif	
+	printk("\n");
+	show_regs(regs);
+}
+
+static int __init setup_print_fatal_signals(char *str)
+{
+	get_option (&str, &print_fatal_signals);
+
+	return 1;
+}
+
+__setup("print-fatal-signals=", setup_print_fatal_signals);
 
 #ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER
 
@@ -1737,6 +1765,11 @@ relock:
 		if (!signr)
 			break; /* will return 0 */
 
+		if ((signr == SIGSEGV) && print_fatal_signals) {
+			spin_unlock_irq(&current->sighand->siglock);
+			print_fatal_signal(regs, signr);
+			spin_lock_irq(&current->sighand->siglock);
+		}
 		if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
 			ptrace_signal_deliver(regs, cookie);
 
@@ -1841,6 +1874,8 @@ relock:
 		 * Anything else is fatal, maybe with a core dump.
 		 */
 		current->flags |= PF_SIGNALED;
+		if (print_fatal_signals)
+			print_fatal_signal(regs, signr);
 		if (sig_kernel_coredump(signr) &&
 		    do_coredump((long)signr, signr, regs)) {
 			/*
kernel/sysctl.c renamed from kernel/sysctl.c.orig +39 -0
@@ -65,6 +65,19 @@ extern int min_free_kbytes;
 extern int printk_ratelimit_jiffies;
 extern int printk_ratelimit_burst;
 
+extern unsigned int vdso_enabled;
+
+int exec_shield = 1;
+
+static int __init setup_exec_shield(char *str)
+{
+        get_option (&str, &exec_shield);
+
+        return 1;
+}
+
+__setup("exec-shield=", setup_exec_shield);
+
 /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
 static int maxolduid = 65535;
 static int minolduid;
@@ -268,6 +281,32 @@ static ctl_table kern_table[] = {
 		.proc_handler	= &proc_dointvec,
 	},
 	{
+		.ctl_name	= KERN_PANIC,
+		.procname	= "exec-shield",
+		.data		= &exec_shield,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= KERN_PANIC,
+		.procname	= "print-fatal-signals",
+		.data		= &print_fatal_signals,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+#if __i386__
+	{
+		.ctl_name	= KERN_PANIC,
+		.procname	= "vdso",
+		.data		= &vdso_enabled,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+#endif
+	{
 		.ctl_name	= KERN_CORE_USES_PID,
 		.procname	= "core_uses_pid",
 		.data		= &core_uses_pid,
mm/mmap.c renamed from mm/mmap.c.orig +36 -10

137 lines not on this page · raw message

mm/mprotect.c renamed from mm/mprotect.c.orig +6 -1
@@ -21,6 +21,7 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/pgtable.h>
+#include <asm/pgalloc.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 
@@ -113,8 +114,9 @@ mprotect_fixup(struct vm_area_struct *vm
 	unsigned long start, unsigned long end, unsigned int newflags)
 {
 	struct mm_struct * mm = vma->vm_mm;
-	unsigned long charged = 0;
+	unsigned long charged = 0, old_end = vma->vm_end;
 	pgprot_t newprot;
+	unsigned int oldflags;
 	pgoff_t pgoff;
 	int error;
 
@@ -175,8 +177,11 @@ success:
 	 * vm_flags and vm_page_prot are protected by the mmap_sem
 	 * held in write mode.
 	 */
+	oldflags = vma->vm_flags;
 	vma->vm_flags = newflags;
 	vma->vm_page_prot = newprot;
+	if (oldflags & VM_EXEC)
+		arch_remove_exec_range(current->mm, old_end);
 	change_protection(vma, start, end, newprot);
 	return 0;
 
mm/mremap.c renamed from mm/mremap.c.orig +2 -1
@@ -381,7 +381,8 @@ unsigned long do_mremap(unsigned long ad
 				map_flags |= MAP_SHARED;
 
 			new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
-						vma->vm_pgoff, map_flags);
+					vma->vm_pgoff, map_flags,
+						vma->vm_flags & VM_EXEC);
 			ret = new_addr;
 			if (new_addr & ~PAGE_MASK)
 				goto out;
mm/fremap.c renamed from mm/fremap.c.orig +0 -6
@@ -61,12 +61,6 @@ int install_page(struct mm_struct *mm, s
 	pmd_t *pmd;
 	pte_t pte_val;
 
-	/*
-	 * We use page_add_file_rmap below: if install_page is
-	 * ever extended to anonymous pages, this will warn us.
-	 */
-	BUG_ON(!page_mapping(page));
-
 	pgd = pgd_offset(mm, addr);
 	spin_lock(&mm->page_table_lock);
 
#3 lore raw
In reply to Christoph Hellwig
Quoted 10 lines · Christoph Hellwig
Quoted 5 lines · Ingo Molnar

Here's the latest exec-shield patch for 2.6.7-rc2-bk2, integrated with the 'NX' feature (see the announcement from earlier today):

http://redhat.com/~mingo/exec-shield/exec-shield-on-nx-2.6.7-rc2-bk2-A7

Any chance to split this up a bit? Having the pure non-exec stack (and maybe heap) would be really nice while the randomization feature are a litte bit too much security by obscurity for my taste.

It's no more security by obscurity than keeping your key secret is security by obscurity. (One can think of the randomization as a white-noise key).

Joe