Commit 700019f9 authored by Hugh Dickins's avatar Hugh Dickins Committed by Greg Kroah-Hartman

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: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarChris Wright <chrisw@sous-sol.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent dbee2bf2
...@@ -146,10 +146,10 @@ static int msync_interval(struct vm_area_struct *vma, unsigned long addr, ...@@ -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) asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
{ {
unsigned long end; unsigned long end;
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma; struct vm_area_struct *vma;
int unmapped_error = 0; int unmapped_error = 0;
int error = -EINVAL; int error = -EINVAL;
int done = 0;
if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC)) if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
goto out; goto out;
...@@ -169,64 +169,58 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags) ...@@ -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, * If the interval [start,end) covers some unmapped address ranges,
* just ignore them, but return -ENOMEM at the end. * just ignore them, but return -ENOMEM at the end.
*/ */
down_read(&current->mm->mmap_sem); down_read(&mm->mmap_sem);
vma = find_vma(current->mm, start); vma = find_vma(mm, start);
if (!vma) { for (;;) {
error = -ENOMEM;
goto out_unlock;
}
do {
unsigned long nr_pages_dirtied = 0; unsigned long nr_pages_dirtied = 0;
struct file *file; struct file *file;
/* Still start < end. */
error = -ENOMEM;
if (!vma)
goto out_unlock;
/* Here start < vma->vm_end. */ /* Here start < vma->vm_end. */
if (start < vma->vm_start) { if (start < vma->vm_start) {
unmapped_error = -ENOMEM;
start = vma->vm_start; start = vma->vm_start;
} if (start >= end)
/* 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)
goto out_unlock; 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; file = vma->vm_file;
start = vma->vm_end; start = vma->vm_end;
if ((flags & MS_ASYNC) && file && nr_pages_dirtied) { if ((flags & MS_ASYNC) && file && nr_pages_dirtied) {
get_file(file); get_file(file);
up_read(&current->mm->mmap_sem); up_read(&mm->mmap_sem);
balance_dirty_pages_ratelimited_nr(file->f_mapping, balance_dirty_pages_ratelimited_nr(file->f_mapping,
nr_pages_dirtied); nr_pages_dirtied);
fput(file); fput(file);
down_read(&current->mm->mmap_sem); if (start >= end)
vma = find_vma(current->mm, start); goto out;
down_read(&mm->mmap_sem);
vma = find_vma(mm, start);
} else if ((flags & MS_SYNC) && file && } else if ((flags & MS_SYNC) && file &&
(vma->vm_flags & VM_SHARED)) { (vma->vm_flags & VM_SHARED)) {
get_file(file); get_file(file);
up_read(&current->mm->mmap_sem); up_read(&mm->mmap_sem);
error = do_fsync(file, 0); error = do_fsync(file, 0);
fput(file); fput(file);
down_read(&current->mm->mmap_sem); if (error || start >= end)
if (error) goto out;
goto out_unlock; down_read(&mm->mmap_sem);
vma = find_vma(current->mm, start); vma = find_vma(mm, start);
} else { } else {
if (start >= end)
goto out_unlock;
vma = vma->vm_next; vma = vma->vm_next;
} }
} while (vma && !done); }
out_unlock: out_unlock:
up_read(&current->mm->mmap_sem); up_read(&mm->mmap_sem);
out: out:
return error; return error ? : unmapped_error;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment