Commit 98bc93e5 authored by KOSAKI Motohiro's avatar KOSAKI Motohiro Committed by Linus Torvalds

proc: fix pagemap_read() error case

Currently, pagemap_read() has three error and/or corner case handling
mistake.

 (1) If ppos parameter is wrong, mm refcount will be leak.
 (2) If count parameter is 0, mm refcount will be leak too.
 (3) If the current task is sleeping in kmalloc() and the system
     is out of memory and oom-killer kill the proc associated task,
     mm_refcount prevent the task free its memory. then system may
     hang up.

<Quote Hugh's explain why we shold call kmalloc() before get_mm()>

  check_mem_permission gets a reference to the mm.  If we
  __get_free_page after check_mem_permission, imagine what happens if the
  system is out of memory, and the mm we're looking at is selected for
  killing by the OOM killer: while we wait in __get_free_page for more
  memory, no memory is freed from the selected mm because it cannot reach
  exit_mmap while we hold that reference.

This patch fixes the above three.
Signed-off-by: default avatarKOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Jovi Zhang <bookjovi@gmail.com>
Acked-by: default avatarHugh Dickins <hughd@google.com>
Cc: Stephen Wilson <wilsons@start.ca>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 30cd8903
...@@ -771,18 +771,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, ...@@ -771,18 +771,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
if (!task) if (!task)
goto out; goto out;
mm = mm_for_maps(task);
ret = PTR_ERR(mm);
if (!mm || IS_ERR(mm))
goto out_task;
ret = -EINVAL; ret = -EINVAL;
/* file position must be aligned */ /* file position must be aligned */
if ((*ppos % PM_ENTRY_BYTES) || (count % PM_ENTRY_BYTES)) if ((*ppos % PM_ENTRY_BYTES) || (count % PM_ENTRY_BYTES))
goto out_task; goto out_task;
ret = 0; ret = 0;
if (!count) if (!count)
goto out_task; goto out_task;
...@@ -790,7 +784,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, ...@@ -790,7 +784,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
pm.buffer = kmalloc(pm.len, GFP_TEMPORARY); pm.buffer = kmalloc(pm.len, GFP_TEMPORARY);
ret = -ENOMEM; ret = -ENOMEM;
if (!pm.buffer) if (!pm.buffer)
goto out_mm; goto out_task;
mm = mm_for_maps(task);
ret = PTR_ERR(mm);
if (!mm || IS_ERR(mm))
goto out_free;
pagemap_walk.pmd_entry = pagemap_pte_range; pagemap_walk.pmd_entry = pagemap_pte_range;
pagemap_walk.pte_hole = pagemap_pte_hole; pagemap_walk.pte_hole = pagemap_pte_hole;
...@@ -833,7 +832,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, ...@@ -833,7 +832,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
len = min(count, PM_ENTRY_BYTES * pm.pos); len = min(count, PM_ENTRY_BYTES * pm.pos);
if (copy_to_user(buf, pm.buffer, len)) { if (copy_to_user(buf, pm.buffer, len)) {
ret = -EFAULT; ret = -EFAULT;
goto out_free; goto out_mm;
} }
copied += len; copied += len;
buf += len; buf += len;
...@@ -843,10 +842,10 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, ...@@ -843,10 +842,10 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
if (!ret || ret == PM_END_OF_BUFFER) if (!ret || ret == PM_END_OF_BUFFER)
ret = copied; ret = copied;
out_free:
kfree(pm.buffer);
out_mm: out_mm:
mmput(mm); mmput(mm);
out_free:
kfree(pm.buffer);
out_task: out_task:
put_task_struct(task); put_task_struct(task);
out: out:
......
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