diff -urN linux-2.6.5/Makefile linux-2.6.5/Makefile --- linux-2.6.5/Makefile 2004-04-03 22:37:36.000000000 -0500 +++ linux-2.6.5/Makefile 2004-04-18 11:05:50.000000000 -0400 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 5 -EXTRAVERSION = +EXTRAVERSION = -grsec NAME=Zonked Quokka # *DOCUMENTATION* @@ -424,7 +424,7 @@ CFLAGS := $(CPPFLAGS) $(CFLAGS) AFLAGS := $(CPPFLAGS) $(AFLAGS) -core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ +core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ grsecurity/ SUBDIRS += $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ diff -urN linux-2.6.5/arch/alpha/kernel/osf_sys.c linux-2.6.5/arch/alpha/kernel/osf_sys.c --- linux-2.6.5/arch/alpha/kernel/osf_sys.c 2004-04-03 22:36:11.000000000 -0500 +++ linux-2.6.5/arch/alpha/kernel/osf_sys.c 2004-04-18 11:05:50.000000000 -0400 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -179,6 +180,11 @@ struct file *file = NULL; unsigned long ret = -EBADF; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + #if 0 if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED)) printk("%s: unimplemented OSF mmap flags %04lx\n", @@ -189,6 +195,13 @@ if (!file) goto out; } + + if (gr_handle_mmap(file, prot)) { + fput(file); + ret = -EACCES; + goto out; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); down_write(¤t->mm->mmap_sem); ret = do_mmap(file, addr, len, prot, flags, off); @@ -1295,6 +1308,10 @@ merely specific addresses, but regions of memory -- perhaps this feature should be incorporated into all ports? */ +#ifdef CONFIG_PAX_RANDMMAP + if (!(current->flags & PF_PAX_RANDMMAP) || !filp) +#endif + if (addr) { addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit); if (addr != (unsigned long) -ENOMEM) @@ -1302,8 +1319,16 @@ } /* Next, try allocating at TASK_UNMAPPED_BASE. */ - addr = arch_get_unmapped_area_1 (PAGE_ALIGN(TASK_UNMAPPED_BASE), - len, limit); + + addr = TASK_UNMAPPED_BASE; + +#ifdef CONFIG_PAX_RANDMMAP + if (current->flags & PF_PAX_RANDMMAP) + addr += current->mm->delta_mmap; +#endif + + addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit); + if (addr != (unsigned long) -ENOMEM) return addr; diff -urN linux-2.6.5/arch/alpha/kernel/ptrace.c linux-2.6.5/arch/alpha/kernel/ptrace.c --- linux-2.6.5/arch/alpha/kernel/ptrace.c 2004-04-03 22:38:13.000000000 -0500 +++ linux-2.6.5/arch/alpha/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -288,6 +289,9 @@ if (!child) goto out_notsk; + if (gr_handle_ptrace(child, request)) + goto out; + if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out; diff -urN linux-2.6.5/arch/alpha/mm/fault.c linux-2.6.5/arch/alpha/mm/fault.c --- linux-2.6.5/arch/alpha/mm/fault.c 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/arch/alpha/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,142 @@ __reload_thread(pcb); } +#ifdef CONFIG_PAX_PAGEEXEC +/* + * PaX: decide what to do with offenders (regs->pc = fault address) + * + * returns 1 when task should be killed + * 2 when patched PLT trampoline was detected + * 3 when unpatched PLT trampoline was detected + * 4 when legitimate ET_EXEC was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#ifdef CONFIG_PAX_EMUPLT + int err; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + if (current->flags & PF_PAX_RANDEXEC) { + if (regs->pc >= current->mm->start_code && + regs->pc < current->mm->end_code) + { + if (regs->r26 == regs->pc) + return 1; + + regs->pc += current->mm->delta_exec; + return 4; + } + } +#endif + +#ifdef CONFIG_PAX_EMUPLT + do { /* PaX: patched PLT emulation #1 */ + unsigned int ldah, ldq, jmp; + + err = get_user(ldah, (unsigned int *)regs->pc); + err |= get_user(ldq, (unsigned int *)(regs->pc+4)); + err |= get_user(jmp, (unsigned int *)(regs->pc+8)); + + if (err) + break; + + if ((ldah & 0xFFFF0000U) == 0x277B0000U && + (ldq & 0xFFFF0000U) == 0xA77B0000U && + jmp == 0x6BFB0000U) + { + unsigned long r27, addr; + unsigned long addrh = (ldah | 0xFFFFFFFFFFFF0000UL) << 16; + unsigned long addrl = ldq | 0xFFFFFFFFFFFF0000UL; + + addr = regs->r27 + ((addrh ^ 0x80000000UL) + 0x80000000UL) + ((addrl ^ 0x8000UL) + 0x8000UL); + err = get_user(r27, (unsigned long*)addr); + if (err) + break; + + regs->r27 = r27; + regs->pc = r27; + return 2; + } + } while (0); + + do { /* PaX: patched PLT emulation #2 */ + unsigned int ldah, lda, br; + + err = get_user(ldah, (unsigned int *)regs->pc); + err |= get_user(lda, (unsigned int *)(regs->pc+4)); + err |= get_user(br, (unsigned int *)(regs->pc+8)); + + if (err) + break; + + if ((ldah & 0xFFFF0000U)== 0x277B0000U && + (lda & 0xFFFF0000U) == 0xA77B0000U && + (br & 0xFFE00000U) == 0xC3E00000U) + { + unsigned long addr = br | 0xFFFFFFFFFFE00000UL; + unsigned long addrh = (ldah | 0xFFFFFFFFFFFF0000UL) << 16; + unsigned long addrl = lda | 0xFFFFFFFFFFFF0000UL; + + regs->r27 += ((addrh ^ 0x80000000UL) + 0x80000000UL) + ((addrl ^ 0x8000UL) + 0x8000UL); + regs->pc += 12 + (((addr ^ 0x00100000UL) + 0x00100000UL) << 2); + return 2; + } + } while (0); + + do { /* PaX: unpatched PLT emulation */ + unsigned int br; + + err = get_user(br, (unsigned int *)regs->pc); + + if (!err && (br & 0xFFE00000U) == 0xC3800000U) { + unsigned int br2, ldq, nop, jmp; + unsigned long addr = br | 0xFFFFFFFFFFE00000UL, resolver; + + addr = regs->pc + 4 + (((addr ^ 0x00100000UL) + 0x00100000UL) << 2); + err = get_user(br2, (unsigned int *)addr); + err |= get_user(ldq, (unsigned int *)(addr+4)); + err |= get_user(nop, (unsigned int *)(addr+8)); + err |= get_user(jmp, (unsigned int *)(addr+12)); + err |= get_user(resolver, (unsigned long *)(addr+16)); + + if (err) + break; + + if (br2 == 0xC3600000U && + ldq == 0xA77B000CU && + nop == 0x47FF041FU && + jmp == 0x6B7B0000U) + { + regs->r28 = regs->pc+4; + regs->r27 = addr+16; + regs->pc = resolver; + return 3; + } + } + } while (0); +#endif + + return 1; +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 5; i++) { + unsigned int c; + if (get_user(c, (unsigned int*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif /* * This routine handles page faults. It determines the address, @@ -133,8 +270,34 @@ good_area: si_code = SEGV_ACCERR; if (cause < 0) { - if (!(vma->vm_flags & VM_EXEC)) + if (!(vma->vm_flags & VM_EXEC)) { + +#ifdef CONFIG_PAX_PAGEEXEC + if (!(current->flags & PF_PAX_PAGEEXEC) || address != regs->pc) + goto bad_area; + + up_read(&mm->mmap_sem); + switch(pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_EMUPLT + case 2: + case 3: + return; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + case 4: + return; +#endif + + } + pax_report_fault(regs, (void*)regs->pc, (void*)rdusp()); + do_exit(SIGKILL); +#else goto bad_area; +#endif + + } } else if (!cause) { /* Allow reads even for write-only mappings */ if (!(vma->vm_flags & (VM_READ | VM_WRITE))) diff -urN linux-2.6.5/arch/i386/Kconfig linux-2.6.5/arch/i386/Kconfig --- linux-2.6.5/arch/i386/Kconfig 2004-04-03 22:36:25.000000000 -0500 +++ linux-2.6.5/arch/i386/Kconfig 2004-04-18 11:05:50.000000000 -0400 @@ -390,7 +390,7 @@ config X86_ALIGNMENT_16 bool - depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK6 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 + depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK8 || MK7 || MK6 || MPENTIUM4 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 default y config X86_GOOD_APIC diff -urN linux-2.6.5/arch/i386/kernel/apm.c linux-2.6.5/arch/i386/kernel/apm.c --- linux-2.6.5/arch/i386/kernel/apm.c 2004-04-03 22:36:16.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/apm.c 2004-04-18 11:05:50.000000000 -0400 @@ -597,19 +597,40 @@ int cpu; struct desc_struct save_desc_40; +#ifdef CONFIG_PAX_KERNEXEC + unsigned long cr3; +#endif + cpus = apm_save_cpus(); cpu = get_cpu(); + +#ifdef CONFIG_PAX_KERNEXEC + pax_open_kernel(flags, cr3); +#endif + save_desc_40 = cpu_gdt_table[cpu][0x40 / 8]; cpu_gdt_table[cpu][0x40 / 8] = bad_bios_desc; +#ifndef CONFIG_PAX_KERNEXEC local_save_flags(flags); APM_DO_CLI; +#endif + APM_DO_SAVE_SEGS; apm_bios_call_asm(func, ebx_in, ecx_in, eax, ebx, ecx, edx, esi); APM_DO_RESTORE_SEGS; + +#ifndef CONFIG_PAX_KERNEXEC local_irq_restore(flags); +#endif + cpu_gdt_table[cpu][0x40 / 8] = save_desc_40; + +#ifdef CONFIG_PAX_KERNEXEC + pax_close_kernel(flags, cr3); +#endif + put_cpu(); apm_restore_cpus(cpus); @@ -639,20 +660,40 @@ int cpu; struct desc_struct save_desc_40; +#ifdef CONFIG_PAX_KERNEXEC + unsigned long cr3; +#endif cpus = apm_save_cpus(); cpu = get_cpu(); + +#ifdef CONFIG_PAX_KERNEXEC + pax_open_kernel(flags, cr3); +#endif + save_desc_40 = cpu_gdt_table[cpu][0x40 / 8]; cpu_gdt_table[cpu][0x40 / 8] = bad_bios_desc; +#ifndef CONFIG_PAX_KERNEXEC local_save_flags(flags); APM_DO_CLI; +#endif + APM_DO_SAVE_SEGS; error = apm_bios_call_simple_asm(func, ebx_in, ecx_in, eax); APM_DO_RESTORE_SEGS; + +#ifndef CONFIG_PAX_KERNEXEC local_irq_restore(flags); +#endif + cpu_gdt_table[smp_processor_id()][0x40 / 8] = save_desc_40; + +#ifdef CONFIG_PAX_KERNEXEC + pax_close_kernel(flags, cr3); +#endif + put_cpu(); apm_restore_cpus(cpus); return error; diff -urN linux-2.6.5/arch/i386/kernel/cpu/common.c linux-2.6.5/arch/i386/kernel/cpu/common.c --- linux-2.6.5/arch/i386/kernel/cpu/common.c 2004-04-03 22:36:16.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/cpu/common.c 2004-04-18 11:05:50.000000000 -0400 @@ -319,6 +319,10 @@ if (this_cpu->c_init) this_cpu->c_init(c); +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_KERNEXEC) || defined(CONFIG_PAX_NOVSYSCALL) + clear_bit(X86_FEATURE_SEP, c->x86_capability); +#endif + /* Disable the PN if appropriate */ squash_the_stupid_serial_number(c); @@ -514,7 +518,7 @@ set_tss_desc(cpu,t); cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff; load_TR_desc(); - load_LDT(&init_mm.context); + _load_LDT(&init_mm.context); /* Set up doublefault TSS pointer in the GDT */ __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss); diff -urN linux-2.6.5/arch/i386/kernel/entry.S linux-2.6.5/arch/i386/kernel/entry.S --- linux-2.6.5/arch/i386/kernel/entry.S 2004-04-03 22:36:52.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/entry.S 2004-04-18 11:05:50.000000000 -0400 @@ -272,6 +272,11 @@ movl TI_FLAGS(%ebp), %ecx testw $_TIF_ALLWORK_MASK, %cx jne syscall_exit_work + +#ifdef CONFIG_PAX_RANDKSTACK + call pax_randomize_kstack +#endif + /* if something modifies registers it must also disable sysexit */ movl EIP(%esp), %edx movl OLDESP(%esp), %ecx @@ -299,6 +304,11 @@ movl TI_FLAGS(%ebp), %ecx testw $_TIF_ALLWORK_MASK, %cx # current->work jne syscall_exit_work + +#ifdef CONFIG_PAX_RANDKSTACK + call pax_randomize_kstack +#endif + restore_all: RESTORE_ALL @@ -591,7 +601,13 @@ jmp error_code ENTRY(page_fault) +#ifdef CONFIG_PAX_PAGEEXEC + ALIGN + pushl $pax_do_page_fault +#else pushl $do_page_fault +#endif + jmp error_code #ifdef CONFIG_X86_MCE @@ -606,7 +622,7 @@ pushl $do_spurious_interrupt_bug jmp error_code -.data +.section .rodata,"a",@progbits ENTRY(sys_call_table) .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ .long sys_exit diff -urN linux-2.6.5/arch/i386/kernel/head.S linux-2.6.5/arch/i386/kernel/head.S --- linux-2.6.5/arch/i386/kernel/head.S 2004-04-03 22:36:18.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/head.S 2004-04-18 11:05:50.000000000 -0400 @@ -49,6 +49,12 @@ /* + * Real beginning of normal "text" segment + */ +ENTRY(stext) +ENTRY(_stext) + +/* * 32-bit kernel entrypoint; only used by the boot CPU. On entry, * %esi points to the real-mode code as a 32-bit pointer. * CS and DS must be 4 GB flat segments, but we don't depend on @@ -80,9 +86,9 @@ movl $(pg0 - __PAGE_OFFSET), %edi movl $(swapper_pg_dir - __PAGE_OFFSET), %edx - movl $0x007, %eax /* 0x007 = PRESENT+RW+USER */ + movl $0x067, %eax /* 0x067 = DIRTY+ACCESSED+PRESENT+RW+USER */ 10: - leal 0x007(%edi),%ecx /* Create PDE entry */ + leal 0x067(%edi),%ecx /* Create PDE entry */ movl %ecx,(%edx) /* Store identity PDE entry */ movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */ addl $4,%edx @@ -92,8 +98,8 @@ addl $0x1000,%eax loop 11b /* End condition: we must map up to and including INIT_MAP_BEYOND_END */ - /* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */ - leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp + /* bytes beyond the end of our own page tables; the +0x067 is the attribute bits */ + leal (INIT_MAP_BEYOND_END+0x067)(%edi),%ebp cmpl %ebp,%eax jb 10b movl %edi,(init_pg_tables_end - __PAGE_OFFSET) @@ -152,7 +158,7 @@ movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 /* ..and set paging (PG) bit */ - ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */ + ljmp $__BOOT_CS,$1f + __KERNEL_TEXT_OFFSET /* Clear prefetch and normalize %eip */ 1: /* Set up the stack pointer */ lss stack_start,%esp @@ -304,8 +310,6 @@ jmp L6 # main should never return here, but # just in case, we know what happens. -ready: .byte 0 - /* * We depend on ET to be correct. This checks for 287/387. */ @@ -353,13 +357,6 @@ jne rp_sidt ret -ENTRY(stack_start) - .long init_thread_union+THREAD_SIZE - .long __BOOT_DS - -/* This is the default interrupt "handler" :-) */ -int_msg: - .asciz "Unknown interrupt or fault at EIP %p %p %p\n" ALIGN ignore_int: cld @@ -386,6 +383,56 @@ iret /* + * This starts the data section. Note that the above is all + * in the text section because it has alignment requirements + * that we cannot fulfill any other way except for PaX ;-). + */ +.data +ready: .byte 0 + +/* + * swapper_pg_dir is the main page directory, address 0x00101000 + * + * This is initialized to create an identity-mapping at 0 (for bootup + * purposes) and another mapping at virtual address PAGE_OFFSET. The + * values put here should be all invalid (zero); the valid + * entries are created dynamically at boot time. + * + * The code creates enough page tables to map 0-_end, the page tables + * themselves, plus INIT_MAP_BEYOND_END bytes; see comment at beginning. + */ +.section .data.swapper_pg_dir,"a",@progbits +ENTRY(swapper_pg_dir) + .fill 1024,4,0 + +#ifdef CONFIG_PAX_KERNEXEC +ENTRY(kernexec_pg_dir) + .fill 1024,4,0 +#endif + +.section .rodata.empty_zero_page,"a",@progbits +ENTRY(empty_zero_page) + .fill 4096,1,0 + +/* + * The IDT has to be page-aligned to simplify the Pentium + * F0 0F bug workaround.. We have a special link segment + * for this. + */ +.section .rodata.idt,"a",@progbits +ENTRY(idt_table) + .fill 256,8,0 + +.section .rodata,"a",@progbits +ENTRY(stack_start) + .long init_thread_union+THREAD_SIZE + .long __BOOT_DS + +/* This is the default interrupt "handler" :-) */ +int_msg: + .asciz "Unknown interrupt or fault at EIP %p %p %p\n" + +/* * The IDT and GDT 'descriptors' are a strange 48-bit object * only used by the lidt and lgdt instructions. They are not * like usual segment descriptors - they consist of a 16-bit @@ -417,47 +464,14 @@ .fill NR_CPUS-1,8,0 # space for the other GDT descriptors /* - * swapper_pg_dir is the main page directory, address 0x00101000 - * - * This is initialized to create an identity-mapping at 0 (for bootup - * purposes) and another mapping at virtual address PAGE_OFFSET. The - * values put here should be all invalid (zero); the valid - * entries are created dynamically at boot time. - * - * The code creates enough page tables to map 0-_end, the page tables - * themselves, plus INIT_MAP_BEYOND_END bytes; see comment at beginning. - */ -.org 0x1000 -ENTRY(swapper_pg_dir) - .fill 1024,4,0 - -.org 0x2000 -ENTRY(empty_zero_page) - .fill 4096,1,0 - -.org 0x3000 -/* - * Real beginning of normal "text" segment - */ -ENTRY(stext) -ENTRY(_stext) - -/* - * This starts the data section. Note that the above is all - * in the text section because it has alignment requirements - * that we cannot fulfill any other way. - */ -.data - -/* * The boot_gdt_table must mirror the equivalent in setup.S and is * used only for booting. */ .align L1_CACHE_BYTES ENTRY(boot_gdt_table) .fill GDT_ENTRY_BOOT_CS,8,0 - .quad 0x00cf9a000000ffff /* kernel 4GB code at 0x00000000 */ - .quad 0x00cf92000000ffff /* kernel 4GB data at 0x00000000 */ + .quad 0x00cf9b000000ffff /* kernel 4GB code at 0x00000000 */ + .quad 0x00cf93000000ffff /* kernel 4GB data at 0x00000000 */ /* * The Global Descriptor Table contains 28 quadwords, per-CPU. @@ -468,7 +482,13 @@ .quad 0x0000000000000000 /* 0x0b reserved */ .quad 0x0000000000000000 /* 0x13 reserved */ .quad 0x0000000000000000 /* 0x1b reserved */ + +#if defined(CONFIG_PAX_KERNEXEC) && defined(CONFIG_PCI_BIOS) + .quad 0x00cf9b000000ffff /* 0x20 kernel 4GB code at 0x00000000 */ +#else .quad 0x0000000000000000 /* 0x20 unused */ +#endif + .quad 0x0000000000000000 /* 0x28 unused */ .quad 0x0000000000000000 /* 0x33 TLS entry 1 */ .quad 0x0000000000000000 /* 0x3b TLS entry 2 */ @@ -477,27 +497,32 @@ .quad 0x0000000000000000 /* 0x53 reserved */ .quad 0x0000000000000000 /* 0x5b reserved */ - .quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */ - .quad 0x00cf92000000ffff /* 0x68 kernel 4GB data at 0x00000000 */ - .quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */ - .quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0x00000000 */ +#ifdef CONFIG_PAX_KERNEXEC + .quad 0xc0cf9b400000ffff /* 0x60 kernel 4GB code at 0xc0400000 */ +#else + .quad 0x00cf9b000000ffff /* 0x60 kernel 4GB code at 0x00000000 */ +#endif + + .quad 0x00cf93000000ffff /* 0x68 kernel 4GB data at 0x00000000 */ + .quad 0x00cffb000000ffff /* 0x73 user 4GB code at 0x00000000 */ + .quad 0x00cff3000000ffff /* 0x7b user 4GB data at 0x00000000 */ .quad 0x0000000000000000 /* 0x80 TSS descriptor */ .quad 0x0000000000000000 /* 0x88 LDT descriptor */ /* Segments used for calling PnP BIOS */ - .quad 0x00c09a0000000000 /* 0x90 32-bit code */ - .quad 0x00809a0000000000 /* 0x98 16-bit code */ - .quad 0x0080920000000000 /* 0xa0 16-bit data */ - .quad 0x0080920000000000 /* 0xa8 16-bit data */ - .quad 0x0080920000000000 /* 0xb0 16-bit data */ + .quad 0x00c09b0000000000 /* 0x90 32-bit code */ + .quad 0x00809b0000000000 /* 0x98 16-bit code */ + .quad 0x0080930000000000 /* 0xa0 16-bit data */ + .quad 0x0080930000000000 /* 0xa8 16-bit data */ + .quad 0x0080930000000000 /* 0xb0 16-bit data */ /* * The APM segments have byte granularity and their bases * and limits are set at run time. */ - .quad 0x00409a0000000000 /* 0xb8 APM CS code */ - .quad 0x00009a0000000000 /* 0xc0 APM CS 16 code (16 bit) */ - .quad 0x0040920000000000 /* 0xc8 APM DS data */ + .quad 0x00409b0000000000 /* 0xb8 APM CS code */ + .quad 0x00009b0000000000 /* 0xc0 APM CS 16 code (16 bit) */ + .quad 0x0040930000000000 /* 0xc8 APM DS data */ .quad 0x0000000000000000 /* 0xd0 - unused */ .quad 0x0000000000000000 /* 0xd8 - unused */ diff -urN linux-2.6.5/arch/i386/kernel/ioport.c linux-2.6.5/arch/i386/kernel/ioport.c --- linux-2.6.5/arch/i386/kernel/ioport.c 2004-04-03 22:37:06.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/ioport.c 2004-04-18 11:05:50.000000000 -0400 @@ -15,6 +15,7 @@ #include #include #include +#include /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ static void set_bitmap(unsigned long *bitmap, unsigned int base, unsigned int extent, int new_value) @@ -62,9 +63,16 @@ if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) return -EINVAL; +#ifdef CONFIG_GRKERNSEC_IO + if (turn_on) { + gr_handle_ioperm(); +#else if (turn_on && !capable(CAP_SYS_RAWIO)) +#endif return -EPERM; - +#ifdef CONFIG_GRKERNSEC_IO + } +#endif /* * If it's the first ioperm() call in this thread's lifetime, set the * IO bitmap up. ioperm() is much less timing critical than clone(), @@ -115,8 +123,13 @@ return -EINVAL; /* Trying to gain more privileges? */ if (level > old) { +#ifdef CONFIG_GRKERNSEC_IO + gr_handle_iopl(); + return -EPERM; +#else if (!capable(CAP_SYS_RAWIO)) return -EPERM; +#endif } regs->eflags = (regs->eflags &~ 0x3000UL) | (level << 12); /* Make sure we return the long way (not sysenter) */ diff -urN linux-2.6.5/arch/i386/kernel/ldt.c linux-2.6.5/arch/i386/kernel/ldt.c --- linux-2.6.5/arch/i386/kernel/ldt.c 2004-04-03 22:37:37.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/ldt.c 2004-04-18 11:05:50.000000000 -0400 @@ -154,7 +154,7 @@ { int err; unsigned long size; - void *address; + const void *address; err = 0; address = &default_ldt[0]; @@ -211,6 +211,13 @@ } } +#ifdef CONFIG_PAX_SEGMEXEC + if ((current->flags & PF_PAX_SEGMEXEC) && (ldt_info.contents & 2)) { + error = -EINVAL; + goto out_unlock; + } +#endif + entry_1 = LDT_entry_a(&ldt_info); entry_2 = LDT_entry_b(&ldt_info); if (oldmode) diff -urN linux-2.6.5/arch/i386/kernel/process.c linux-2.6.5/arch/i386/kernel/process.c --- linux-2.6.5/arch/i386/kernel/process.c 2004-04-03 22:36:10.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/process.c 2004-04-18 11:05:50.000000000 -0400 @@ -344,7 +344,7 @@ struct task_struct *tsk; int err; - childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p->thread_info)) - 1; + childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p->thread_info - sizeof(unsigned long))) - 1; struct_cpy(childregs, regs); childregs->eax = 0; childregs->esp = esp; @@ -446,9 +446,8 @@ int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) { struct pt_regs ptregs; - - ptregs = *(struct pt_regs *) - ((unsigned long)tsk->thread_info+THREAD_SIZE - sizeof(ptregs)); + + ptregs = *(struct pt_regs *)(tsk->thread.esp0 - sizeof(ptregs)); ptregs.xcs &= 0xffff; ptregs.xds &= 0xffff; ptregs.xes &= 0xffff; @@ -501,10 +500,22 @@ int cpu = smp_processor_id(); struct tss_struct *tss = init_tss + cpu; +#ifdef CONFIG_PAX_KERNEXEC + unsigned long flags, cr3; +#endif + /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */ __unlazy_fpu(prev_p); +#ifdef CONFIG_PAX_KERNEXEC + pax_open_kernel(flags, cr3); +#endif + +#ifdef CONFIG_PAX_SEGMEXEC + pax_switch_segments(next_p, cpu); +#endif + /* * Reload esp0, LDT and the page table pointer: */ @@ -515,6 +526,10 @@ */ load_TLS(next, cpu); +#ifdef CONFIG_PAX_KERNEXEC + pax_close_kernel(flags, cr3); +#endif + /* * Save away %fs and %gs. No need to save %es and %ds, as * those are always kernel segments while inside the kernel. @@ -689,6 +704,10 @@ struct desc_struct *desc; int cpu, idx; +#ifdef CONFIG_PAX_KERNEXEC + unsigned long flags, cr3; +#endif + if (copy_from_user(&info, u_info, sizeof(info))) return -EFAULT; idx = info.entry_number; @@ -722,8 +741,17 @@ desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); } + +#ifdef CONFIG_PAX_KERNEXEC + pax_open_kernel(flags, cr3); +#endif + load_TLS(t, cpu); +#ifdef CONFIG_PAX_KERNEXEC + pax_close_kernel(flags, cr3); +#endif + put_cpu(); return 0; @@ -777,3 +805,29 @@ return 0; } +#ifdef CONFIG_PAX_RANDKSTACK +asmlinkage void pax_randomize_kstack(void) +{ + struct tss_struct *tss = init_tss + smp_processor_id(); + unsigned long time; + +#ifdef CONFIG_PAX_SOFTMODE + if (!pax_aslr) + return; +#endif + + rdtscl(time); + + /* P4 seems to return a 0 LSB, ignore it */ +#ifdef CONFIG_MPENTIUM4 + time &= 0x3EUL; + time <<= 1; +#else + time &= 0x1FUL; + time <<= 2; +#endif + + tss->esp0 ^= time; + current->thread.esp0 = tss->esp0; +} +#endif diff -urN linux-2.6.5/arch/i386/kernel/ptrace.c linux-2.6.5/arch/i386/kernel/ptrace.c --- linux-2.6.5/arch/i386/kernel/ptrace.c 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -262,6 +263,9 @@ if (pid == 1) /* you may not mess with init */ goto out_tsk; + if (gr_handle_ptrace(child, request)) + goto out_tsk; + if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; @@ -340,6 +344,17 @@ if(addr == (long) &dummy->u_debugreg[5]) break; if(addr < (long) &dummy->u_debugreg[4] && ((unsigned long) data) >= TASK_SIZE-3) break; + +#ifdef CONFIG_GRKERNSEC + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[3]){ + long reg = (addr - (long) &dummy->u_debugreg[0]) >> 2; + long type = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 4*reg)) & 3; + long align = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 2 + 4*reg)) & 3; + if((type & 1) && (data & align)) + break; + } +#endif if(addr == (long) &dummy->u_debugreg[7]) { data &= ~DR_CONTROL_RESERVED; diff -urN linux-2.6.5/arch/i386/kernel/reboot.c linux-2.6.5/arch/i386/kernel/reboot.c --- linux-2.6.5/arch/i386/kernel/reboot.c 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/reboot.c 2004-04-18 11:05:50.000000000 -0400 @@ -74,18 +74,18 @@ doesn't work with at least one type of 486 motherboard. It is easy to stop this code working; hence the copious comments. */ -static unsigned long long +static const unsigned long long real_mode_gdt_entries [3] = { 0x0000000000000000ULL, /* Null descriptor */ - 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */ - 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ + 0x00009b000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */ + 0x000093000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ }; static struct { unsigned short size __attribute__ ((packed)); - unsigned long long * base __attribute__ ((packed)); + const unsigned long long * base __attribute__ ((packed)); } real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries }, real_mode_idt = { 0x3ff, 0 }, diff -urN linux-2.6.5/arch/i386/kernel/setup.c linux-2.6.5/arch/i386/kernel/setup.c --- linux-2.6.5/arch/i386/kernel/setup.c 2004-04-03 22:37:06.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/setup.c 2004-04-18 11:05:50.000000000 -0400 @@ -1202,6 +1202,15 @@ #endif } +#ifdef CONFIG_PAX_SOFTMODE +static int __init setup_pax_softmode(char *str) +{ + get_option (&str, &pax_softmode); + return 1; +} +__setup("pax_softmode=", setup_pax_softmode); +#endif + #include "setup_arch_post.h" /* * Local Variables: diff -urN linux-2.6.5/arch/i386/kernel/signal.c linux-2.6.5/arch/i386/kernel/signal.c --- linux-2.6.5/arch/i386/kernel/signal.c 2004-04-03 22:36:58.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/signal.c 2004-04-18 11:05:50.000000000 -0400 @@ -371,7 +371,17 @@ if (err) goto give_sigsegv; +#ifdef CONFIG_PAX_NOVSYSCALL + restorer = frame->retcode; +#else restorer = &__kernel_sigreturn; + +#ifdef CONFIG_PAX_SEGMEXEC + if (current->flags & PF_PAX_SEGMEXEC) + restorer -= SEGMEXEC_TASK_SIZE; +#endif +#endif + if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; @@ -454,7 +464,18 @@ goto give_sigsegv; /* Set up to return from userspace. */ + +#ifdef CONFIG_PAX_NOVSYSCALL + restorer = frame->retcode; +#else restorer = &__kernel_rt_sigreturn; + +#ifdef CONFIG_PAX_SEGMEXEC + if (current->flags & PF_PAX_SEGMEXEC) + restorer -= SEGMEXEC_TASK_SIZE; +#endif +#endif + if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; err |= __put_user(restorer, &frame->pretcode); diff -urN linux-2.6.5/arch/i386/kernel/sys_i386.c linux-2.6.5/arch/i386/kernel/sys_i386.c --- linux-2.6.5/arch/i386/kernel/sys_i386.c 2004-04-03 22:38:21.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/sys_i386.c 2004-04-18 11:05:50.000000000 -0400 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,11 @@ int error = -EBADF; struct file * file = NULL; +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); @@ -56,8 +62,14 @@ goto out; } + if (gr_handle_mmap(file, prot)) { + fput(file); + error = -EACCES; + goto out; + } + down_write(¤t->mm->mmap_sem); - error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + error = do_mmap(file, addr, len, prot, flags, pgoff << PAGE_SHIFT); up_write(¤t->mm->mmap_sem); if (file) diff -urN linux-2.6.5/arch/i386/kernel/sysenter.c linux-2.6.5/arch/i386/kernel/sysenter.c --- linux-2.6.5/arch/i386/kernel/sysenter.c 2004-04-03 22:38:24.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/sysenter.c 2004-04-18 11:05:50.000000000 -0400 @@ -41,13 +41,15 @@ extern const char vsyscall_int80_start, vsyscall_int80_end; extern const char vsyscall_sysenter_start, vsyscall_sysenter_end; +#ifndef CONFIG_PAX_NOVSYSCALL static int __init sysenter_setup(void) { unsigned long page = get_zeroed_page(GFP_ATOMIC); __set_fixmap(FIX_VSYSCALL, __pa(page), PAGE_READONLY); - if (!boot_cpu_has(X86_FEATURE_SEP)) { + if (!boot_cpu_has(X86_FEATURE_SEP)) + { memcpy((void *) page, &vsyscall_int80_start, &vsyscall_int80_end - &vsyscall_int80_start); @@ -63,3 +65,4 @@ } __initcall(sysenter_setup); +#endif diff -urN linux-2.6.5/arch/i386/kernel/trampoline.S linux-2.6.5/arch/i386/kernel/trampoline.S --- linux-2.6.5/arch/i386/kernel/trampoline.S 2004-04-03 22:36:56.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/trampoline.S 2004-04-18 11:05:50.000000000 -0400 @@ -58,7 +58,7 @@ inc %ax # protected mode (PE) bit lmsw %ax # into protected mode # flush prefetch and jump to startup_32_smp in arch/i386/kernel/head.S - ljmpl $__BOOT_CS, $(startup_32_smp-__PAGE_OFFSET) + ljmpl $__BOOT_CS, $(startup_32_smp+__KERNEL_TEXT_OFFSET-__PAGE_OFFSET) # These need to be in the same 64K segment as the above; # hence we don't use the boot_gdt_descr defined in head.S diff -urN linux-2.6.5/arch/i386/kernel/traps.c linux-2.6.5/arch/i386/kernel/traps.c --- linux-2.6.5/arch/i386/kernel/traps.c 2004-04-03 22:36:25.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/traps.c 2004-04-18 11:05:50.000000000 -0400 @@ -59,18 +59,13 @@ asmlinkage void lcall7(void); asmlinkage void lcall27(void); -struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, +const struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; /* Do we ignore FPU interrupts ? */ char ignore_fpu_irq = 0; -/* - * The IDT has to be page-aligned to simplify the Pentium - * F0 0F bug workaround.. We have a special link segment - * for this. - */ -struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, }; +extern struct desc_struct idt_table[256]; asmlinkage void divide_error(void); asmlinkage void debug(void); @@ -97,6 +92,7 @@ void show_trace(struct task_struct *task, unsigned long * stack) { unsigned long addr; + int i = kstack_depth_to_print; if (!stack) stack = (unsigned long*)&stack; @@ -105,11 +101,12 @@ #ifdef CONFIG_KALLSYMS printk("\n"); #endif - while (!kstack_end(stack)) { + while (i && !kstack_end(stack)) { addr = *stack++; if (kernel_text_address(addr)) { printk(" [<%08lx>] ", addr); print_symbol("%s\n", addr); + --i; } } printk("\n"); @@ -199,14 +196,23 @@ show_stack(NULL, (unsigned long*)esp); printk("Code: "); + +#ifndef CONFIG_PAX_KERNEXEC if(regs->eip < PAGE_OFFSET) goto bad; +#endif for(i=0;i<20;i++) { unsigned char c; + +#ifdef CONFIG_PAX_KERNEXEC + if(__get_user(c, &((unsigned char*)regs->eip)[i+__KERNEL_TEXT_OFFSET])) { +#else if(__get_user(c, &((unsigned char*)regs->eip)[i])) { bad: +#endif + printk(" Bad EIP value."); break; } @@ -229,8 +235,13 @@ eip = regs->eip; +#ifdef CONFIG_PAX_KERNEXEC + eip += __KERNEL_TEXT_OFFSET; +#else if (eip < PAGE_OFFSET) goto no_bug; +#endif + if (__get_user(ud2, (unsigned short *)eip)) goto no_bug; if (ud2 != 0x0b0f) @@ -238,7 +249,13 @@ if (__get_user(line, (unsigned short *)(eip + 2))) goto bug; if (__get_user(file, (char **)(eip + 4)) || + +#ifdef CONFIG_PAX_KERNEXEC + __get_user(c, file + __KERNEL_TEXT_OFFSET)) +#else (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) +#endif + file = ""; printk("------------[ cut here ]------------\n"); @@ -411,8 +428,16 @@ return; gp_in_kernel: - if (!fixup_exception(regs)) + if (!fixup_exception(regs)) { + +#ifdef CONFIG_PAX_KERNEXEC + if ((regs->xcs & 0xFFFF) == __KERNEL_CS) + die("PAX: suspicious general protection fault", regs, error_code); + else +#endif + die("general protection fault", regs, error_code); + } } static void mem_parity_error(unsigned char reason, struct pt_regs * regs) @@ -845,7 +870,7 @@ _set_gate(idt_table+n,15,3,addr,__KERNEL_CS); } -static void __init set_call_gate(void *a, void *addr) +static void __init set_call_gate(const void *a, void *addr) { _set_gate(a,12,3,addr,__KERNEL_CS); } diff -urN linux-2.6.5/arch/i386/kernel/vmlinux.lds.S linux-2.6.5/arch/i386/kernel/vmlinux.lds.S --- linux-2.6.5/arch/i386/kernel/vmlinux.lds.S 2004-04-03 22:36:26.000000000 -0500 +++ linux-2.6.5/arch/i386/kernel/vmlinux.lds.S 2004-04-18 11:05:50.000000000 -0400 @@ -2,7 +2,12 @@ * Written by Martin Mares ; */ +#include + #include +#include +#include + #include OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") @@ -11,25 +16,16 @@ jiffies = jiffies_64; SECTIONS { - . = 0xC0000000 + 0x100000; - /* read-only */ - _text = .; /* Text and read-only data */ - .text : { - *(.text) - *(.fixup) - *(.gnu.warning) - } = 0x9090 - - _etext = .; /* End of text section */ - - . = ALIGN(16); /* Exception table */ - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - RODATA + . = __PAGE_OFFSET + 0x100000; + .text.startup : { + BYTE(0xEA) /* jmp far */ + LONG(startup_32 + __KERNEL_TEXT_OFFSET - __PAGE_OFFSET) + SHORT(__BOOT_CS) + } /* writeable */ + . = ALIGN(32); + _data = .; .data : { /* Data */ *(.data) CONSTRUCTORS @@ -41,25 +37,28 @@ . = ALIGN(4096); __nosave_end = .; - . = ALIGN(4096); - .data.page_aligned : { *(.data.idt) } - . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } - _edata = .; /* End of data section */ - . = ALIGN(THREAD_SIZE); /* init_task */ .data.init_task : { *(.data.init_task) } + . = ALIGN(4096); + .data.page_aligned : { *(.data.swapper_pg_dir) } + + _edata = .; /* End of data section */ + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + LONG(0) + } + . = ALIGN(4); + __bss_stop = .; + /* will be freed after init */ . = ALIGN(4096); /* Init code and data */ __init_begin = .; - .init.text : { - _sinittext = .; - *(.init.text) - _einittext = .; - } .init.data : { *(.init.data) } . = ALIGN(16); __setup_start = .; @@ -100,16 +99,68 @@ __per_cpu_start = .; .data.percpu : { *(.data.percpu) } __per_cpu_end = .; + + /* read-only */ + +#ifdef CONFIG_PAX_KERNEXEC + __init_text_start = .; + .init.text (. - __KERNEL_TEXT_OFFSET) : AT (__init_text_start) { + _sinittext = .; + *(.init.text) + _einittext = .; + . = ALIGN(4*1024*1024) - 1; + BYTE(0) + } + . = ALIGN(4096); + __init_end = . + __KERNEL_TEXT_OFFSET; + /* freed after init ends here */ + +/* + * PaX: this must be kept in synch with the KERNEL_CS base + * in the GDTs in arch/i386/kernel/head.S + */ + _text = .; /* Text and read-only data */ + .text : AT (. + __KERNEL_TEXT_OFFSET) { +#else + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } . = ALIGN(4096); __init_end = .; /* freed after init ends here */ - - __bss_start = .; /* BSS */ - .bss : { *(.bss) } - . = ALIGN(4); - __bss_stop = .; + _text = .; /* Text and read-only data */ + .text : { +#endif + + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x9090 + + _etext = .; /* End of text section */ + . += __KERNEL_TEXT_OFFSET; + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + .rodata.page_aligned : { + *(.rodata.empty_zero_page) + *(.rodata.idt) + } + + RODATA + +#ifdef CONFIG_PAX_KERNEXEC + _end = ALIGN(4*1024*1024); + . = _end ; +#else _end = . ; +#endif /* This is where the kernel creates the early boot page tables */ . = ALIGN(4096); diff -urN linux-2.6.5/arch/i386/mm/fault.c linux-2.6.5/arch/i386/mm/fault.c --- linux-2.6.5/arch/i386/mm/fault.c 2004-04-03 22:36:13.000000000 -0500 +++ linux-2.6.5/arch/i386/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -21,6 +21,9 @@ #include /* For unblank_screen() */ #include #include +#include +#include +#include #include #include @@ -199,6 +202,10 @@ asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); +#if defined(CONFIG_PAX_PAGEEXEC) || defined(CONFIG_PAX_SEGMEXEC) +static int pax_handle_fetch_fault(struct pt_regs *regs); +#endif + /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate @@ -209,22 +216,31 @@ * bit 1 == 0 means read, 1 means write * bit 2 == 0 means kernel, 1 means user-mode */ + +#ifdef CONFIG_PAX_PAGEEXEC +static void do_page_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address) +#else asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +#endif { struct task_struct *tsk; struct mm_struct *mm; struct vm_area_struct * vma; +#ifndef CONFIG_PAX_PAGEEXEC unsigned long address; +#endif unsigned long page; int write; siginfo_t info; +#ifndef CONFIG_PAX_PAGEEXEC /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); /* It's safe to allow irq's after cr2 has been saved */ if (regs->eflags & (X86_EFLAGS_IF|VM_MASK)) local_irq_enable(); +#endif tsk = current; @@ -358,6 +374,34 @@ if (is_prefetch(regs, address)) return; +#ifdef CONFIG_PAX_SEGMEXEC + if (current->flags & PF_PAX_SEGMEXEC) { + +#if defined(CONFIG_PAX_EMUTRAMP) || defined(CONFIG_PAX_RANDEXEC) + if ((error_code == 4) && (regs->eip + SEGMEXEC_TASK_SIZE == address)) { + switch (pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_RANDEXEC + case 3: + return; +#endif + +#ifdef CONFIG_PAX_EMUTRAMP + case 2: + return; +#endif + + } + } +#endif + + if (address >= SEGMEXEC_TASK_SIZE) { + pax_report_fault(regs, (void*)regs->eip, (void*)regs->esp); + do_exit(SIGKILL); + } + } +#endif + tsk->thread.cr2 = address; /* Kernel addresses are always protection faults */ tsk->thread.error_code = error_code | (address >= TASK_SIZE); @@ -408,6 +452,13 @@ if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + +#ifdef CONFIG_PAX_KERNEXEC + else if (init_mm.start_code + __KERNEL_TEXT_OFFSET <= address && address < init_mm.end_code + __KERNEL_TEXT_OFFSET) + printk(KERN_ERR "PAX: %s:%d, uid/euid: %u/%u, attempted to modify kernel code", + tsk->comm, tsk->pid, tsk->uid, tsk->euid); +#endif + else printk(KERN_ALERT "Unable to handle kernel paging request"); printk(" at virtual address %08lx\n",address); @@ -509,3 +560,361 @@ return; } } + +#if defined(CONFIG_PAX_PAGEEXEC) || defined(CONFIG_PAX_SEGMEXEC) +/* + * PaX: decide what to do with offenders (regs->eip = fault address) + * + * returns 1 when task should be killed + * 2 when gcc trampoline was detected + * 3 when legitimate ET_EXEC was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#ifdef CONFIG_PAX_EMUTRAMP + static const unsigned char trans[8] = {6, 1, 2, 0, 13, 5, 3, 4}; +#endif + +#if defined(CONFIG_PAX_RANDEXEC) || defined(CONFIG_PAX_EMUTRAMP) + int err; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + if (current->flags & PF_PAX_RANDEXEC) { + unsigned long esp_4; + + if (regs->eip >= current->mm->start_code && + regs->eip < current->mm->end_code) + { + err = get_user(esp_4, (unsigned long*)(regs->esp-4UL)); + if (err || esp_4 == regs->eip) + return 1; + + regs->eip += current->mm->delta_exec; + return 3; + } + } +#endif + +#ifdef CONFIG_PAX_EMUTRAMP + do { /* PaX: gcc trampoline emulation #1 */ + unsigned char mov1, mov2; + unsigned short jmp; + unsigned long addr1, addr2, ret; + unsigned short call; + + err = get_user(mov1, (unsigned char *)regs->eip); + err |= get_user(addr1, (unsigned long *)(regs->eip + 1)); + err |= get_user(mov2, (unsigned char *)(regs->eip + 5)); + err |= get_user(addr2, (unsigned long *)(regs->eip + 6)); + err |= get_user(jmp, (unsigned short *)(regs->eip + 10)); + err |= get_user(ret, (unsigned long *)regs->esp); + + if (err) + break; + + err = get_user(call, (unsigned short *)(ret-2)); + if (err) + break; + + if ((mov1 & 0xF8) == 0xB8 && + (mov2 & 0xF8) == 0xB8 && + (mov1 & 0x07) != (mov2 & 0x07) && + (jmp & 0xF8FF) == 0xE0FF && + (mov2 & 0x07) == ((jmp>>8) & 0x07) && + (call & 0xF8FF) == 0xD0FF && + regs->eip == ((unsigned long*)regs)[trans[(call>>8) & 0x07]]) + { + ((unsigned long *)regs)[trans[mov1 & 0x07]] = addr1; + ((unsigned long *)regs)[trans[mov2 & 0x07]] = addr2; + regs->eip = addr2; + return 2; + } + } while (0); + + do { /* PaX: gcc trampoline emulation #2 */ + unsigned char mov, jmp; + unsigned long addr1, addr2, ret; + unsigned short call; + + err = get_user(mov, (unsigned char *)regs->eip); + err |= get_user(addr1, (unsigned long *)(regs->eip + 1)); + err |= get_user(jmp, (unsigned char *)(regs->eip + 5)); + err |= get_user(addr2, (unsigned long *)(regs->eip + 6)); + err |= get_user(ret, (unsigned long *)regs->esp); + + if (err) + break; + + err = get_user(call, (unsigned short *)(ret-2)); + if (err) + break; + + if ((mov & 0xF8) == 0xB8 && + jmp == 0xE9 && + (call & 0xF8FF) == 0xD0FF && + regs->eip == ((unsigned long*)regs)[trans[(call>>8) & 0x07]]) + { + ((unsigned long *)regs)[trans[mov & 0x07]] = addr1; + regs->eip += addr2 + 10; + return 2; + } + } while (0); + + do { /* PaX: gcc trampoline emulation #3 */ + unsigned char mov, jmp; + char offset; + unsigned long addr1, addr2, ret; + unsigned short call; + + err = get_user(mov, (unsigned char *)regs->eip); + err |= get_user(addr1, (unsigned long *)(regs->eip + 1)); + err |= get_user(jmp, (unsigned char *)(regs->eip + 5)); + err |= get_user(addr2, (unsigned long *)(regs->eip + 6)); + err |= get_user(ret, (unsigned long *)regs->esp); + + if (err) + break; + + err = get_user(call, (unsigned short *)(ret-3)); + err |= get_user(offset, (char *)(ret-1)); + if (err) + break; + + if ((mov & 0xF8) == 0xB8 && + jmp == 0xE9 && + call == 0x55FF) + { + unsigned long addr; + + err = get_user(addr, (unsigned long*)(regs->ebp + (unsigned long)(long)offset)); + if (err || regs->eip != addr) + break; + + ((unsigned long *)regs)[trans[mov & 0x07]] = addr1; + regs->eip += addr2 + 10; + return 2; + } + } while (0); + + do { /* PaX: gcc trampoline emulation #4 */ + unsigned char mov, jmp, sib; + char offset; + unsigned long addr1, addr2, ret; + unsigned short call; + + err = get_user(mov, (unsigned char *)regs->eip); + err |= get_user(addr1, (unsigned long *)(regs->eip + 1)); + err |= get_user(jmp, (unsigned char *)(regs->eip + 5)); + err |= get_user(addr2, (unsigned long *)(regs->eip + 6)); + err |= get_user(ret, (unsigned long *)regs->esp); + + if (err) + break; + + err = get_user(call, (unsigned short *)(ret-4)); + err |= get_user(sib, (unsigned char *)(ret-2)); + err |= get_user(offset, (char *)(ret-1)); + if (err) + break; + + if ((mov & 0xF8) == 0xB8 && + jmp == 0xE9 && + call == 0x54FF && + sib == 0x24) + { + unsigned long addr; + + err = get_user(addr, (unsigned long*)(regs->esp + 4 + (unsigned long)(long)offset)); + if (err || regs->eip != addr) + break; + + ((unsigned long *)regs)[trans[mov & 0x07]] = addr1; + regs->eip += addr2 + 10; + return 2; + } + } while (0); + + do { /* PaX: gcc trampoline emulation #5 */ + unsigned char mov, jmp, sib; + unsigned long addr1, addr2, ret, offset; + unsigned short call; + + err = get_user(mov, (unsigned char *)regs->eip); + err |= get_user(addr1, (unsigned long *)(regs->eip + 1)); + err |= get_user(jmp, (unsigned char *)(regs->eip + 5)); + err |= get_user(addr2, (unsigned long *)(regs->eip + 6)); + err |= get_user(ret, (unsigned long *)regs->esp); + + if (err) + break; + + err = get_user(call, (unsigned short *)(ret-7)); + err |= get_user(sib, (unsigned char *)(ret-5)); + err |= get_user(offset, (unsigned long *)(ret-4)); + if (err) + break; + + if ((mov & 0xF8) == 0xB8 && + jmp == 0xE9 && + call == 0x94FF && + sib == 0x24) + { + unsigned long addr; + + err = get_user(addr, (unsigned long*)(regs->esp + 4 + offset)); + if (err || regs->eip != addr) + break; + + ((unsigned long *)regs)[trans[mov & 0x07]] = addr1; + regs->eip += addr2 + 10; + return 2; + } + } while (0); +#endif + + return 1; /* PaX in action */ +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 20; i++) { + unsigned char c; + if (get_user(c, (unsigned char*)pc+i)) { + printk("."); + break; + } + printk("%02x ", c); + } + printk("\n"); + + printk(KERN_ERR "PAX: bytes at SP: "); + for (i = 0; i < 20; i++) { + unsigned long c; + if (get_user(c, (unsigned long*)sp+i)) { + printk("."); + break; + } + printk("%08lx ", c); + } + printk("\n"); +} +#endif + +#ifdef CONFIG_PAX_PAGEEXEC +/* PaX: called with the page_table_lock spinlock held */ +static inline pte_t * pax_get_pte(struct mm_struct *mm, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + + pgd = pgd_offset(mm, address); + if (!pgd || !pgd_present(*pgd)) + return 0; + pmd = pmd_offset(pgd, address); + if (!pmd || !pmd_present(*pmd)) + return 0; + return pte_offset_map(pmd, address); +} + +/* + * PaX: handle the extra page faults or pass it down to the original handler + */ +asmlinkage void pax_do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + struct mm_struct *mm = current->mm; + unsigned long address; + pte_t *pte; + unsigned char pte_mask; + int ret; + + __asm__("movl %%cr2,%0":"=r" (address)); + + /* It's safe to allow irq's after cr2 has been saved */ + if (regs->eflags & (X86_EFLAGS_IF|VM_MASK)) + local_irq_enable(); + + if (unlikely((error_code & 5) != 5 || + address >= TASK_SIZE || + !(current->flags & PF_PAX_PAGEEXEC))) + return do_page_fault(regs, error_code, address); + + /* PaX: it's our fault, let's handle it if we can */ + + /* PaX: take a look at read faults before acquiring any locks */ + if (unlikely((error_code == 5) && (regs->eip == address))) { + /* instruction fetch attempt from a protected page in user mode */ + ret = pax_handle_fetch_fault(regs); + switch (ret) { + +#ifdef CONFIG_PAX_RANDEXEC + case 3: + return; +#endif + +#ifdef CONFIG_PAX_EMUTRAMP + case 2: + return; +#endif + + case 1: + default: + pax_report_fault(regs, (void*)regs->eip, (void*)regs->esp); + do_exit(SIGKILL); + } + } + + pte_mask = _PAGE_ACCESSED | _PAGE_USER | ((error_code & 2) << (_PAGE_BIT_DIRTY-1)); + + spin_lock(&mm->page_table_lock); + pte = pax_get_pte(mm, address); + if (unlikely(!pte || !(pte_val(*pte) & _PAGE_PRESENT) || pte_exec(*pte))) { + pte_unmap(pte); + spin_unlock(&mm->page_table_lock); + do_page_fault(regs, error_code, address); + return; + } + + if (unlikely((error_code == 7) && !pte_write(*pte))) { + /* write attempt to a protected page in user mode */ + pte_unmap(pte); + spin_unlock(&mm->page_table_lock); + do_page_fault(regs, error_code, address); + return; + } + + /* + * PaX: fill DTLB with user rights and retry + */ + __asm__ __volatile__ ( + "orb %2,%1\n" +#if defined(CONFIG_M586) || defined(CONFIG_M586TSC) +/* + * PaX: let this uncommented 'invlpg' remind us on the behaviour of Intel's + * (and AMD's) TLBs. namely, they do not cache PTEs that would raise *any* + * page fault when examined during a TLB load attempt. this is true not only + * for PTEs holding a non-present entry but also present entries that will + * raise a page fault (such as those set up by PaX, or the copy-on-write + * mechanism). in effect it means that we do *not* need to flush the TLBs + * for our target pages since their PTEs are simply not in the TLBs at all. + + * the best thing in omitting it is that we gain around 15-20% speed in the + * fast path of the page fault handler and can get rid of tracing since we + * can no longer flush unintended entries. + */ + "invlpg %0\n" +#endif + "testb $0,%0\n" + "xorb %3,%1\n" + : + : "m" (*(char*)address), "m" (*(char*)pte), "q" (pte_mask), "i" (_PAGE_USER) + : "memory", "cc"); + pte_unmap(pte); + spin_unlock(&mm->page_table_lock); + return; +} +#endif diff -urN linux-2.6.5/arch/i386/mm/init.c linux-2.6.5/arch/i386/mm/init.c --- linux-2.6.5/arch/i386/mm/init.c 2004-04-03 22:37:39.000000000 -0500 +++ linux-2.6.5/arch/i386/mm/init.c 2004-04-18 11:05:50.000000000 -0400 @@ -40,6 +40,7 @@ #include #include #include +#include DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); unsigned long highstart_pfn, highend_pfn; @@ -394,6 +395,10 @@ #endif __flush_tlb_all(); +#ifdef CONFIG_PAX_KERNEXEC + memcpy(kernexec_pg_dir, swapper_pg_dir, sizeof(kernexec_pg_dir)); +#endif + kmap_init(); zone_sizes_init(); } @@ -488,7 +493,7 @@ set_highmem_pages_init(bad_ppro); codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; + datasize = (unsigned long) &_edata - (unsigned long) &_data; initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; kclist_add(&kcore_mem, __va(0), max_low_pfn << PAGE_SHIFT); @@ -587,6 +592,42 @@ totalram_pages++; } printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (__init_end - __init_begin) >> 10); + +#ifdef CONFIG_PAX_KERNEXEC + /* PaX: limit KERNEL_CS to actual size */ + { + unsigned long limit; + int cpu; + pgd_t *pgd; + pmd_t *pmd; + + limit = (unsigned long)&_etext >> PAGE_SHIFT; + for (cpu = 0; cpu < NR_CPUS; cpu++) { + cpu_gdt_table[cpu][GDT_ENTRY_KERNEL_CS].a = (cpu_gdt_table[cpu][GDT_ENTRY_KERNEL_CS].a & 0xFFFF0000UL) | (limit & 0x0FFFFUL); + cpu_gdt_table[cpu][GDT_ENTRY_KERNEL_CS].b = (cpu_gdt_table[cpu][GDT_ENTRY_KERNEL_CS].b & 0xFFF0FFFFUL) | (limit & 0xF0000UL); + +#ifdef CONFIG_PCI_BIOS + printk(KERN_INFO "PAX: warning, PCI BIOS might still be in use, keeping flat KERNEL_CS.\n"); +#endif + + } + + /* PaX: make KERNEL_CS read-only */ + for (addr = __KERNEL_TEXT_OFFSET; addr < __KERNEL_TEXT_OFFSET + 0x00400000UL; addr += (1UL << PMD_SHIFT)) { + pgd = pgd_offset_k(addr); + pmd = pmd_offset(pgd, addr); + set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_GLOBAL)); + } + memcpy(kernexec_pg_dir, swapper_pg_dir, sizeof(kernexec_pg_dir)); + for (addr = __KERNEL_TEXT_OFFSET; addr < __KERNEL_TEXT_OFFSET + 0x00400000UL; addr += (1UL << PMD_SHIFT)) { + pgd = pgd_offset_k(addr); + pmd = pmd_offset(pgd, addr); + set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_RW)); + } + flush_tlb_all(); + } +#endif + } #ifdef CONFIG_BLK_DEV_INITRD diff -urN linux-2.6.5/arch/i386/pci/pcbios.c linux-2.6.5/arch/i386/pci/pcbios.c --- linux-2.6.5/arch/i386/pci/pcbios.c 2004-04-03 22:36:17.000000000 -0500 +++ linux-2.6.5/arch/i386/pci/pcbios.c 2004-04-18 11:05:50.000000000 -0400 @@ -6,7 +6,7 @@ #include #include "pci.h" #include "pci-functions.h" - +#include /* BIOS32 signature: "_32_" */ #define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) @@ -33,6 +33,12 @@ * and the PCI BIOS specification. */ +#if defined(CONFIG_PAX_KERNEXEC) && defined(CONFIG_PCI_BIOS) +#define __FLAT_KERNEL_CS 0x20 +#else +#define __FLAT_KERNEL_CS __KERNEL_CS +#endif + union bios32 { struct { unsigned long signature; /* _32_ */ @@ -55,7 +61,7 @@ static struct { unsigned long address; unsigned short segment; -} bios32_indirect = { 0, __KERNEL_CS }; +} bios32_indirect = { 0, __FLAT_KERNEL_CS }; /* * Returns the entry point for the given service, NULL on error @@ -96,7 +102,9 @@ static struct { unsigned long address; unsigned short segment; -} pci_indirect = { 0, __KERNEL_CS }; +} pci_indirect = { 0, __FLAT_KERNEL_CS }; + +#undef __FLAT_KERNEL_CS static int pci_bios_present; diff -urN linux-2.6.5/arch/ia64/ia32/binfmt_elf32.c linux-2.6.5/arch/ia64/ia32/binfmt_elf32.c --- linux-2.6.5/arch/ia64/ia32/binfmt_elf32.c 2004-04-03 22:38:16.000000000 -0500 +++ linux-2.6.5/arch/ia64/ia32/binfmt_elf32.c 2004-04-18 11:05:50.000000000 -0400 @@ -41,6 +41,17 @@ #undef SET_PERSONALITY #define SET_PERSONALITY(ex, ibcs2) elf32_set_personality() +#ifdef CONFIG_PAX_ASLR +#define PAX_ELF_ET_DYN_BASE(tsk) ((tsk)->personality == PER_LINUX32 ? 0x08048000UL : 0x4000000000000000UL) + +#define PAX_DELTA_MMAP_LSB(tsk) IA32_PAGE_SHIFT +#define PAX_DELTA_MMAP_LEN(tsk) ((tsk)->personality == PER_LINUX32 ? 16 : 43 - IA32_PAGE_SHIFT) +#define PAX_DELTA_EXEC_LSB(tsk) IA32_PAGE_SHIFT +#define PAX_DELTA_EXEC_LEN(tsk) ((tsk)->personality == PER_LINUX32 ? 16 : 43 - IA32_PAGE_SHIFT) +#define PAX_DELTA_STACK_LSB(tsk) IA32_PAGE_SHIFT +#define PAX_DELTA_STACK_LEN(tsk) ((tsk)->personality == PER_LINUX32 ? 16 : 43 - IA32_PAGE_SHIFT) +#endif + /* Ugly but avoids duplication */ #include "../../../fs/binfmt_elf.c" @@ -178,7 +189,7 @@ 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_page_prot = protection_map[VM_STACK_FLAGS & 0x7]; mpnt->vm_flags = VM_STACK_FLAGS; mpnt->vm_ops = NULL; mpnt->vm_pgoff = 0; diff -urN linux-2.6.5/arch/ia64/ia32/ia32priv.h linux-2.6.5/arch/ia64/ia32/ia32priv.h --- linux-2.6.5/arch/ia64/ia32/ia32priv.h 2004-04-03 22:38:22.000000000 -0500 +++ linux-2.6.5/arch/ia64/ia32/ia32priv.h 2004-04-18 11:05:50.000000000 -0400 @@ -295,7 +295,14 @@ #define ELF_ARCH EM_386 #define IA32_PAGE_OFFSET 0xc0000000 -#define IA32_STACK_TOP IA32_PAGE_OFFSET + +#ifdef CONFIG_PAX_RANDUSTACK +#define __IA32_DELTA_STACK (current->mm->delta_stack) +#else +#define __IA32_DELTA_STACK 0UL +#endif + +#define IA32_STACK_TOP (IA32_PAGE_OFFSET - __IA32_DELTA_STACK) /* * The system segments (GDT, TSS, LDT) have to be mapped below 4GB so the IA-32 engine can diff -urN linux-2.6.5/arch/ia64/ia32/sys_ia32.c linux-2.6.5/arch/ia64/ia32/sys_ia32.c --- linux-2.6.5/arch/ia64/ia32/sys_ia32.c 2004-04-03 22:37:24.000000000 -0500 +++ linux-2.6.5/arch/ia64/ia32/sys_ia32.c 2004-04-18 11:05:50.000000000 -0400 @@ -475,6 +475,11 @@ flags = a.flags; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { file = fget(a.fd); @@ -496,6 +501,11 @@ struct file *file = NULL; unsigned long retval; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); diff -urN linux-2.6.5/arch/ia64/kernel/ptrace.c linux-2.6.5/arch/ia64/kernel/ptrace.c --- linux-2.6.5/arch/ia64/kernel/ptrace.c 2004-04-03 22:38:22.000000000 -0500 +++ linux-2.6.5/arch/ia64/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -1314,6 +1315,9 @@ if (pid == 1) /* no messing around with init! */ goto out_tsk; + if (gr_handle_ptrace(child, request)) + goto out_tsk; + if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; diff -urN linux-2.6.5/arch/ia64/kernel/sys_ia64.c linux-2.6.5/arch/ia64/kernel/sys_ia64.c --- linux-2.6.5/arch/ia64/kernel/sys_ia64.c 2004-04-03 22:37:23.000000000 -0500 +++ linux-2.6.5/arch/ia64/kernel/sys_ia64.c 2004-04-18 11:05:50.000000000 -0400 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,13 @@ if (REGION_NUMBER(addr) == REGION_HPAGE) addr = 0; #endif + +#ifdef CONFIG_PAX_RANDMMAP + if ((current->flags & PF_PAX_RANDMMAP) && addr && filp) + addr = mm->free_area_cache; + else +#endif + if (!addr) addr = mm->free_area_cache; @@ -58,6 +66,13 @@ if (TASK_SIZE - len < addr || RGN_MAP_LIMIT - len < REGION_OFFSET(addr)) { if (start_addr != TASK_UNMAPPED_BASE) { /* Start a new search --- just in case we missed some holes. */ + +#ifdef CONFIG_PAX_RANDMMAP + if (current->flags & PF_PAX_RANDMMAP) + addr = TASK_UNMAPPED_BASE + mm->delta_mmap; + else +#endif + addr = TASK_UNMAPPED_BASE; goto full_search; } @@ -185,6 +200,11 @@ unsigned long roff; struct file *file = 0; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); @@ -216,6 +236,11 @@ goto out; } + if (gr_handle_mmap(file, prot)) { + addr = -EACCES; + goto out; + } + down_write(¤t->mm->mmap_sem); addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); up_write(¤t->mm->mmap_sem); diff -urN linux-2.6.5/arch/ia64/mm/fault.c linux-2.6.5/arch/ia64/mm/fault.c --- linux-2.6.5/arch/ia64/mm/fault.c 2004-04-03 22:38:20.000000000 -0500 +++ linux-2.6.5/arch/ia64/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,54 @@ return pte_present(pte); } +#ifdef CONFIG_PAX_PAGEEXEC +/* + * PaX: decide what to do with offenders (regs->cr_iip = fault address) + * + * returns 1 when task should be killed + * 2 when legitimate ET_EXEC was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#ifdef CONFIG_PAX_RANDEXEC + int err; + + if (current->flags & PF_PAX_RANDEXEC) { + if (regs->cr_iip >= current->mm->start_code && + regs->cr_iip < current->mm->end_code) + { +#if 0 + /* PaX: this needs fixing */ + if (regs->b0 == regs->cr_iip) + return 1; +#endif + regs->cr_iip += current->mm->delta_exec; + return 2; + } + } +#endif + + return 1; +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 8; i++) { + unsigned int c; + if (get_user(c, (unsigned int*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif + void ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *regs) { @@ -125,9 +174,31 @@ | (((isr >> IA64_ISR_W_BIT) & 1UL) << VM_WRITE_BIT) | (((isr >> IA64_ISR_R_BIT) & 1UL) << VM_READ_BIT)); - if ((vma->vm_flags & mask) != mask) + if ((vma->vm_flags & mask) != mask) { + +#ifdef CONFIG_PAX_PAGEEXEC + if (!(vma->vm_flags & VM_EXEC) && (mask & VM_EXEC)) { + if (!(current->flags & PF_PAX_PAGEEXEC) || address != regs->cr_iip) + goto bad_area; + + up_read(&mm->mmap_sem); + switch(pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_RANDEXEC + case 2: + return; +#endif + + } + pax_report_fault(regs, (void*)regs->cr_iip, (void*)regs->r12); + do_exit(SIGKILL); + } +#endif + goto bad_area; + } + survive: /* * If for any reason at all we couldn't handle the fault, make diff -urN linux-2.6.5/arch/mips/kernel/binfmt_elfn32.c linux-2.6.5/arch/mips/kernel/binfmt_elfn32.c --- linux-2.6.5/arch/mips/kernel/binfmt_elfn32.c 2004-04-03 22:37:23.000000000 -0500 +++ linux-2.6.5/arch/mips/kernel/binfmt_elfn32.c 2004-04-18 11:05:50.000000000 -0400 @@ -50,6 +50,17 @@ #undef ELF_ET_DYN_BASE #define ELF_ET_DYN_BASE (TASK32_SIZE / 3 * 2) +#ifdef CONFIG_PAX_ASLR +#define PAX_ELF_ET_DYN_BASE(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 0x00400000UL : 0x00400000UL) + +#define PAX_DELTA_MMAP_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_MMAP_LEN(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 27-PAGE_SHIFT : 36-PAGE_SHIFT) +#define PAX_DELTA_EXEC_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_EXEC_LEN(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 27-PAGE_SHIFT : 36-PAGE_SHIFT) +#define PAX_DELTA_STACK_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_STACK_LEN(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 27-PAGE_SHIFT : 36-PAGE_SHIFT) +#endif + #include #include #include diff -urN linux-2.6.5/arch/mips/kernel/binfmt_elfo32.c linux-2.6.5/arch/mips/kernel/binfmt_elfo32.c --- linux-2.6.5/arch/mips/kernel/binfmt_elfo32.c 2004-04-03 22:36:55.000000000 -0500 +++ linux-2.6.5/arch/mips/kernel/binfmt_elfo32.c 2004-04-18 11:05:50.000000000 -0400 @@ -52,6 +52,17 @@ #undef ELF_ET_DYN_BASE #define ELF_ET_DYN_BASE (TASK32_SIZE / 3 * 2) +#ifdef CONFIG_PAX_ASLR +#define PAX_ELF_ET_DYN_BASE(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 0x00400000UL : 0x00400000UL) + +#define PAX_DELTA_MMAP_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_MMAP_LEN(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 27-PAGE_SHIFT : 36-PAGE_SHIFT) +#define PAX_DELTA_EXEC_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_EXEC_LEN(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 27-PAGE_SHIFT : 36-PAGE_SHIFT) +#define PAX_DELTA_STACK_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_STACK_LEN(tsk) (((tsk)->thread.mflags & MF_32BIT_ADDR) ? 27-PAGE_SHIFT : 36-PAGE_SHIFT) +#endif + #include #include #include diff -urN linux-2.6.5/arch/mips/kernel/syscall.c linux-2.6.5/arch/mips/kernel/syscall.c --- linux-2.6.5/arch/mips/kernel/syscall.c 2004-04-03 22:37:41.000000000 -0500 +++ linux-2.6.5/arch/mips/kernel/syscall.c 2004-04-18 11:05:50.000000000 -0400 @@ -79,6 +79,11 @@ do_color_align = 0; if (filp || (flags & MAP_SHARED)) do_color_align = 1; + +#ifdef CONFIG_PAX_RANDMMAP + if (!(current->flags & PF_PAX_RANDMMAP) || !filp) +#endif + if (addr) { if (do_color_align) addr = COLOUR_ALIGN(addr, pgoff); @@ -89,6 +94,13 @@ (!vmm || addr + len <= vmm->vm_start)) return addr; } + +#ifdef CONFIG_PAX_RANDMMAP + if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp)) + addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap; + else +#endif + addr = TASK_UNMAPPED_BASE; if (do_color_align) addr = COLOUR_ALIGN(addr, pgoff); diff -urN linux-2.6.5/arch/mips/mm/fault.c linux-2.6.5/arch/mips/mm/fault.c --- linux-2.6.5/arch/mips/mm/fault.c 2004-04-03 22:36:53.000000000 -0500 +++ linux-2.6.5/arch/mips/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -28,6 +28,24 @@ #include #include +#ifdef CONFIG_PAX_PAGEEXEC +void pax_report_insns(void *pc) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 5; i++) { + unsigned int c; + if (get_user(c, (unsigned int*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif + /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate diff -urN linux-2.6.5/arch/parisc/kernel/ptrace.c linux-2.6.5/arch/parisc/kernel/ptrace.c --- linux-2.6.5/arch/parisc/kernel/ptrace.c 2004-04-03 22:36:19.000000000 -0500 +++ linux-2.6.5/arch/parisc/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -114,6 +115,9 @@ if (pid == 1) /* no messing around with init! */ goto out_tsk; + if (gr_handle_ptrace(child, request)) + goto out_tsk; + if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; diff -urN linux-2.6.5/arch/parisc/kernel/sys_parisc.c linux-2.6.5/arch/parisc/kernel/sys_parisc.c --- linux-2.6.5/arch/parisc/kernel/sys_parisc.c 2004-04-03 22:38:12.000000000 -0500 +++ linux-2.6.5/arch/parisc/kernel/sys_parisc.c 2004-04-18 11:05:50.000000000 -0400 @@ -31,6 +31,7 @@ #include #include #include +#include int sys_pipe(int *fildes) { @@ -114,6 +115,13 @@ { if (len > TASK_SIZE) return -ENOMEM; + +#ifdef CONFIG_PAX_RANDMMAP + if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp)) + addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap; + else +#endif + if (!addr) addr = TASK_UNMAPPED_BASE; @@ -131,12 +139,23 @@ { struct file * file = NULL; unsigned long error = -EBADF; + +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); if (!file) goto out; } + if (gr_handle_mmap(file, prot)) { + fput(file); + return -EACCES; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); down_write(¤t->mm->mmap_sem); diff -urN linux-2.6.5/arch/parisc/kernel/sys_parisc32.c linux-2.6.5/arch/parisc/kernel/sys_parisc32.c --- linux-2.6.5/arch/parisc/kernel/sys_parisc32.c 2004-04-03 22:36:55.000000000 -0500 +++ linux-2.6.5/arch/parisc/kernel/sys_parisc32.c 2004-04-18 11:05:50.000000000 -0400 @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include @@ -171,6 +173,11 @@ struct file *file; int retval; int i; +#ifdef CONFIG_GRKERNSEC + struct file *old_exec_file; + struct acl_subject_label *old_acl; + struct rlimit old_rlim[RLIM_NLIMITS]; +#endif file = open_exec(filename); @@ -178,7 +185,26 @@ if (IS_ERR(file)) return retval; + gr_learn_resource(current, RLIMIT_NPROC, atomic_read(¤t->user->processes, 1); + + if (gr_handle_nproc()) { + allow_write_access(file); + fput(file); + return -EAGAIN; + } + + if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) { + allow_write_access(file); + fput(file); + return -EACCES; + } + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + +#ifdef CONFIG_PAX_RANDUSTACK + bprm.p -= (pax_get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK; +#endif + memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); DBG(("do_execve32(%s, %p, %p, %p)\n", filename, argv, envp, regs)); @@ -209,11 +235,24 @@ if (retval < 0) goto out; + if (!gr_tpe_allow(file)) { + retval = -EACCES; + goto out; + } + + if (gr_check_crash_exec(file)) { + retval = -EACCES; + goto out; + } + retval = copy_strings_kernel(1, &bprm.filename, &bprm); if (retval < 0) goto out; bprm.exec = bprm.p; + + gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt); + retval = copy_strings32(bprm.envc, envp, &bprm); if (retval < 0) goto out; @@ -222,10 +261,32 @@ if (retval < 0) goto out; +#ifdef CONFIG_GRKERNSEC + old_acl = current->acl; + memcpy(old_rlim, current->rlim, sizeof(old_rlim)); + old_exec_file = current->exec_file; + get_file(file); + current->exec_file = file; +#endif + + gr_set_proc_label(file->f_dentry, file->f_vfsmnt); + retval = search_binary_handler(&bprm,regs); - if (retval >= 0) + if (retval >= 0) { +#ifdef CONFIG_GRKERNSEC + if (old_exec_file) + fput(old_exec_file); +#endif /* execve success */ return retval; + } + +#ifdef CONFIG_GRKERNSEC + current->acl = old_acl; + memcpy(current->rlim, old_rlim, sizeof(old_rlim)); + fput(current->exec_file); + current->exec_file = old_exec_file; +#endif out: /* Something went wrong, return the inode and free the argument pages*/ diff -urN linux-2.6.5/arch/parisc/kernel/traps.c linux-2.6.5/arch/parisc/kernel/traps.c --- linux-2.6.5/arch/parisc/kernel/traps.c 2004-04-03 22:38:27.000000000 -0500 +++ linux-2.6.5/arch/parisc/kernel/traps.c 2004-04-18 11:05:50.000000000 -0400 @@ -661,9 +661,7 @@ down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm,regs->iaoq[0]); - if (vma && (regs->iaoq[0] >= vma->vm_start) - && (vma->vm_flags & VM_EXEC)) { - + if (vma && (regs->iaoq[0] >= vma->vm_start)) { fault_address = regs->iaoq[0]; fault_space = regs->iasq[0]; diff -urN linux-2.6.5/arch/parisc/mm/fault.c linux-2.6.5/arch/parisc/mm/fault.c --- linux-2.6.5/arch/parisc/mm/fault.c 2004-04-03 22:38:13.000000000 -0500 +++ linux-2.6.5/arch/parisc/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -54,7 +56,7 @@ static unsigned long parisc_acctyp(unsigned long code, unsigned int inst) { - if (code == 6 || code == 16) + if (code == 6 || code == 7 || code == 16) return VM_EXEC; switch (inst & 0xf0000000) { @@ -140,6 +142,139 @@ } #endif +#ifdef CONFIG_PAX_PAGEEXEC +/* + * PaX: decide what to do with offenders (instruction_pointer(regs) = fault address) + * + * returns 1 when task should be killed + * 2 when rt_sigreturn trampoline was detected + * 3 when unpatched PLT trampoline was detected + * 4 when legitimate ET_EXEC was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#if defined(CONFIG_PAX_EMUPLT) || defined(CONFIG_PAX_EMUTRAMP) + int err; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + if (current->flags & PF_PAX_RANDEXEC) { + if (instruction_pointer(regs) >= current->mm->start_code && + instruction_pointer(regs) < current->mm->end_code) + { +#if 0 + /* PaX: this needs fixing */ + if ((regs->gr[2] & ~3UL) == instruction_pointer(regs)) + return 1; +#endif + regs->iaoq[0] += current->mm->delta_exec; + if ((regs->iaoq[1] & ~3UL) >= current->mm->start_code && + (regs->iaoq[1] & ~3UL) < current->mm->end_code) + regs->iaoq[1] += current->mm->delta_exec; + return 4; + } + } +#endif + +#ifdef CONFIG_PAX_EMUPLT + do { /* PaX: unpatched PLT emulation */ + unsigned int bl, depwi; + + err = get_user(bl, (unsigned int*)instruction_pointer(regs)); + err |= get_user(depwi, (unsigned int*)(instruction_pointer(regs)+4)); + + if (err) + break; + + if (bl == 0xEA9F1FDDU && depwi == 0xD6801C1EU) { + unsigned int ldw, bv, ldw2, addr = instruction_pointer(regs)-12; + + err = get_user(ldw, (unsigned int*)addr); + err |= get_user(bv, (unsigned int*)(addr+4)); + err |= get_user(ldw2, (unsigned int*)(addr+8)); + + if (err) + break; + + if (ldw == 0x0E801096U && + bv == 0xEAC0C000U && + ldw2 == 0x0E881095U) + { + unsigned int resolver, map; + + err = get_user(resolver, (unsigned int*)(instruction_pointer(regs)+8)); + err |= get_user(map, (unsigned int*)(instruction_pointer(regs)+12)); + if (err) + break; + + regs->gr[20] = instruction_pointer(regs)+8; + regs->gr[21] = map; + regs->gr[22] = resolver; + regs->iaoq[0] = resolver | 3UL; + regs->iaoq[1] = regs->iaoq[0] + 4; + return 3; + } + } + } while (0); +#endif + +#ifdef CONFIG_PAX_EMUTRAMP + +#ifndef CONFIG_PAX_EMUSIGRT + if (!(current->flags & PF_PAX_EMUTRAMP)) + return 1; +#endif + + do { /* PaX: rt_sigreturn emulation */ + unsigned int ldi1, ldi2, bel, nop; + + err = get_user(ldi1, (unsigned int *)instruction_pointer(regs)); + err |= get_user(ldi2, (unsigned int *)(instruction_pointer(regs)+4)); + err |= get_user(bel, (unsigned int *)(instruction_pointer(regs)+8)); + err |= get_user(nop, (unsigned int *)(instruction_pointer(regs)+12)); + + if (err) + break; + + if ((ldi1 == 0x34190000U || ldi1 == 0x34190002U) && + ldi2 == 0x3414015AU && + bel == 0xE4008200U && + nop == 0x08000240U) + { + regs->gr[25] = (ldi1 & 2) >> 1; + regs->gr[20] = __NR_rt_sigreturn; + regs->gr[31] = regs->iaoq[1] + 16; + regs->sr[0] = regs->iasq[1]; + regs->iaoq[0] = 0x100UL; + regs->iaoq[1] = regs->iaoq[0] + 4; + regs->iasq[0] = regs->sr[2]; + regs->iasq[1] = regs->sr[2]; + return 2; + } + } while (0); +#endif + + return 1; +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 5; i++) { + unsigned int c; + if (get_user(c, (unsigned int*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif + void do_page_fault(struct pt_regs *regs, unsigned long code, unsigned long address) { @@ -165,8 +300,38 @@ acc_type = parisc_acctyp(code,regs->iir); - if ((vma->vm_flags & acc_type) != acc_type) + if ((vma->vm_flags & acc_type) != acc_type) { + +#ifdef CONFIG_PAX_PAGEEXEC + if ((current->flags & PF_PAX_PAGEEXEC) && (acc_type & VM_EXEC) && + (address & ~3UL) == instruction_pointer(regs)) + { + up_read(&mm->mmap_sem); + switch(pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_RANDEXEC + case 4: + return; +#endif + +#ifdef CONFIG_PAX_EMUPLT + case 3: + return; +#endif + +#ifdef CONFIG_PAX_EMUTRAMP + case 2: + return; +#endif + + } + pax_report_fault(regs, (void*)instruction_pointer(regs), (void*)regs->gr[30]); + do_exit(SIGKILL); + } +#endif + goto bad_area; + } /* * If for any reason at all we couldn't handle the fault, make diff -urN linux-2.6.5/arch/ppc/kernel/ptrace.c linux-2.6.5/arch/ppc/kernel/ptrace.c --- linux-2.6.5/arch/ppc/kernel/ptrace.c 2004-04-03 22:36:52.000000000 -0500 +++ linux-2.6.5/arch/ppc/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -202,6 +203,9 @@ if (pid == 1) /* you may not mess with init */ goto out_tsk; + if (gr_handle_ptrace(child, request)) + goto out_tsk; + if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; diff -urN linux-2.6.5/arch/ppc/kernel/syscalls.c linux-2.6.5/arch/ppc/kernel/syscalls.c --- linux-2.6.5/arch/ppc/kernel/syscalls.c 2004-04-03 22:38:18.000000000 -0500 +++ linux-2.6.5/arch/ppc/kernel/syscalls.c 2004-04-18 11:05:50.000000000 -0400 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -165,14 +166,25 @@ struct file * file = NULL; int ret = -EBADF; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { if (!(file = fget(fd))) goto out; } + if (gr_handle_mmap(file, prot)) { + fput(file); + ret = -EACCES; + goto out; + } + down_write(¤t->mm->mmap_sem); - ret = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + ret = do_mmap(file, addr, len, prot, flags, pgoff << PAGE_SHIFT); up_write(¤t->mm->mmap_sem); if (file) fput(file); diff -urN linux-2.6.5/arch/ppc/mm/fault.c linux-2.6.5/arch/ppc/mm/fault.c --- linux-2.6.5/arch/ppc/mm/fault.c 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/arch/ppc/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -28,6 +28,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -56,6 +61,366 @@ void do_page_fault(struct pt_regs *, unsigned long, unsigned long); extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); +#ifdef CONFIG_PAX_EMUSIGRT +void pax_syscall_close(struct vm_area_struct * vma) +{ + vma->vm_mm->call_syscall = 0UL; +} + +static struct page* pax_syscall_nopage(struct vm_area_struct *vma, unsigned long address, int *type) +{ + struct page* page; + unsigned int *kaddr; + + page = alloc_page(GFP_HIGHUSER); + if (!page) + return NOPAGE_OOM; + + kaddr = kmap(page); + memset(kaddr, 0, PAGE_SIZE); + kaddr[0] = 0x44000002U; /* sc */ + __flush_dcache_icache(kaddr); + kunmap(page); + if (type) + *type = VM_FAULT_MAJOR; + return page; +} + +static struct vm_operations_struct pax_vm_ops = { + close: pax_syscall_close, + nopage: pax_syscall_nopage, +}; + +static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr) +{ + vma->vm_mm = current->mm; + vma->vm_start = addr; + vma->vm_end = addr + PAGE_SIZE; + vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC; + vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f]; + vma->vm_ops = &pax_vm_ops; + vma->vm_pgoff = 0UL; + vma->vm_file = NULL; + vma->vm_private_data = NULL; + INIT_LIST_HEAD(&vma->shared); + insert_vm_struct(current->mm, vma); + ++current->mm->total_vm; +} +#endif + +#ifdef CONFIG_PAX_PAGEEXEC +/* + * PaX: decide what to do with offenders (regs->nip = fault address) + * + * returns 1 when task should be killed + * 2 when patched GOT trampoline was detected + * 3 when patched PLT trampoline was detected + * 4 when unpatched PLT trampoline was detected + * 5 when legitimate ET_EXEC was detected + * 6 when sigreturn trampoline was detected + * 7 when rt_sigreturn trampoline was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#if defined(CONFIG_PAX_EMUPLT) || defined(CONFIG_PAX_EMUSIGRT) + int err; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + if (current->flags & PF_PAX_RANDEXEC) { + if (regs->nip >= current->mm->start_code && + regs->nip < current->mm->end_code) + { + if (regs->link == regs->nip) + return 1; + + regs->nip += current->mm->delta_exec; + return 5; + } + } +#endif + +#ifdef CONFIG_PAX_EMUPLT + do { /* PaX: patched GOT emulation */ + unsigned int blrl; + + err = get_user(blrl, (unsigned int*)regs->nip); + + if (!err && blrl == 0x4E800021U) { + unsigned long temp = regs->nip; + + regs->nip = regs->link & 0xFFFFFFFCUL; + regs->link = temp + 4UL; + return 2; + } + } while (0); + + do { /* PaX: patched PLT emulation #1 */ + unsigned int b; + + err = get_user(b, (unsigned int *)regs->nip); + + if (!err && (b & 0xFC000003U) == 0x48000000U) { + regs->nip += (((b | 0xFC000000UL) ^ 0x02000000UL) + 0x02000000UL); + return 3; + } + } while (0); + + do { /* PaX: unpatched PLT emulation #1 */ + unsigned int li, b; + + err = get_user(li, (unsigned int *)regs->nip); + err |= get_user(b, (unsigned int *)(regs->nip+4)); + + if (!err && (li & 0xFFFF0000U) == 0x39600000U && (b & 0xFC000003U) == 0x48000000U) { + unsigned int rlwinm, add, li2, addis2, mtctr, li3, addis3, bctr; + unsigned long addr = b | 0xFC000000UL; + + addr = regs->nip + 4 + ((addr ^ 0x02000000UL) + 0x02000000UL); + err = get_user(rlwinm, (unsigned int*)addr); + err |= get_user(add, (unsigned int*)(addr+4)); + err |= get_user(li2, (unsigned int*)(addr+8)); + err |= get_user(addis2, (unsigned int*)(addr+12)); + err |= get_user(mtctr, (unsigned int*)(addr+16)); + err |= get_user(li3, (unsigned int*)(addr+20)); + err |= get_user(addis3, (unsigned int*)(addr+24)); + err |= get_user(bctr, (unsigned int*)(addr+28)); + + if (err) + break; + + if (rlwinm == 0x556C083CU && + add == 0x7D6C5A14U && + (li2 & 0xFFFF0000U) == 0x39800000U && + (addis2 & 0xFFFF0000U) == 0x3D8C0000U && + mtctr == 0x7D8903A6U && + (li3 & 0xFFFF0000U) == 0x39800000U && + (addis3 & 0xFFFF0000U) == 0x3D8C0000U && + bctr == 0x4E800420U) + { + regs->gpr[PT_R11] = 3 * (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + regs->gpr[PT_R12] = (((li3 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + regs->gpr[PT_R12] += (addis3 & 0xFFFFU) << 16; + regs->ctr = (((li2 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + regs->ctr += (addis2 & 0xFFFFU) << 16; + regs->nip = regs->ctr; + return 4; + } + } + } while (0); + +#if 0 + do { /* PaX: unpatched PLT emulation #2 */ + unsigned int lis, lwzu, b, bctr; + + err = get_user(lis, (unsigned int *)regs->nip); + err |= get_user(lwzu, (unsigned int *)(regs->nip+4)); + err |= get_user(b, (unsigned int *)(regs->nip+8)); + err |= get_user(bctr, (unsigned int *)(regs->nip+12)); + + if (err) + break; + + if ((lis & 0xFFFF0000U) == 0x39600000U && + (lwzu & 0xU) == 0xU && + (b & 0xFC000003U) == 0x48000000U && + bctr == 0x4E800420U) + { + unsigned int addis, addi, rlwinm, add, li2, addis2, mtctr, li3, addis3, bctr; + unsigned long addr = b | 0xFC000000UL; + + addr = regs->nip + 12 + ((addr ^ 0x02000000UL) + 0x02000000UL); + err = get_user(addis, (unsigned int*)addr); + err |= get_user(addi, (unsigned int*)(addr+4)); + err |= get_user(rlwinm, (unsigned int*)(addr+8)); + err |= get_user(add, (unsigned int*)(addr+12)); + err |= get_user(li2, (unsigned int*)(addr+16)); + err |= get_user(addis2, (unsigned int*)(addr+20)); + err |= get_user(mtctr, (unsigned int*)(addr+24)); + err |= get_user(li3, (unsigned int*)(addr+28)); + err |= get_user(addis3, (unsigned int*)(addr+32)); + err |= get_user(bctr, (unsigned int*)(addr+36)); + + if (err) + break; + + if ((addis & 0xFFFF0000U) == 0x3D6B0000U && + (addi & 0xFFFF0000U) == 0x396B0000U && + rlwinm == 0x556C083CU && + add == 0x7D6C5A14U && + (li2 & 0xFFFF0000U) == 0x39800000U && + (addis2 & 0xFFFF0000U) == 0x3D8C0000U && + mtctr == 0x7D8903A6U && + (li3 & 0xFFFF0000U) == 0x39800000U && + (addis3 & 0xFFFF0000U) == 0x3D8C0000U && + bctr == 0x4E800420U) + { + regs->gpr[PT_R11] = + regs->gpr[PT_R11] = 3 * (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + regs->gpr[PT_R12] = (((li3 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + regs->gpr[PT_R12] += (addis3 & 0xFFFFU) << 16; + regs->ctr = (((li2 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + regs->ctr += (addis2 & 0xFFFFU) << 16; + regs->nip = regs->ctr; + return 4; + } + } + } while (0); +#endif + + do { /* PaX: unpatched PLT emulation #3 */ + unsigned int li, b; + + err = get_user(li, (unsigned int *)regs->nip); + err |= get_user(b, (unsigned int *)(regs->nip+4)); + + if (!err && (li & 0xFFFF0000U) == 0x39600000U && (b & 0xFC000003U) == 0x48000000U) { + unsigned int addis, lwz, mtctr, bctr; + unsigned long addr = b | 0xFC000000UL; + + addr = regs->nip + 4 + ((addr ^ 0x02000000UL) + 0x02000000UL); + err = get_user(addis, (unsigned int*)addr); + err |= get_user(lwz, (unsigned int*)(addr+4)); + err |= get_user(mtctr, (unsigned int*)(addr+8)); + err |= get_user(bctr, (unsigned int*)(addr+12)); + + if (err) + break; + + if ((addis & 0xFFFF0000U) == 0x3D6B0000U && + (lwz & 0xFFFF0000U) == 0x816B0000U && + mtctr == 0x7D6903A6U && + bctr == 0x4E800420U) + { + unsigned int r11; + + addr = (addis << 16) + (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + addr += (((lwz | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL); + + err = get_user(r11, (unsigned int*)addr); + if (err) + break; + + regs->gpr[PT_R11] = r11; + regs->ctr = r11; + regs->nip = r11; + return 4; + } + } + } while (0); +#endif + +#ifdef CONFIG_PAX_EMUSIGRT + do { /* PaX: sigreturn emulation */ + unsigned int li, sc; + + err = get_user(li, (unsigned int *)regs->nip); + err |= get_user(sc, (unsigned int *)(regs->nip+4)); + + if (!err && li == 0x38000000U + __NR_sigreturn && sc == 0x44000002U) { + struct vm_area_struct *vma; + unsigned long call_syscall; + + down_read(¤t->mm->mmap_sem); + call_syscall = current->mm->call_syscall; + up_read(¤t->mm->mmap_sem); + if (likely(call_syscall)) + goto emulate; + + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + + down_write(¤t->mm->mmap_sem); + if (current->mm->call_syscall) { + call_syscall = current->mm->call_syscall; + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + goto emulate; + } + + call_syscall = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE); + if (!vma || (call_syscall & ~PAGE_MASK)) { + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + return 1; + } + + pax_insert_vma(vma, call_syscall); + current->mm->call_syscall = call_syscall; + up_write(¤t->mm->mmap_sem); + +emulate: + regs->gpr[PT_R0] = __NR_sigreturn; + regs->nip = call_syscall; + return 6; + } + } while (0); + + do { /* PaX: rt_sigreturn emulation */ + unsigned int li, sc; + + err = get_user(li, (unsigned int *)regs->nip); + err |= get_user(sc, (unsigned int *)(regs->nip+4)); + + if (!err && li == 0x38000000U + __NR_rt_sigreturn && sc == 0x44000002U) { + struct vm_area_struct *vma; + unsigned int call_syscall; + + down_read(¤t->mm->mmap_sem); + call_syscall = current->mm->call_syscall; + up_read(¤t->mm->mmap_sem); + if (likely(call_syscall)) + goto rt_emulate; + + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + + down_write(¤t->mm->mmap_sem); + if (current->mm->call_syscall) { + call_syscall = current->mm->call_syscall; + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + goto rt_emulate; + } + + call_syscall = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE); + if (!vma || (call_syscall & ~PAGE_MASK)) { + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + return 1; + } + + pax_insert_vma(vma, call_syscall); + current->mm->call_syscall = call_syscall; + up_write(¤t->mm->mmap_sem); + +rt_emulate: + regs->gpr[PT_R0] = __NR_rt_sigreturn; + regs->nip = call_syscall; + return 7; + } + } while (0); +#endif + + return 1; +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 5; i++) { + unsigned int c; + if (get_user(c, (unsigned int*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif + /* * Check whether the instruction at regs->nip is a store using * an update addressing form which will update r1. @@ -116,7 +481,7 @@ * indicate errors in DSISR but can validly be set in SRR1. */ if (TRAP(regs) == 0x400) - error_code &= 0x48200000; + error_code &= 0x58200000; else is_write = error_code & 0x02000000; #endif /* CONFIG_4xx */ @@ -211,15 +576,14 @@ } else if (TRAP(regs) == 0x400) { pte_t *ptep; -#if 0 +#if 1 /* It would be nice to actually enforce the VM execute permission on CPUs which can do so, but far too much stuff in userspace doesn't get the permissions right, so we let any page be executed for now. */ if (! (vma->vm_flags & VM_EXEC)) goto bad_area; -#endif - +#else /* Since 4xx supports per-page execute permission, * we lazily flush dcache to icache. */ ptep = NULL; @@ -240,6 +604,7 @@ if (ptep != NULL) pte_unmap(ptep); #endif +#endif /* a read */ } else { /* protection fault */ @@ -285,6 +650,38 @@ /* User mode accesses cause a SIGSEGV */ if (user_mode(regs)) { + +#ifdef CONFIG_PAX_PAGEEXEC + if (current->flags & PF_PAX_PAGEEXEC) { + if ((TRAP(regs) == 0x400) && (regs->nip == address)) { + switch (pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_EMUPLT + case 2: + case 3: + case 4: + return; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + case 5: + return; +#endif + +#ifdef CONFIG_PAX_EMUSIGRT + case 6: + case 7: + return; +#endif + + } + + pax_report_fault(regs, (void*)regs->nip, (void*)regs->gpr[1]); + do_exit(SIGKILL); + } + } +#endif + info.si_signo = SIGSEGV; info.si_errno = 0; info.si_code = code; diff -urN linux-2.6.5/arch/sparc/kernel/ptrace.c linux-2.6.5/arch/sparc/kernel/ptrace.c --- linux-2.6.5/arch/sparc/kernel/ptrace.c 2004-04-03 22:38:24.000000000 -0500 +++ linux-2.6.5/arch/sparc/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -320,6 +321,11 @@ goto out; } + if (gr_handle_ptrace(child, request)) { + pt_error_return(regs, EPERM); + goto out_tsk; + } + if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { if (ptrace_attach(child)) { diff -urN linux-2.6.5/arch/sparc/kernel/sys_sparc.c linux-2.6.5/arch/sparc/kernel/sys_sparc.c --- linux-2.6.5/arch/sparc/kernel/sys_sparc.c 2004-04-03 22:36:56.000000000 -0500 +++ linux-2.6.5/arch/sparc/kernel/sys_sparc.c 2004-04-18 11:05:50.000000000 -0400 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,13 @@ return -ENOMEM; if (ARCH_SUN4C_SUN4 && len > 0x20000000) return -ENOMEM; + +#ifdef CONFIG_PAX_RANDMMAP + if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp)) + addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap; + else +#endif + if (!addr) addr = TASK_UNMAPPED_BASE; @@ -224,6 +232,11 @@ struct file * file = NULL; unsigned long retval = -EBADF; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); if (!file) @@ -242,6 +255,12 @@ if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE) goto out_putf; + if (gr_handle_mmap(file, prot)) { + fput(file); + retval = -EACCES; + goto out; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); down_write(¤t->mm->mmap_sem); diff -urN linux-2.6.5/arch/sparc/kernel/sys_sunos.c linux-2.6.5/arch/sparc/kernel/sys_sunos.c --- linux-2.6.5/arch/sparc/kernel/sys_sunos.c 2004-04-03 22:36:57.000000000 -0500 +++ linux-2.6.5/arch/sparc/kernel/sys_sunos.c 2004-04-18 11:05:50.000000000 -0400 @@ -71,6 +71,11 @@ struct file * file = NULL; unsigned long retval, ret_type; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + if(flags & MAP_NORESERVE) { static int cnt; if (cnt++ < 10) diff -urN linux-2.6.5/arch/sparc/mm/fault.c linux-2.6.5/arch/sparc/mm/fault.c --- linux-2.6.5/arch/sparc/mm/fault.c 2004-04-03 22:36:55.000000000 -0500 +++ linux-2.6.5/arch/sparc/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -21,6 +21,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -201,6 +205,272 @@ return 0; } +#ifdef CONFIG_PAX_PAGEEXEC +void pax_emuplt_close(struct vm_area_struct * vma) +{ + vma->vm_mm->call_dl_resolve = 0UL; +} + +static struct page* pax_emuplt_nopage(struct vm_area_struct *vma, unsigned long address, int *type) +{ + struct page* page; + unsigned int *kaddr; + + page = alloc_page(GFP_HIGHUSER); + if (!page) + return NOPAGE_OOM; + + kaddr = kmap(page); + memset(kaddr, 0, PAGE_SIZE); + kaddr[0] = 0x9DE3BFA8U; /* save */ + flush_dcache_page(page); + kunmap(page); + if (type) + *type = VM_FAULT_MAJOR; + + return page; +} + +static struct vm_operations_struct pax_vm_ops = { + close: pax_emuplt_close, + nopage: pax_emuplt_nopage, +}; + +static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr) +{ + vma->vm_mm = current->mm; + vma->vm_start = addr; + vma->vm_end = addr + PAGE_SIZE; + vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC; + vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f]; + vma->vm_ops = &pax_vm_ops; + vma->vm_pgoff = 0UL; + vma->vm_file = NULL; + vma->vm_private_data = NULL; + INIT_LIST_HEAD(&vma->shared); + insert_vm_struct(current->mm, vma); + ++current->mm->total_vm; +} + +/* + * PaX: decide what to do with offenders (regs->pc = fault address) + * + * returns 1 when task should be killed + * 2 when patched PLT trampoline was detected + * 3 when unpatched PLT trampoline was detected + * 4 when legitimate ET_EXEC was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#ifdef CONFIG_PAX_EMUPLT + int err; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + if (current->flags & PF_PAX_RANDEXEC) { + if (regs->pc >= current->mm->start_code && + regs->pc < current->mm->end_code) + { + if (regs->u_regs[UREG_RETPC] + 8UL == regs->pc) + return 1; + + regs->pc += current->mm->delta_exec; + if (regs->npc >= current->mm->start_code && + regs->npc < current->mm->end_code) + regs->npc += current->mm->delta_exec; + return 4; + } + if (regs->pc >= current->mm->start_code + current->mm->delta_exec && + regs->pc < current->mm->end_code + current->mm->delta_exec) + { + regs->pc -= current->mm->delta_exec; + if (regs->npc >= current->mm->start_code + current->mm->delta_exec && + regs->npc < current->mm->end_code + current->mm->delta_exec) + regs->npc -= current->mm->delta_exec; + } + } +#endif + +#ifdef CONFIG_PAX_EMUPLT + do { /* PaX: patched PLT emulation #1 */ + unsigned int sethi1, sethi2, jmpl; + + err = get_user(sethi1, (unsigned int*)regs->pc); + err |= get_user(sethi2, (unsigned int*)(regs->pc+4)); + err |= get_user(jmpl, (unsigned int*)(regs->pc+8)); + + if (err) + break; + + if ((sethi1 & 0xFFC00000U) == 0x03000000U && + (sethi2 & 0xFFC00000U) == 0x03000000U && + (jmpl & 0xFFFFE000U) == 0x81C06000U) + { + unsigned int addr; + + regs->u_regs[UREG_G1] = (sethi2 & 0x003FFFFFU) << 10; + addr = regs->u_regs[UREG_G1]; + addr += (((jmpl | 0xFFFFE000U) ^ 0x00001000U) + 0x00001000U); + regs->pc = addr; + regs->npc = addr+4; + return 2; + } + } while (0); + + { /* PaX: patched PLT emulation #2 */ + unsigned int ba; + + err = get_user(ba, (unsigned int*)regs->pc); + + if (!err && (ba & 0xFFC00000U) == 0x30800000U) { + unsigned int addr; + + addr = regs->pc + 4 + (((ba | 0xFFC00000U) ^ 0x00200000U) + 0x00200000U); + regs->pc = addr; + regs->npc = addr+4; + return 2; + } + } + + do { /* PaX: patched PLT emulation #3 */ + unsigned int sethi, jmpl, nop; + + err = get_user(sethi, (unsigned int*)regs->pc); + err |= get_user(jmpl, (unsigned int*)(regs->pc+4)); + err |= get_user(nop, (unsigned int*)(regs->pc+8)); + + if (err) + break; + + if ((sethi & 0xFFC00000U) == 0x03000000U && + (jmpl & 0xFFFFE000U) == 0x81C06000U && + nop == 0x01000000U) + { + unsigned int addr; + + addr = (sethi & 0x003FFFFFU) << 10; + regs->u_regs[UREG_G1] = addr; + addr += (((jmpl | 0xFFFFE000U) ^ 0x00001000U) + 0x00001000U); + regs->pc = addr; + regs->npc = addr+4; + return 2; + } + } while (0); + + do { /* PaX: unpatched PLT emulation step 1 */ + unsigned int sethi, ba, nop; + + err = get_user(sethi, (unsigned int*)regs->pc); + err |= get_user(ba, (unsigned int*)(regs->pc+4)); + err |= get_user(nop, (unsigned int*)(regs->pc+8)); + + if (err) + break; + + if ((sethi & 0xFFC00000U) == 0x03000000U && + ((ba & 0xFFC00000U) == 0x30800000U || (ba & 0xFFF80000U) == 0x30680000U) && + nop == 0x01000000U) + { + unsigned int addr, save, call; + + if ((ba & 0xFFC00000U) == 0x30800000U) + addr = regs->pc + 4 + ((((ba | 0xFFC00000U) ^ 0x00200000U) + 0x00200000U) << 2); + else + addr = regs->pc + 4 + ((((ba | 0xFFF80000U) ^ 0x00040000U) + 0x00040000U) << 2); + + err = get_user(save, (unsigned int*)addr); + err |= get_user(call, (unsigned int*)(addr+4)); + err |= get_user(nop, (unsigned int*)(addr+8)); + if (err) + break; + + if (save == 0x9DE3BFA8U && + (call & 0xC0000000U) == 0x40000000U && + nop == 0x01000000U) + { + struct vm_area_struct *vma; + unsigned long call_dl_resolve; + + down_read(¤t->mm->mmap_sem); + call_dl_resolve = current->mm->call_dl_resolve; + up_read(¤t->mm->mmap_sem); + if (likely(call_dl_resolve)) + goto emulate; + + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + + down_write(¤t->mm->mmap_sem); + if (current->mm->call_dl_resolve) { + call_dl_resolve = current->mm->call_dl_resolve; + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + goto emulate; + } + + call_dl_resolve = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE); + if (!vma || (call_dl_resolve & ~PAGE_MASK)) { + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + return 1; + } + + pax_insert_vma(vma, call_dl_resolve); + current->mm->call_dl_resolve = call_dl_resolve; + up_write(¤t->mm->mmap_sem); + +emulate: + regs->u_regs[UREG_G1] = (sethi & 0x003FFFFFU) << 10; + regs->pc = call_dl_resolve; + regs->npc = addr+4; + return 3; + } + } + } while (0); + + do { /* PaX: unpatched PLT emulation step 2 */ + unsigned int save, call, nop; + + err = get_user(save, (unsigned int*)(regs->pc-4)); + err |= get_user(call, (unsigned int*)regs->pc); + err |= get_user(nop, (unsigned int*)(regs->pc+4)); + if (err) + break; + + if (save == 0x9DE3BFA8U && + (call & 0xC0000000U) == 0x40000000U && + nop == 0x01000000U) + { + unsigned int dl_resolve = regs->pc + ((((call | 0xC0000000U) ^ 0x20000000U) + 0x20000000U) << 2); + + regs->u_regs[UREG_RETPC] = regs->pc; + regs->pc = dl_resolve; + regs->npc = dl_resolve+4; + return 3; + } + } while (0); +#endif + + return 1; +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 5; i++) { + unsigned int c; + if (get_user(c, (unsigned int*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif + asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, unsigned long address) { @@ -264,6 +534,29 @@ if(!(vma->vm_flags & VM_WRITE)) goto bad_area; } else { + +#ifdef CONFIG_PAX_PAGEEXEC + if ((current->flags & PF_PAX_PAGEEXEC) && text_fault && !(vma->vm_flags & VM_EXEC)) { + up_read(&mm->mmap_sem); + switch (pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_EMUPLT + case 2: + case 3: + return; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + case 4: + return; +#endif + + } + pax_report_fault(regs, (void*)regs->pc, (void*)regs->u_regs[UREG_FP]); + do_exit(SIGKILL); + } +#endif + /* Allow reads even for write-only mappings */ if(!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; diff -urN linux-2.6.5/arch/sparc/mm/init.c linux-2.6.5/arch/sparc/mm/init.c --- linux-2.6.5/arch/sparc/mm/init.c 2004-04-03 22:36:57.000000000 -0500 +++ linux-2.6.5/arch/sparc/mm/init.c 2004-04-18 11:05:50.000000000 -0400 @@ -337,17 +337,17 @@ /* Initialize the protection map with non-constant, MMU dependent values. */ protection_map[0] = PAGE_NONE; - protection_map[1] = PAGE_READONLY; - protection_map[2] = PAGE_COPY; - protection_map[3] = PAGE_COPY; + protection_map[1] = PAGE_READONLY_NOEXEC; + protection_map[2] = PAGE_COPY_NOEXEC; + protection_map[3] = PAGE_COPY_NOEXEC; protection_map[4] = PAGE_READONLY; protection_map[5] = PAGE_READONLY; protection_map[6] = PAGE_COPY; protection_map[7] = PAGE_COPY; protection_map[8] = PAGE_NONE; - protection_map[9] = PAGE_READONLY; - protection_map[10] = PAGE_SHARED; - protection_map[11] = PAGE_SHARED; + protection_map[9] = PAGE_READONLY_NOEXEC; + protection_map[10] = PAGE_SHARED_NOEXEC; + protection_map[11] = PAGE_SHARED_NOEXEC; protection_map[12] = PAGE_READONLY; protection_map[13] = PAGE_READONLY; protection_map[14] = PAGE_SHARED; diff -urN linux-2.6.5/arch/sparc/mm/srmmu.c linux-2.6.5/arch/sparc/mm/srmmu.c --- linux-2.6.5/arch/sparc/mm/srmmu.c 2004-04-03 22:37:23.000000000 -0500 +++ linux-2.6.5/arch/sparc/mm/srmmu.c 2004-04-18 11:05:50.000000000 -0400 @@ -2138,6 +2138,13 @@ BTFIXUPSET_INT(page_shared, pgprot_val(SRMMU_PAGE_SHARED)); BTFIXUPSET_INT(page_copy, pgprot_val(SRMMU_PAGE_COPY)); BTFIXUPSET_INT(page_readonly, pgprot_val(SRMMU_PAGE_RDONLY)); + +#ifdef CONFIG_PAX_PAGEEXEC + BTFIXUPSET_INT(page_shared_noexec, pgprot_val(SRMMU_PAGE_SHARED_NOEXEC)); + BTFIXUPSET_INT(page_copy_noexec, pgprot_val(SRMMU_PAGE_COPY_NOEXEC)); + BTFIXUPSET_INT(page_readonly_noexec, pgprot_val(SRMMU_PAGE_RDONLY_NOEXEC)); +#endif + BTFIXUPSET_INT(page_kernel, pgprot_val(SRMMU_PAGE_KERNEL)); page_kernel = pgprot_val(SRMMU_PAGE_KERNEL); pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF; diff -urN linux-2.6.5/arch/sparc64/kernel/itlb_base.S linux-2.6.5/arch/sparc64/kernel/itlb_base.S --- linux-2.6.5/arch/sparc64/kernel/itlb_base.S 2004-04-03 22:36:18.000000000 -0500 +++ linux-2.6.5/arch/sparc64/kernel/itlb_base.S 2004-04-18 11:05:50.000000000 -0400 @@ -41,7 +41,9 @@ CREATE_VPTE_OFFSET2(%g4, %g6) ! Create VPTE offset ldxa [%g3 + %g6] ASI_P, %g5 ! Load VPTE 1: brgez,pn %g5, 3f ! Not valid, branch out - nop ! Delay-slot + and %g5, _PAGE_EXEC, %g4 + brz,pn %g4, 3f ! Not executable, branch out + nop ! Delay-slot 2: stxa %g5, [%g0] ASI_ITLB_DATA_IN ! Load PTE into TLB retry ! Trap return 3: rdpr %pstate, %g4 ! Move into alternate globals @@ -74,8 +76,6 @@ nop nop nop - nop - nop CREATE_VPTE_NOP #undef CREATE_VPTE_OFFSET1 diff -urN linux-2.6.5/arch/sparc64/kernel/ptrace.c linux-2.6.5/arch/sparc64/kernel/ptrace.c --- linux-2.6.5/arch/sparc64/kernel/ptrace.c 2004-04-03 22:38:22.000000000 -0500 +++ linux-2.6.5/arch/sparc64/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -169,6 +170,11 @@ goto out; } + if (gr_handle_ptrace(child, (long)request)) { + pt_error_return(regs, EPERM); + goto out_tsk; + } + if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { if (ptrace_attach(child)) { diff -urN linux-2.6.5/arch/sparc64/kernel/sys_sparc.c linux-2.6.5/arch/sparc64/kernel/sys_sparc.c --- linux-2.6.5/arch/sparc64/kernel/sys_sparc.c 2004-04-03 22:37:23.000000000 -0500 +++ linux-2.6.5/arch/sparc64/kernel/sys_sparc.c 2004-04-18 11:05:50.000000000 -0400 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,10 @@ if (filp || (flags & MAP_SHARED)) do_color_align = 1; +#ifdef CONFIG_PAX_RANDMMAP + if (!(current->flags & PF_PAX_RANDMMAP) || !filp) +#endif + if (addr) { if (do_color_align) addr = COLOUR_ALIGN(addr, pgoff); @@ -101,6 +106,13 @@ } if (task_size < addr) { if (start_addr != TASK_UNMAPPED_BASE) { + +#ifdef CONFIG_PAX_RANDMMAP + if (current->flags & PF_PAX_RANDMMAP) + start_addr = addr = TASK_UNMAPPED_BASE + mm->delta_mmap; + else +#endif + start_addr = addr = TASK_UNMAPPED_BASE; goto full_search; } @@ -310,11 +322,22 @@ struct file * file = NULL; unsigned long retval = -EBADF; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); if (!file) goto out; } + + if (gr_handle_mmap(file, prot)) { + retval = -EACCES; + goto out_putf; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); len = PAGE_ALIGN(len); retval = -EINVAL; diff -urN linux-2.6.5/arch/sparc64/kernel/sys_sparc32.c linux-2.6.5/arch/sparc64/kernel/sys_sparc32.c --- linux-2.6.5/arch/sparc64/kernel/sys_sparc32.c 2004-04-03 22:37:36.000000000 -0500 +++ linux-2.6.5/arch/sparc64/kernel/sys_sparc32.c 2004-04-18 11:05:50.000000000 -0400 @@ -54,6 +54,8 @@ #include #include #include +#include +#include #include #include @@ -1759,6 +1761,11 @@ struct file * file; int retval; int i; +#ifdef CONFIG_GRKERNSEC + struct file *old_exec_file; + struct acl_subject_label *old_acl; + struct rlimit old_rlim[RLIM_NLIMITS]; +#endif sched_balance_exec(); @@ -1768,7 +1775,26 @@ if (IS_ERR(file)) return retval; + gr_learn_resource(current, RLIMIT_NPROC, atomic_read(¤t->user->processes), 1); + + if (gr_handle_nproc()) { + allow_write_access(file); + fput(file); + return -EAGAIN; + } + + if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) { + allow_write_access(file); + fput(file); + return -EACCES; + } + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + +#ifdef CONFIG_PAX_RANDUSTACK + bprm.p -= (pax_get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK; +#endif + memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0])); bprm.file = file; @@ -1803,11 +1829,24 @@ if (retval < 0) goto out; + if (!gr_tpe_allow(file)) { + retval = -EACCES; + goto out; + } + + if (gr_check_crash_exec(file)) { + retval = -EACCES; + goto out; + } + retval = copy_strings_kernel(1, &bprm.filename, &bprm); if (retval < 0) goto out; bprm.exec = bprm.p; + + gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt); + retval = copy_strings32(bprm.envc, envp, &bprm); if (retval < 0) goto out; @@ -1816,13 +1855,34 @@ if (retval < 0) goto out; +#ifdef CONFIG_GRKERNSEC + old_acl = current->acl; + memcpy(old_rlim, current->rlim, sizeof(old_rlim)); + old_exec_file = current->exec_file; + get_file(file); + current->exec_file = file; +#endif + + gr_set_proc_label(file->f_dentry, file->f_vfsmnt); + retval = search_binary_handler(&bprm, regs); if (retval >= 0) { /* execve success */ +#ifdef CONFIG_GRKERNSEC + if (old_exec_file) + fput(old_exec_file); +#endif security_bprm_free(&bprm); return retval; } +#ifdef CONFIG_GRKERNSEC + current->acl = old_acl; + memcpy(current->rlim, old_rlim, sizeof(old_rlim)); + fput(current->exec_file); + current->exec_file = old_exec_file; +#endif + out: /* Something went wrong, return the inode and free the argument pages*/ for (i = 0 ; i < MAX_ARG_PAGES ; i++) { diff -urN linux-2.6.5/arch/sparc64/kernel/sys_sunos32.c linux-2.6.5/arch/sparc64/kernel/sys_sunos32.c --- linux-2.6.5/arch/sparc64/kernel/sys_sunos32.c 2004-04-03 22:37:24.000000000 -0500 +++ linux-2.6.5/arch/sparc64/kernel/sys_sunos32.c 2004-04-18 11:05:50.000000000 -0400 @@ -75,6 +75,11 @@ struct file *file = NULL; unsigned long retval, ret_type; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + if (flags & MAP_NORESERVE) { static int cnt; if (cnt++ < 10) diff -urN linux-2.6.5/arch/sparc64/mm/fault.c linux-2.6.5/arch/sparc64/mm/fault.c --- linux-2.6.5/arch/sparc64/mm/fault.c 2004-04-03 22:38:20.000000000 -0500 +++ linux-2.6.5/arch/sparc64/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -18,6 +18,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -303,6 +307,364 @@ unhandled_fault (address, current, regs); } +#ifdef CONFIG_PAX_PAGEEXEC +#ifdef CONFIG_PAX_EMUPLT +static void pax_emuplt_close(struct vm_area_struct * vma) +{ + vma->vm_mm->call_dl_resolve = 0UL; +} + +static struct page* pax_emuplt_nopage(struct vm_area_struct *vma, unsigned long address, int *type) +{ + struct page* page; + unsigned int *kaddr; + + page = alloc_page(GFP_HIGHUSER); + if (!page) + return NOPAGE_OOM; + + kaddr = kmap(page); + memset(kaddr, 0, PAGE_SIZE); + kaddr[0] = 0x9DE3BFA8U; /* save */ + flush_dcache_page(page); + kunmap(page); + if (type) + *type = VM_FAULT_MAJOR; + return page; +} + +static struct vm_operations_struct pax_vm_ops = { + close: pax_emuplt_close, + nopage: pax_emuplt_nopage, +}; + +static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr) +{ + vma->vm_mm = current->mm; + vma->vm_start = addr; + vma->vm_end = addr + PAGE_SIZE; + vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC; + vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f]; + vma->vm_ops = &pax_vm_ops; + vma->vm_pgoff = 0UL; + vma->vm_file = NULL; + vma->vm_private_data = NULL; + INIT_LIST_HEAD(&vma->shared); + insert_vm_struct(current->mm, vma); + ++current->mm->total_vm; +} +#endif + +/* + * PaX: decide what to do with offenders (regs->tpc = fault address) + * + * returns 1 when task should be killed + * 2 when patched PLT trampoline was detected + * 3 when unpatched PLT trampoline was detected + * 4 when legitimate ET_EXEC was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#ifdef CONFIG_PAX_EMUPLT + int err; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + if (current->flags & PF_PAX_RANDEXEC) { + if (regs->tpc >= current->mm->start_code && + regs->tpc < current->mm->end_code) + { + if (regs->u_regs[UREG_RETPC] + 8UL == regs->tpc) + return 1; + + regs->tpc += current->mm->delta_exec; + if (regs->tnpc >= current->mm->start_code && + regs->tnpc < current->mm->end_code) + regs->tnpc += current->mm->delta_exec; + return 4; + } + if (regs->tpc >= current->mm->start_code + current->mm->delta_exec && + regs->tpc < current->mm->end_code + current->mm->delta_exec) + { + regs->tpc -= current->mm->delta_exec; + if (regs->tnpc >= current->mm->start_code + current->mm->delta_exec && + regs->tnpc < current->mm->end_code + current->mm->delta_exec) + regs->tnpc -= current->mm->delta_exec; + } + } +#endif + +#ifdef CONFIG_PAX_EMUPLT + do { /* PaX: patched PLT emulation #1 */ + unsigned int sethi1, sethi2, jmpl; + + err = get_user(sethi1, (unsigned int*)regs->tpc); + err |= get_user(sethi2, (unsigned int*)(regs->tpc+4)); + err |= get_user(jmpl, (unsigned int*)(regs->tpc+8)); + + if (err) + break; + + if ((sethi1 & 0xFFC00000U) == 0x03000000U && + (sethi2 & 0xFFC00000U) == 0x03000000U && + (jmpl & 0xFFFFE000U) == 0x81C06000U) + { + unsigned long addr; + + regs->u_regs[UREG_G1] = (sethi2 & 0x003FFFFFU) << 10; + addr = regs->u_regs[UREG_G1]; + addr += (((jmpl | 0xFFFFFFFFFFFFE000UL) ^ 0x00001000UL) + 0x00001000UL); + regs->tpc = addr; + regs->tnpc = addr+4; + return 2; + } + } while (0); + + { /* PaX: patched PLT emulation #2 */ + unsigned int ba; + + err = get_user(ba, (unsigned int*)regs->tpc); + + if (!err && (ba & 0xFFC00000U) == 0x30800000U) { + unsigned long addr; + + addr = regs->tpc + 4 + (((ba | 0xFFFFFFFFFFC00000UL) ^ 0x00200000UL) + 0x00200000UL); + regs->tpc = addr; + regs->tnpc = addr+4; + return 2; + } + } + + do { /* PaX: patched PLT emulation #3 */ + unsigned int sethi, jmpl, nop; + + err = get_user(sethi, (unsigned int*)regs->tpc); + err |= get_user(jmpl, (unsigned int*)(regs->tpc+4)); + err |= get_user(nop, (unsigned int*)(regs->tpc+8)); + + if (err) + break; + + if ((sethi & 0xFFC00000U) == 0x03000000U && + (jmpl & 0xFFFFE000U) == 0x81C06000U && + nop == 0x01000000U) + { + unsigned long addr; + + addr = (sethi & 0x003FFFFFU) << 10; + regs->u_regs[UREG_G1] = addr; + addr += (((jmpl | 0xFFFFFFFFFFFFE000UL) ^ 0x00001000UL) + 0x00001000UL); + regs->tpc = addr; + regs->tnpc = addr+4; + return 2; + } + } while (0); + + do { /* PaX: patched PLT emulation #4 */ + unsigned int mov1, call, mov2; + + err = get_user(mov1, (unsigned int*)regs->tpc); + err |= get_user(call, (unsigned int*)(regs->tpc+4)); + err |= get_user(mov2, (unsigned int*)(regs->tpc+8)); + + if (err) + break; + + if (mov1 == 0x8210000FU && + (call & 0xC0000000U) == 0x40000000U && + mov2 == 0x9E100001U) + { + unsigned long addr; + + regs->u_regs[UREG_G1] = regs->u_regs[UREG_RETPC]; + addr = regs->tpc + 4 + ((((call | 0xFFFFFFFFC0000000UL) ^ 0x20000000UL) + 0x20000000UL) << 2); + regs->tpc = addr; + regs->tnpc = addr+4; + return 2; + } + } while (0); + + do { /* PaX: patched PLT emulation #5 */ + unsigned int sethi1, sethi2, or1, or2, sllx, jmpl, nop; + + err = get_user(sethi1, (unsigned int*)regs->tpc); + err |= get_user(sethi2, (unsigned int*)(regs->tpc+4)); + err |= get_user(or1, (unsigned int*)(regs->tpc+8)); + err |= get_user(or2, (unsigned int*)(regs->tpc+12)); + err |= get_user(sllx, (unsigned int*)(regs->tpc+16)); + err |= get_user(jmpl, (unsigned int*)(regs->tpc+20)); + err |= get_user(nop, (unsigned int*)(regs->tpc+24)); + + if (err) + break; + + if ((sethi1 & 0xFFC00000U) == 0x03000000U && + (sethi2 & 0xFFC00000U) == 0x0B000000U && + (or1 & 0xFFFFE000U) == 0x82106000U && + (or2 & 0xFFFFE000U) == 0x8A116000U && + sllx == 0x83287020 && + jmpl == 0x81C04005U && + nop == 0x01000000U) + { + unsigned long addr; + + regs->u_regs[UREG_G1] = ((sethi1 & 0x003FFFFFU) << 10) | (or1 & 0x000003FFU); + regs->u_regs[UREG_G1] <<= 32; + regs->u_regs[UREG_G5] = ((sethi2 & 0x003FFFFFU) << 10) | (or2 & 0x000003FFU); + addr = regs->u_regs[UREG_G1] + regs->u_regs[UREG_G5]; + regs->tpc = addr; + regs->tnpc = addr+4; + return 2; + } + } while (0); + + do { /* PaX: patched PLT emulation #6 */ + unsigned int sethi1, sethi2, sllx, or, jmpl, nop; + + err = get_user(sethi1, (unsigned int*)regs->tpc); + err |= get_user(sethi2, (unsigned int*)(regs->tpc+4)); + err |= get_user(sllx, (unsigned int*)(regs->tpc+8)); + err |= get_user(or, (unsigned int*)(regs->tpc+12)); + err |= get_user(jmpl, (unsigned int*)(regs->tpc+16)); + err |= get_user(nop, (unsigned int*)(regs->tpc+20)); + + if (err) + break; + + if ((sethi1 & 0xFFC00000U) == 0x03000000U && + (sethi2 & 0xFFC00000U) == 0x0B000000U && + sllx == 0x83287020 && + (or & 0xFFFFE000U) == 0x8A116000U && + jmpl == 0x81C04005U && + nop == 0x01000000U) + { + unsigned long addr; + + regs->u_regs[UREG_G1] = (sethi1 & 0x003FFFFFU) << 10; + regs->u_regs[UREG_G1] <<= 32; + regs->u_regs[UREG_G5] = ((sethi2 & 0x003FFFFFU) << 10) | (or & 0x3FFU); + addr = regs->u_regs[UREG_G1] + regs->u_regs[UREG_G5]; + regs->tpc = addr; + regs->tnpc = addr+4; + return 2; + } + } while (0); + + do { /* PaX: unpatched PLT emulation step 1 */ + unsigned int sethi, ba, nop; + + err = get_user(sethi, (unsigned int*)regs->tpc); + err |= get_user(ba, (unsigned int*)(regs->tpc+4)); + err |= get_user(nop, (unsigned int*)(regs->tpc+8)); + + if (err) + break; + + if ((sethi & 0xFFC00000U) == 0x03000000U && + ((ba & 0xFFC00000U) == 0x30800000U || (ba & 0xFFF80000U) == 0x30680000U) && + nop == 0x01000000U) + { + unsigned long addr; + unsigned int save, call; + + if ((ba & 0xFFC00000U) == 0x30800000U) + addr = regs->tpc + 4 + ((((ba | 0xFFFFFFFFFFC00000UL) ^ 0x00200000UL) + 0x00200000UL) << 2); + else + addr = regs->tpc + 4 + ((((ba | 0xFFFFFFFFFFF80000UL) ^ 0x00040000UL) + 0x00040000UL) << 2); + + err = get_user(save, (unsigned int*)addr); + err |= get_user(call, (unsigned int*)(addr+4)); + err |= get_user(nop, (unsigned int*)(addr+8)); + if (err) + break; + + if (save == 0x9DE3BFA8U && + (call & 0xC0000000U) == 0x40000000U && + nop == 0x01000000U) + { + struct vm_area_struct *vma; + unsigned long call_dl_resolve; + + down_read(¤t->mm->mmap_sem); + call_dl_resolve = current->mm->call_dl_resolve; + up_read(¤t->mm->mmap_sem); + if (likely(call_dl_resolve)) + goto emulate; + + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + + down_write(¤t->mm->mmap_sem); + if (current->mm->call_dl_resolve) { + call_dl_resolve = current->mm->call_dl_resolve; + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + goto emulate; + } + + call_dl_resolve = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE); + if (!vma || (call_dl_resolve & ~PAGE_MASK)) { + up_write(¤t->mm->mmap_sem); + if (vma) kmem_cache_free(vm_area_cachep, vma); + return 1; + } + + pax_insert_vma(vma, call_dl_resolve); + current->mm->call_dl_resolve = call_dl_resolve; + up_write(¤t->mm->mmap_sem); + +emulate: + regs->u_regs[UREG_G1] = (sethi & 0x003FFFFFU) << 10; + regs->tpc = call_dl_resolve; + regs->tnpc = addr+4; + return 3; + } + } + } while (0); + + do { /* PaX: unpatched PLT emulation step 2 */ + unsigned int save, call, nop; + + err = get_user(save, (unsigned int*)(regs->tpc-4)); + err |= get_user(call, (unsigned int*)regs->tpc); + err |= get_user(nop, (unsigned int*)(regs->tpc+4)); + if (err) + break; + + if (save == 0x9DE3BFA8U && + (call & 0xC0000000U) == 0x40000000U && + nop == 0x01000000U) + { + unsigned long dl_resolve = regs->tpc + ((((call | 0xFFFFFFFFC0000000UL) ^ 0x20000000UL) + 0x20000000UL) << 2); + + regs->u_regs[UREG_RETPC] = regs->tpc; + regs->tpc = dl_resolve; + regs->tnpc = dl_resolve+4; + return 3; + } + } while (0); +#endif + + return 1; +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 5; i++) { + unsigned int c; + if (get_user(c, (unsigned int*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif + asmlinkage void do_sparc64_fault(struct pt_regs *regs) { struct mm_struct *mm = current->mm; @@ -340,8 +702,10 @@ goto intr_or_no_mm; if (test_thread_flag(TIF_32BIT)) { - if (!(regs->tstate & TSTATE_PRIV)) + if (!(regs->tstate & TSTATE_PRIV)) { regs->tpc &= 0xffffffff; + regs->tnpc &= 0xffffffff; + } address &= 0xffffffff; } @@ -350,6 +714,34 @@ if (!vma) goto bad_area; +#ifdef CONFIG_PAX_PAGEEXEC + /* PaX: detect ITLB misses on non-exec pages */ + if ((current->flags & PF_PAX_PAGEEXEC) && vma->vm_start <= address && + !(vma->vm_flags & VM_EXEC) && (fault_code & FAULT_CODE_ITLB)) + { + if (address != regs->tpc) + goto good_area; + + up_read(&mm->mmap_sem); + switch (pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_EMUPLT + case 2: + case 3: + goto fault_done; +#endif + +#ifdef CONFIG_PAX_RANDEXEC + case 4: + goto fault_done; +#endif + + } + pax_report_fault(regs, (void*)regs->tpc, (void*)(regs->u_regs[UREG_FP] + STACK_BIAS)); + do_exit(SIGKILL); + } +#endif + /* Pure DTLB misses do not tell us whether the fault causing * load/store/atomic was a write or not, it only says that there * was no match. So in such a case we (carefully) read the diff -urN linux-2.6.5/arch/sparc64/solaris/misc.c linux-2.6.5/arch/sparc64/solaris/misc.c --- linux-2.6.5/arch/sparc64/solaris/misc.c 2004-04-03 22:38:19.000000000 -0500 +++ linux-2.6.5/arch/sparc64/solaris/misc.c 2004-04-18 11:05:50.000000000 -0400 @@ -56,6 +56,11 @@ struct file *file = NULL; unsigned long retval, ret_type; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + /* Do we need it here? */ set_personality(PER_SVR4); if (flags & MAP_NORESERVE) { diff -urN linux-2.6.5/arch/x86_64/ia32/ia32_binfmt.c linux-2.6.5/arch/x86_64/ia32/ia32_binfmt.c --- linux-2.6.5/arch/x86_64/ia32/ia32_binfmt.c 2004-04-03 22:36:15.000000000 -0500 +++ linux-2.6.5/arch/x86_64/ia32/ia32_binfmt.c 2004-04-18 11:05:50.000000000 -0400 @@ -185,6 +185,17 @@ //#include #include +#ifdef CONFIG_PAX_ASLR +#define PAX_ELF_ET_DYN_BASE(tsk) (test_thread_flag(TIF_IA32) ? 0x08048000UL : 0x400000UL) + +#define PAX_DELTA_MMAP_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_MMAP_LEN(tsk) (test_thread_flag(TIF_IA32) ? 16 : 24) +#define PAX_DELTA_EXEC_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_EXEC_LEN(tsk) (test_thread_flag(TIF_IA32) ? 16 : 24) +#define PAX_DELTA_STACK_LSB(tsk) PAGE_SHIFT +#define PAX_DELTA_STACK_LEN(tsk) (test_thread_flag(TIF_IA32) ? 16 : 24) +#endif + typedef struct user_i387_ia32_struct elf_fpregset_t; typedef struct user32_fxsr_struct elf_fpxregset_t; @@ -354,7 +365,13 @@ mpnt->vm_mm = mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = IA32_STACK_TOP; + +#ifdef CONFIG_PAX_PAGEEXEC + mpnt->vm_flags = VM_STACK_FLAGS; +#else mpnt->vm_flags = vm_stack_flags32; +#endif + mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? PAGE_COPY_EXEC : PAGE_COPY; mpnt->vm_ops = NULL; diff -urN linux-2.6.5/arch/x86_64/ia32/sys_ia32.c linux-2.6.5/arch/x86_64/ia32/sys_ia32.c --- linux-2.6.5/arch/x86_64/ia32/sys_ia32.c 2004-04-03 22:36:57.000000000 -0500 +++ linux-2.6.5/arch/x86_64/ia32/sys_ia32.c 2004-04-18 11:05:50.000000000 -0400 @@ -1235,6 +1235,11 @@ unsigned long error; struct file * file = NULL; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + return -EINVAL; +#endif + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); diff -urN linux-2.6.5/arch/x86_64/kernel/ptrace.c linux-2.6.5/arch/x86_64/kernel/ptrace.c --- linux-2.6.5/arch/x86_64/kernel/ptrace.c 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/arch/x86_64/kernel/ptrace.c 2004-04-18 11:05:50.000000000 -0400 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -212,6 +213,9 @@ if (pid == 1) /* you may not mess with init */ goto out_tsk; + if (gr_handle_ptrace(child, request)) + goto out_tsk; + if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; diff -urN linux-2.6.5/arch/x86_64/kernel/setup64.c linux-2.6.5/arch/x86_64/kernel/setup64.c --- linux-2.6.5/arch/x86_64/kernel/setup64.c 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/arch/x86_64/kernel/setup64.c 2004-04-18 11:05:50.000000000 -0400 @@ -42,8 +42,15 @@ unsigned long __supported_pte_mask = ~0UL; static int do_not_nx __initdata = 0; + +#ifdef CONFIG_PAX_PAGEEXEC +unsigned long vm_stack_flags = VM_GROWSDOWN | __VM_DATA_DEFAULT_FLAGS; +unsigned long vm_stack_flags32 = VM_GROWSDOWN | __VM_DATA_DEFAULT_FLAGS; +#else unsigned long vm_stack_flags = __VM_STACK_FLAGS; unsigned long vm_stack_flags32 = __VM_STACK_FLAGS; +#endif + unsigned long vm_data_default_flags = __VM_DATA_DEFAULT_FLAGS; unsigned long vm_data_default_flags32 = __VM_DATA_DEFAULT_FLAGS; unsigned long vm_force_exec32 = PROT_EXEC; diff -urN linux-2.6.5/arch/x86_64/kernel/sys_x86_64.c linux-2.6.5/arch/x86_64/kernel/sys_x86_64.c --- linux-2.6.5/arch/x86_64/kernel/sys_x86_64.c 2004-04-03 22:37:36.000000000 -0500 +++ linux-2.6.5/arch/x86_64/kernel/sys_x86_64.c 2004-04-18 11:05:50.000000000 -0400 @@ -48,6 +48,11 @@ if (off & ~PAGE_MASK) goto out; +#ifdef CONFIG_PAX_RANDEXEC + if (flags & MAP_MIRROR) + goto out; +#endif + error = -EBADF; file = NULL; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); @@ -102,6 +107,15 @@ find_start_end(flags, &begin, &end); +#ifdef CONFIG_PAX_RANDMMAP + if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp)) { + if (begin == 0x40000000) + begin += current->mm->delta_mmap & 0x0FFFFFFFU; + else + begin += current->mm->delta_mmap; + } +#endif + if (len > end) return -ENOMEM; diff -urN linux-2.6.5/arch/x86_64/mm/fault.c linux-2.6.5/arch/x86_64/mm/fault.c --- linux-2.6.5/arch/x86_64/mm/fault.c 2004-04-03 22:36:14.000000000 -0500 +++ linux-2.6.5/arch/x86_64/mm/fault.c 2004-04-18 11:05:50.000000000 -0400 @@ -23,6 +23,7 @@ #include /* For unblank_screen() */ #include #include +#include #include #include @@ -217,6 +218,63 @@ (tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL); } +#ifdef CONFIG_PAX_PAGEEXEC +/* + * PaX: decide what to do with offenders (regs->rip = fault address) + * + * returns 1 when task should be killed + * 2 when legitimate ET_EXEC was detected + */ +static int pax_handle_fetch_fault(struct pt_regs *regs) +{ + +#ifdef CONFIG_PAX_RANDEXEC + int err; + + if (current->flags & PF_PAX_RANDEXEC) { + if (regs->rip >= current->mm->start_code && + regs->rip < current->mm->end_code) + { + if (test_thread_flag(TIF_IA32)) { + unsigned int esp_4; + + err = get_user(esp_4, (unsigned int*)(regs->rsp-4UL)); + if (err || esp_4 == regs->rip) + return 1; + } else { + unsigned long esp_8; + + err = get_user(esp_8, (unsigned long*)(regs->rsp-8UL)); + if (err || esp_8 == regs->rip) + return 1; + } + + regs->rip += current->mm->delta_exec; + return 2; + } + } +#endif + + return 1; +} + +void pax_report_insns(void *pc, void *sp) +{ + unsigned long i; + + printk(KERN_ERR "PAX: bytes at PC: "); + for (i = 0; i < 20; i++) { + unsigned int c; + if (get_user(c, (unsigned char*)pc+i)) { + printk("."); + break; + } + printk("%08x ", c); + } + printk("\n"); +} +#endif + int page_fault_trace; int exception_trace = 1; @@ -302,6 +360,23 @@ * we can handle it.. */ good_area: + +#ifdef CONFIG_PAX_PAGEEXEC + if ((current->flags & PF_PAX_PAGEEXEC) && (error_code & 16) && !(vma->vm_flags & VM_EXEC)) { + up_read(&mm->mmap_sem); + switch(pax_handle_fetch_fault(regs)) { + +#ifdef CONFIG_PAX_RANDEXEC + case 2: + return; +#endif + + } + pax_report_fault(regs, (void*)regs->rip, (void*)regs->rsp); + do_exit(SIGKILL); + } +#endif + info.si_code = SEGV_ACCERR; write = 0; switch (error_code & 3) { diff -urN linux-2.6.5/drivers/char/keyboard.c linux-2.6.5/drivers/char/keyboard.c --- linux-2.6.5/drivers/char/keyboard.c 2004-04-03 22:38:28.000000000 -0500 +++ linux-2.6.5/drivers/char/keyboard.c 2004-04-18 11:05:50.000000000 -0400 @@ -606,6 +606,16 @@ kbd->kbdmode == VC_MEDIUMRAW) && value != KVAL(K_SAK)) return; /* SAK is allowed even in raw mode */ + +#if defined(CONFIG_GRKERNSEC_PROC) || defined(CONFIG_GRKERNSEC_PROC_MEMMAP) + { + void *func = fn_handler[value]; + if (func == fn_show_state || func == fn_show_ptregs || + func == fn_show_mem) + return; + } +#endif + fn_handler[value](vc, regs); } diff -urN linux-2.6.5/drivers/char/mem.c linux-2.6.5/drivers/char/mem.c --- linux-2.6.5/drivers/char/mem.c 2004-04-03 22:37:07.000000000 -0500 +++ linux-2.6.5/drivers/char/mem.c 2004-04-18 11:05:50.000000000 -0400 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,10 @@ extern void tapechar_init(void); #endif +#ifdef CONFIG_GRKERNSEC +extern struct file_operations grsec_fops; +#endif + /* * Architectures vary in how they handle caching for addresses * outside of main memory. @@ -178,6 +183,12 @@ if (!valid_phys_addr_range(p, &count)) return -EFAULT; + +#ifdef CONFIG_GRKERNSEC_KMEM + gr_handle_mem_write(); + return -EPERM; +#endif + return do_write_mem(__va(p), p, buf, count, ppos); } @@ -192,6 +203,11 @@ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); #endif +#ifdef CONFIG_GRKERNSEC_KMEM + if (gr_handle_mem_mmap(offset, vma)) + return -EPERM; +#endif + /* Don't try to swap out physical pages.. */ vma->vm_flags |= VM_RESERVED; @@ -285,6 +301,11 @@ ssize_t written; char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ +#ifdef CONFIG_GRKERNSEC_KMEM + gr_handle_kmem_write(); + return -EPERM; +#endif + if (p < (unsigned long) high_memory) { wrote = count; @@ -411,7 +432,23 @@ count = size; zap_page_range(vma, addr, count); - zeromap_page_range(vma, addr, count, PAGE_COPY); + zeromap_page_range(vma, addr, count, vma->vm_page_prot); + +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (vma->vm_flags & VM_MIRROR) { + unsigned long addr_m; + struct vm_area_struct * vma_m; + + addr_m = vma->vm_start + (unsigned long)vma->vm_private_data; + vma_m = find_vma(mm, addr_m); + if (vma_m && vma_m->vm_start == addr_m && (vma_m->vm_flags & VM_MIRROR)) { + addr_m = addr + (unsigned long)vma->vm_private_data; + zap_page_range(vma_m, addr_m, count); + } else + printk(KERN_ERR "PAX: VMMIRROR: read_zero bug, %08lx, %08lx\n", + addr, vma->vm_start); + } +#endif size -= count; buf += count; @@ -560,6 +597,16 @@ static int open_port(struct inode * inode, struct file * filp) { +#ifdef CONFIG_GRKERNSEC_KMEM + gr_handle_open_port(); + return -EPERM; +#endif + + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;