From: Linus Torvalds <[email-masked]>

(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 {