From: Linus Torvalds (rediffed to apply cleanly) diff -urN 2.4.18rc2/mm/memory.c ptrace/mm/memory.c --- 2.4.18rc2/mm/memory.c Wed Feb 20 02:32:26 2002 +++ ptrace/mm/memory.c Wed Feb 20 19:05:54 2002 @@ -177,7 +177,7 @@ pgd_t * src_pgd, * dst_pgd; unsigned long address = vma->vm_start; unsigned long end = vma->vm_end; - unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE; + unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; src_pgd = pgd_offset(src, address)-1; dst_pgd = pgd_offset(dst, address)-1; @@ -245,7 +245,7 @@ goto cont_copy_pte_range; /* If it's a COW mapping, write protect it both in the parent and the child */ - if (cow) { + if (cow && pte_write(pte)) { ptep_set_wrprotect(src_pte); pte = *src_pte; } @@ -452,21 +452,24 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas) { - int i = 0; + int i; + unsigned int flags; + + /* + * Require read or write permissions. + * If 'force' is set, we only require the "MAY" flags. + */ + flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD); + flags &= force ? (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE); + i = 0; do { struct vm_area_struct * vma; vma = find_extend_vma(mm, start); - if ( !vma || - (pages && vma->vm_flags & VM_IO) || - (!force && - ((write && (!(vma->vm_flags & VM_WRITE))) || - (!write && (!(vma->vm_flags & VM_READ))) ) )) { - if (i) return i; - return -EFAULT; - } + if ( !vma || (pages && vma->vm_flags & VM_IO) || !(flags & vma->vm_flags) ) + return i ? : -EFAULT; spin_lock(&mm->page_table_lock); do {