]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fix msync error on unmapped area
authorHugh Dickins <hugh@veritas.com>
Thu, 4 Jan 2007 20:22:14 +0000 (20:22 +0000)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 23 Feb 2007 23:49:52 +0000 (15:49 -0800)
Fix the 2.6.18 sys_msync to report -ENOMEM correctly when an unmapped area
falls within its range, and not to overshoot: to satisfy LSB 3.1 tests and
to fix Debian Bug#394392.  Took the 2.6.19 sys_msync as starting point
(including its cleanup of repeated "current->mm"s), reintroducing the
msync_interval and balance_dirty_pages_ratelimited_nr needed in 2.6.18.

The misbehaviour fixed here may not seem very serious; but it was enough
to mislead Debian into backporting 2.6.19's dirty page tracking patches,
with attendant mayhem when those resulted in unsuspected file corruption.

Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
mm/msync.c

index d083544df21bdf7a7f2229d5846a02ba22e1218f..48497dd4e94585493d279a62bb22aebbccb76546 100644 (file)
@@ -146,10 +146,10 @@ static int msync_interval(struct vm_area_struct *vma, unsigned long addr,
 asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 {
        unsigned long end;
+       struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
        int unmapped_error = 0;
        int error = -EINVAL;
-       int done = 0;
 
        if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
                goto out;
@@ -169,64 +169,58 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
         * If the interval [start,end) covers some unmapped address ranges,
         * just ignore them, but return -ENOMEM at the end.
         */
-       down_read(&current->mm->mmap_sem);
-       vma = find_vma(current->mm, start);
-       if (!vma) {
-               error = -ENOMEM;
-               goto out_unlock;
-       }
-       do {
+       down_read(&mm->mmap_sem);
+       vma = find_vma(mm, start);
+       for (;;) {
                unsigned long nr_pages_dirtied = 0;
                struct file *file;
 
+               /* Still start < end. */
+               error = -ENOMEM;
+               if (!vma)
+                       goto out_unlock;
                /* Here start < vma->vm_end. */
                if (start < vma->vm_start) {
-                       unmapped_error = -ENOMEM;
                        start = vma->vm_start;
-               }
-               /* Here vma->vm_start <= start < vma->vm_end. */
-               if (end <= vma->vm_end) {
-                       if (start < end) {
-                               error = msync_interval(vma, start, end, flags,
-                                                       &nr_pages_dirtied);
-                               if (error)
-                                       goto out_unlock;
-                       }
-                       error = unmapped_error;
-                       done = 1;
-               } else {
-                       /* Here vma->vm_start <= start < vma->vm_end < end. */
-                       error = msync_interval(vma, start, vma->vm_end, flags,
-                                               &nr_pages_dirtied);
-                       if (error)
+                       if (start >= end)
                                goto out_unlock;
+                       unmapped_error = -ENOMEM;
                }
+               /* Here vma->vm_start <= start < vma->vm_end. */
+               error = msync_interval(vma, start, min(end, vma->vm_end),
+                                               flags, &nr_pages_dirtied);
+               if (error)
+                       goto out_unlock;
                file = vma->vm_file;
                start = vma->vm_end;
                if ((flags & MS_ASYNC) && file && nr_pages_dirtied) {
                        get_file(file);
-                       up_read(&current->mm->mmap_sem);
+                       up_read(&mm->mmap_sem);
                        balance_dirty_pages_ratelimited_nr(file->f_mapping,
                                                        nr_pages_dirtied);
                        fput(file);
-                       down_read(&current->mm->mmap_sem);
-                       vma = find_vma(current->mm, start);
+                       if (start >= end)
+                               goto out;
+                       down_read(&mm->mmap_sem);
+                       vma = find_vma(mm, start);
                } else if ((flags & MS_SYNC) && file &&
                                (vma->vm_flags & VM_SHARED)) {
                        get_file(file);
-                       up_read(&current->mm->mmap_sem);
+                       up_read(&mm->mmap_sem);
                        error = do_fsync(file, 0);
                        fput(file);
-                       down_read(&current->mm->mmap_sem);
-                       if (error)
-                               goto out_unlock;
-                       vma = find_vma(current->mm, start);
+                       if (error || start >= end)
+                               goto out;
+                       down_read(&mm->mmap_sem);
+                       vma = find_vma(mm, start);
                } else {
+                       if (start >= end)
+                               goto out_unlock;
                        vma = vma->vm_next;
                }
-       } while (vma && !done);
+       }
 out_unlock:
-       up_read(&current->mm->mmap_sem);
+       up_read(&mm->mmap_sem);
 out:
-       return error;
+       return error ? : unmapped_error;
 }