From: Hugh Dickins Date: Thu, 4 Jan 2007 20:22:14 +0000 (+0000) Subject: fix msync error on unmapped area X-Git-Tag: v2.6.18.8~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=700019f9fea4d78100e0b6032db3a66040620d42;p=thirdparty%2Fkernel%2Fstable.git fix msync error on unmapped area 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 Signed-off-by: Chris Wright Signed-off-by: Greg Kroah-Hartman --- diff --git a/mm/msync.c b/mm/msync.c index d083544df21bd..48497dd4e9458 100644 --- a/mm/msync.c +++ b/mm/msync.c @@ -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(¤t->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(¤t->mm->mmap_sem); + up_read(&mm->mmap_sem); balance_dirty_pages_ratelimited_nr(file->f_mapping, nr_pages_dirtied); fput(file); - down_read(¤t->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(¤t->mm->mmap_sem); + up_read(&mm->mmap_sem); error = do_fsync(file, 0); fput(file); - down_read(¤t->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(¤t->mm->mmap_sem); + up_read(&mm->mmap_sem); out: - return error; + return error ? : unmapped_error; }