static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, unsigned long start, unsigned long end, unsigned int newflags) { struct mm_struct * mm = vma->vm_mm; int pages; int ret; #ifdef CONFIG_PAX_SEGMEXEC struct vm_area_struct * vma_m = NULL, *prev_m; unsigned long start_m = 0UL, end_m = 0UL, newflags_m = 0UL; if (vma->vm_flags & VM_MIRROR) { start_m = vma->vm_start + vma->vm_mirror; vma_m = find_vma_prev(mm, start_m, &prev_m); if (!vma_m || vma_m->vm_start != start_m || !(vma_m->vm_flags & VM_MIRROR)) { printk("PAX: VMMIRROR: mlock bug in %s, %08lx\n", current->comm, vma->vm_start); return -ENOMEM; } start_m = start + vma->vm_mirror; end_m = end + vma->vm_mirror; if (newflags & VM_LOCKED) newflags_m = vma_m->vm_flags | VM_LOCKED; else newflags_m = vma_m->vm_flags & ~VM_LOCKED; ret = __mlock_fixup(vma_m, &prev_m, start_m, end_m, newflags_m); if (ret) return ret; } #endif ret = __mlock_fixup(vma, prev, start, end, newflags); if (ret) return ret; /* * vm_flags is protected by the mmap_sem held in write mode. * It's okay if try_to_unmap_one unmaps a page just after we * set VM_LOCKED, make_pages_present below will bring it back. */ vma->vm_flags = newflags; #ifdef CONFIG_PAX_SEGMEXEC if (vma->vm_flags & VM_MIRROR) vma_m->vm_flags = newflags_m; #endif /* * Keep track of amount of locked VM. */ pages = (end - start) >> PAGE_SHIFT; if (newflags & VM_LOCKED) { pages = -pages; if (!(newflags & VM_IO)) ret = make_pages_present(start, end); } mm->locked_vm -= pages; #ifdef CONFIG_PAX_SEGMEXEC if (vma->vm_flags & VM_MIRROR) mm->locked_vm -= pages; #endif if (ret == -ENOMEM) ret = -EAGAIN; return ret; } static int __mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, unsigned long start, unsigned long end, unsigned int newflags) { struct mm_struct * mm = vma->vm_mm; pgoff_t pgoff; int ret = 0; if (newflags == vma->vm_flags) { *prev = vma; goto out; } pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); if (*prev) { vma = *prev; goto out; } *prev = vma; if (start != vma->vm_start) { ret = split_vma(mm, vma, start, 1); if (ret) goto out; } if (end != vma->vm_end) ret = split_vma(mm, vma, end, 0); out: if (ret == -ENOMEM) ret = -EAGAIN; return ret; }