diff -urN linux-2.4.32-oc+pax/fs/exec.c linux-2.4.32-oc+pax-fixed/fs/exec.c --- linux-2.4.32-oc+pax/fs/exec.c 2006-01-16 16:28:38.000000000 +0100 +++ linux-2.4.32-oc+pax-fixed/fs/exec.c 2006-01-16 16:26:33.000000000 +0100 @@ -381,23 +381,69 @@ return -ENOMEM; } +#ifdef CONFIG_PAX_SEGMEXEC + if ((current->mm->pax_flags & MF_PAX_SEGMEXEC) && (VM_STACK_FLAGS & VM_MAYEXEC)) { + mpnt_m = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!mpnt_m) { + kmem_cache_free(vm_area_cachep, mpnt); + return -ENOMEM; + } + } +#endif + down_write(¤t->mm->mmap_sem); { mpnt->vm_mm = current->mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = STACK_TOP; mpnt->vm_flags = VM_STACK_FLAGS; + +#ifdef CONFIG_PAX_PAGEEXEC + if (!(current->mm->pax_flags & MF_PAX_PAGEEXEC)) + mpnt->vm_page_prot = protection_map[(VM_STACK_FLAGS | VM_EXEC) & 0x7]; + else +#endif + mpnt->vm_page_prot = protection_map[VM_STACK_FLAGS & 0x7]; mpnt->vm_ops = NULL; mpnt->vm_pgoff = 0; mpnt->vm_file = NULL; mpnt->vm_private_data = (void *) 0; + mpnt->vm_mirror = 0; if ((ret = insert_vm_struct(current->mm, mpnt))) { up_write(¤t->mm->mmap_sem); kmem_cache_free(vm_area_cachep, mpnt); + +#ifdef CONFIG_PAX_SEGMEXEC + if (mpnt_m) + kmem_cache_free(vm_area_cachep, mpnt_m); +#endif return ret; } current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + +#ifdef CONFIG_PAX_SEGMEXEC + if (mpnt_m) { + *mpnt_m = *mpnt; + if (!(VM_STACK_FLAGS & VM_EXEC)) { + mpnt_m->vm_flags &= ~(VM_READ | VM_WRITE | VM_EXEC); + mpnt_m->vm_page_prot = PAGE_NONE; + } + mpnt_m->vm_start += SEGMEXEC_TASK_SIZE; + mpnt_m->vm_end += SEGMEXEC_TASK_SIZE; + if ((ret = insert_vm_struct(current->mm, mpnt_m))) { + up_write(¤t->mm->mmap_sem); + kmem_cache_free(vm_area_cachep, mpnt_m); + return ret; + } + mpnt_m->vm_flags |= VM_MIRROR; + mpnt->vm_flags |= VM_MIRROR; + mpnt_m->vm_mirror = mpnt->vm_start - mpnt_m->vm_start; + mpnt->vm_mirror = mpnt_m->vm_start - mpnt->vm_start; + current->mm->total_vm += (mpnt_m->vm_end - mpnt_m->vm_start) >> PAGE_SHIFT; + } +#endif + } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { diff -urN linux-2.4.32-oc+pax/include/asm-x86_64/page.h linux-2.4.32-oc+pax-fixed/include/asm-x86_64/page.h --- linux-2.4.32-oc+pax/include/asm-x86_64/page.h 2006-01-16 16:28:38.000000000 +0100 +++ linux-2.4.32-oc+pax-fixed/include/asm-x86_64/page.h 2006-01-16 16:26:33.000000000 +0100 @@ -158,6 +158,7 @@ #define VM_DATA_DEFAULT_FLAGS \ ((current->thread.flags & THREAD_IA32) ? vm_data_default_flags32 : \ vm_data_default_flags) +#endif #endif /* __KERNEL__ */ diff -urN linux-2.4.32-oc+pax/include/linux/mm.h linux-2.4.32-oc+pax-fixed/include/linux/mm.h --- linux-2.4.32-oc+pax/include/linux/mm.h 2006-01-16 16:28:38.000000000 +0100 +++ linux-2.4.32-oc+pax-fixed/include/linux/mm.h 2006-01-16 16:26:33.000000000 +0100 @@ -107,7 +107,23 @@ #define VM_DONTEXPAND 0x00040000 /* Cannot expand with mremap() */ #define VM_RESERVED 0x00080000 /* Don't unmap it from swap_out */ -#define VM_ACCOUNT 0x00100000 /* Memory is a vm accounted object */ +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) +#define VM_MIRROR 0x00100000 /* vma is mirroring another */ +#endif + +#ifdef CONFIG_PAX_MPROTECT +#define VM_MAYNOTWRITE 0x00200000 /* vma cannot be granted VM_WRITE any more */ +#endif + +#ifdef __VM_STACK_FLAGS +#ifdef ARCH_STACK_GROWSUP +#define VM_STACK_FLAGS (0x00000233 | __VM_STACK_FLAGS) +#else +#define VM_STACK_FLAGS (0x00000133 | __VM_STACK_FLAGS) +#endif +#endif + +#define VM_ACCOUNT 0x00400000 /* Memory is a vm accounted object */ #ifdef ARCH_STACK_GROWSUP #define VM_STACK_FLAGS (VM_DATA_DEFAULT_FLAGS|VM_GROWSUP|VM_ACCOUNT) @@ -658,14 +674,14 @@ return gfp_mask; } -/* Do stack extension */ -extern int expand_stack(struct vm_area_struct * vma, unsigned long address); - /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr); extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr, struct vm_area_struct **pprev); +/* Do stack extension */ +extern int expand_stack(struct vm_area_struct * vma, unsigned long address); + /* Look up the first VMA which intersects the interval start_addr..end_addr-1, NULL if none. Assume start_addr < end_addr. */ static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr) diff -urN linux-2.4.32-oc+pax/mm/mmap.c linux-2.4.32-oc+pax-fixed/mm/mmap.c --- linux-2.4.32-oc+pax/mm/mmap.c 2006-01-16 16:28:38.000000000 +0100 +++ linux-2.4.32-oc+pax-fixed/mm/mmap.c 2006-01-16 16:26:33.000000000 +0100 @@ -563,6 +563,30 @@ */ vm_flags = calc_vm_flags(prot,flags) | mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; + if (file && (file->f_vfsmnt->mnt_flags & MNT_NOEXEC)) + vm_flags &= ~VM_MAYEXEC; + +#if defined(CONFIG_PAX_PAGEEXEC) || defined(CONFIG_PAX_SEGMEXEC) + if (mm->pax_flags & (MF_PAX_PAGEEXEC | MF_PAX_SEGMEXEC)) { + +#ifdef CONFIG_PAX_MPROTECT + if (mm->pax_flags & MF_PAX_MPROTECT) { + if ((prot & (PROT_WRITE | PROT_EXEC)) != PROT_EXEC) + vm_flags &= ~(VM_EXEC | VM_MAYEXEC); + else + vm_flags &= ~(VM_WRITE | VM_MAYWRITE); + +#ifdef CONFIG_PAX_RANDEXEC + if (file && (flags & MAP_MIRROR) && (vm_flags & VM_EXEC)) + vma_m->vm_flags &= ~VM_MAYWRITE; +#endif + + } +#endif + + } +#endif + /* mlock MCL_FUTURE? */ if (vm_flags & VM_LOCKED) { unsigned long locked = mm->locked_vm << PAGE_SHIFT; @@ -946,6 +970,11 @@ vm_validate_enough("entering expand_stack"); +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + struct vm_area_struct * vma_m = NULL; + unsigned long address_m = 0UL; +#endif + /* * vma->vm_start/vm_end cannot change under us because the caller * is required to hold the mmap_sem in read mode. We need the @@ -970,27 +999,54 @@ return -ENOMEM; } - if (address - vma->vm_start > current->rlim[RLIMIT_STACK].rlim_cur || - ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur) { - spin_unlock(&vma->vm_mm->page_table_lock); - vm_unacct_memory(grow); - vm_validate_enough("exiting expand_stack - FAIL"); - return -ENOMEM; - } +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (vma->vm_flags & VM_MIRROR) { + address_m = vma->vm_start + vma->vm_mirror; + vma_m = find_vma(vma->vm_mm, address_m); + if (!vma_m || vma_m->vm_start != address_m || !(vma_m->vm_flags & VM_MIRROR) || + vma->vm_end - vma->vm_start != vma_m->vm_end - vma_m->vm_start) { + printk(KERN_ERR "PAX: VMMIRROR: expand bug, %08lx, %08lx, %08lx, %08lx, %08lx\n", + address, vma->vm_start, vma_m->vm_start, vma->vm_end, vma_m->vm_end); + spin_unlock(&vma->vm_mm->page_table_lock); + return -ENOMEM; + } - if ((vma->vm_flags & VM_LOCKED) && - ((vma->vm_mm->locked_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_MEMLOCK].rlim_cur) { + address_m = address + vma->vm_mirror; + if (2*grow < grow || vma_m->vm_end - address_m > current->rlim[RLIMIT_STACK].rlim_cur || + ((vma_m->vm_mm->total_vm + 2*grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur || + ((vma_m->vm_flags & VM_LOCKED) && + ((vma_m->vm_mm->locked_vm + 2*grow) << PAGE_SHIFT) > current->rlim[RLIMIT_MEMLOCK].rlim_cur)) { + spin_unlock(&vma->vm_mm->page_table_lock); + return -ENOMEM; + } + } else +#endif + + if (address - vma->vm_start > current->rlim[RLIMIT_STACK].rlim_cur || + ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur || + ((vma->vm_flags & VM_LOCKED) && + ((vma->vm_mm->locked_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_MEMLOCK].rlim_cur)) { spin_unlock(&vma->vm_mm->page_table_lock); vm_unacct_memory(grow); vm_validate_enough("exiting expand_stack - FAIL"); return -ENOMEM; } - vma->vm_end = address; vma->vm_mm->total_vm += grow; if (vma->vm_flags & VM_LOCKED) vma->vm_mm->locked_vm += grow; + +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (vma->vm_flags & VM_MIRROR) { + vma_m->vm_start = address_m; + vma_m->vm_pgoff -= grow; + vma_m->vm_mm->total_vm += grow; + if (vma_m->vm_flags & VM_LOCKED) + vma_m->vm_mm->locked_vm += grow; + } +#endif + spin_unlock(&vma->vm_mm->page_table_lock); vm_validate_enough("exiting expand_stack"); return 0; @@ -1006,6 +1062,11 @@ vm_validate_enough("entering expand_stack"); +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + struct vm_area_struct * vma_m = NULL; + unsigned long address_m = 0UL; +#endif + /* * vma->vm_start/vm_end cannot change under us because the caller * is required to hold the mmap_sem in read mode. We need the @@ -1028,28 +1089,55 @@ return -ENOMEM; } - if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur || - ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur) { - spin_unlock(&vma->vm_mm->page_table_lock); - vm_unacct_memory(grow); - vm_validate_enough("exiting expand_stack - FAIL"); - return -ENOMEM; - } - if ((vma->vm_flags & VM_LOCKED) && - ((vma->vm_mm->locked_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_MEMLOCK].rlim_cur) { +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (vma->vm_flags & VM_MIRROR) { + address_m = vma->vm_start + vma->vm_mirror; + vma_m = find_vma(vma->vm_mm, address_m); + if (!vma_m || vma_m->vm_start != address_m || !(vma_m->vm_flags & VM_MIRROR) || + vma->vm_end - vma->vm_start != vma_m->vm_end - vma_m->vm_start) { + printk(KERN_ERR "PAX: VMMIRROR: expand bug, %08lx, %08lx, %08lx, %08lx, %08lx\n", + address, vma->vm_start, vma_m->vm_start, vma->vm_end, vma_m->vm_end); + spin_unlock(&vma->vm_mm->page_table_lock); + return -ENOMEM; + } + + address_m = address + vma->vm_mirror; + if (2*grow < grow || vma_m->vm_end - address_m > current->rlim[RLIMIT_STACK].rlim_cur || + ((vma_m->vm_mm->total_vm + 2*grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur || + ((vma_m->vm_flags & VM_LOCKED) && + ((vma_m->vm_mm->locked_vm + 2*grow) << PAGE_SHIFT) > current->rlim[RLIMIT_MEMLOCK].rlim_cur)) { + spin_unlock(&vma->vm_mm->page_table_lock); + return -ENOMEM; + } + } else +#endif + if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur || + ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur || + ((vma->vm_flags & VM_LOCKED) && + ((vma->vm_mm->locked_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_MEMLOCK].rlim_cur)) { spin_unlock(&vma->vm_mm->page_table_lock); vm_unacct_memory(grow); vm_validate_enough("exiting expand_stack - FAIL"); return -ENOMEM; } - vma->vm_start = address; vma->vm_pgoff -= grow; vma->vm_mm->total_vm += grow; if (vma->vm_flags & VM_LOCKED) vma->vm_mm->locked_vm += grow; + +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (vma->vm_flags & VM_MIRROR) { + vma_m->vm_start = address_m; + vma_m->vm_pgoff -= grow; + vma_m->vm_mm->total_vm += grow; + if (vma_m->vm_flags & VM_LOCKED) + vma_m->vm_mm->locked_vm += grow; + } +#endif + spin_unlock(&vma->vm_mm->page_table_lock); vm_validate_enough("exiting expand_stack"); return 0; @@ -1369,60 +1457,69 @@ if (!extra) return -ENOMEM; +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (mm->pax_flags & (MF_PAX_SEGMEXEC | MF_PAX_RANDEXEC)) { + extra_m = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!extra_m) { + kmem_cache_free(vm_area_cachep, extra); + return -ENOMEM; + } + } else + extra_m = NULL; + + free_m = NULL; +#endif + npp = (prev ? &prev->vm_next : &mm->mmap); free = NULL; spin_lock(&mm->page_table_lock); for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) { + mm->map_count--; *npp = mpnt->vm_next; mpnt->vm_next = free; free = mpnt; rb_erase(&mpnt->vm_rb, &mm->mm_rb); + +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (free->vm_flags & VM_MIRROR) { + struct vm_area_struct *mpnt_m, *prev_m, **npp_m; + unsigned long addr_m = free->vm_start + free->vm_mirror; + + mm->mmap_cache = NULL; /* Kill the cache. */ + mpnt_m = find_vma_prev(mm, addr_m, &prev_m); + if (mpnt_m && mpnt_m->vm_start == addr_m && (mpnt_m->vm_flags & VM_MIRROR)) { + mm->map_count--; + npp_m = (prev_m ? &prev_m->vm_next : &mm->mmap); + *npp_m = mpnt_m->vm_next; + mpnt_m->vm_next = free_m; + free_m = mpnt_m; + rb_erase(&mpnt_m->vm_rb, &mm->mm_rb); + } else + printk("PAX: VMMIRROR: munmap bug in %s, %08lx\n", current->comm, free->vm_start); + } +#endif + } mm->mmap_cache = NULL; /* Kill the cache. */ spin_unlock(&mm->page_table_lock); - /* Ok - we have the memory areas we should free on the 'free' list, - * so release them, and unmap the page range.. - * If the one of the segments is only being partially unmapped, - * it will put new vm_area_struct(s) into the address space. - * In that case we have to be careful with VM_DENYWRITE. - */ - while ((mpnt = free) != NULL) { - unsigned long st, end, size; - struct file *file = NULL; - - free = free->vm_next; - - st = addr < mpnt->vm_start ? mpnt->vm_start : addr; - end = addr+len; - end = end > mpnt->vm_end ? mpnt->vm_end : end; - size = end - st; - - if (mpnt->vm_flags & VM_DENYWRITE && - (st != mpnt->vm_start || end != mpnt->vm_end) && - (file = mpnt->vm_file) != NULL) { - atomic_dec(&file->f_dentry->d_inode->i_writecount); - } - - remove_shared_vm_struct(mpnt); - mm->map_count--; + extra = unmap_vma_list(mm, addr, len, free, extra, prev); - zap_page_range(mm, st, size); +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + extra_m = unmap_vma_mirror_list(mm, addr, len, free_m, extra_m); +#endif - /* - * Fix the mapping, and free the old area if it wasn't reused. - */ - extra = unmap_fixup(mm, mpnt, st, size, extra); - if (file) - atomic_inc(&file->f_dentry->d_inode->i_writecount); - } validate_mm(mm); - /* Release the extra vma struct if it wasn't used */ - if (extra) - kmem_cache_free(vm_area_cachep, extra); +#if defined(CONFIG_PAX_SEGMEXEC) || defined(CONFIG_PAX_RANDEXEC) + if (extra_m) + kmem_cache_free(vm_area_cachep, extra_m); +#endif + + /* Release the extra vma struct if it wasn't used */ + if (extra) + kmem_cache_free(vm_area_cachep, extra); - free_pgtables(mm, prev, addr, addr+len); vm_validate_enough("exit -ok- do_munmap"); return 0; @@ -1459,11 +1556,34 @@ * anonymous maps. eventually we may be able to do some * brk-specific accounting here. */ +#ifdef CONFIG_PAX_SEGMEXEC +unsigned long __do_brk(unsigned long addr, unsigned long len); + +unsigned long do_brk(unsigned long addr, unsigned long len) +{ + unsigned long ret; + + ret = __do_brk(addr, len); + if (ret == addr && (current->mm->pax_flags & (MF_PAX_SEGMEXEC | MF_PAX_MPROTECT)) == MF_PAX_SEGMEXEC) { + unsigned long ret_m; + + ret_m = __do_mmap_pgoff(NULL, addr + SEGMEXEC_TASK_SIZE, 0UL, PROT_NONE, MAP_PRIVATE | MAP_FIXED | MAP_MIRROR, addr); + if (ret_m > TASK_SIZE) { + do_munmap(current->mm, addr, len); + ret = ret_m; + } + } + + return ret; +} + +unsigned long __do_brk(unsigned long addr, unsigned long len) +#else unsigned long do_brk(unsigned long addr, unsigned long len) { struct mm_struct * mm = current->mm; struct vm_area_struct * vma, * prev; - unsigned long flags; + unsigned long flags, task_size = TASK_SIZE; rb_node_t ** rb_link, * rb_parent; vm_validate_enough("entering do_brk"); @@ -1473,7 +1593,12 @@ if (!len) return addr; - if ((addr + len) > TASK_SIZE || (addr + len) < addr) +#ifdef CONFIG_PAX_SEGMEXEC + if (mm->pax_flags & MF_PAX_SEGMEXEC) + task_size = SEGMEXEC_TASK_SIZE; +#endif + + if ((addr + len) > task_size || (addr + len) < addr) return -EINVAL; /* diff -urN linux-2.4.32-oc+pax/mm/mremap.c linux-2.4.32-oc+pax-fixed/mm/mremap.c --- linux-2.4.32-oc+pax/mm/mremap.c 2006-01-16 16:28:38.000000000 +0100 +++ linux-2.4.32-oc+pax-fixed/mm/mremap.c 2006-01-16 16:26:33.000000000 +0100 @@ -270,7 +270,7 @@ unsigned long flags, unsigned long new_addr) { struct vm_area_struct *vma; - unsigned long ret = -EINVAL; + unsigned long ret = -EINVAL, task_size = TASK_SIZE; unsigned long charged = 0; if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))