Fix pagemap_read() use of struct mm_walk
Fix some issues in pagemap_read noted by Alexey: - initialize pagemap_walk.mm to "mm" , so the code starts working as advertised - initialize ->private to "&pm" so it wouldn't immediately oops in pagemap_pte_hole() - unstatic struct pagemap_walk, so two threads won't fsckup each other (including those started by root, including flipping ->mm when you don't have permissions) - pagemap_read() contains two calls to ptrace_may_attach(), second one looks unneeded. - avoid possible kmalloc(0) and integer wraparound. Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Matt Mackall <mpm@selenic.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> [ Personally, I'd just remove the functionality entirely - Linus ] Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
ca31e146d5
commit
5d7e0d2bd9
|
@ -602,11 +602,6 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct mm_walk pagemap_walk = {
|
||||
.pmd_entry = pagemap_pte_range,
|
||||
.pte_hole = pagemap_pte_hole
|
||||
};
|
||||
|
||||
/*
|
||||
* /proc/pid/pagemap - an array mapping virtual pages to pfns
|
||||
*
|
||||
|
@ -641,6 +636,11 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
|
|||
struct pagemapread pm;
|
||||
int pagecount;
|
||||
int ret = -ESRCH;
|
||||
struct mm_walk pagemap_walk;
|
||||
unsigned long src;
|
||||
unsigned long svpfn;
|
||||
unsigned long start_vaddr;
|
||||
unsigned long end_vaddr;
|
||||
|
||||
if (!task)
|
||||
goto out;
|
||||
|
@ -659,11 +659,15 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
|
|||
if (!mm)
|
||||
goto out_task;
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
uaddr = (unsigned long)buf & PAGE_MASK;
|
||||
uend = (unsigned long)(buf + count);
|
||||
pagecount = (PAGE_ALIGN(uend) - uaddr) / PAGE_SIZE;
|
||||
pages = kmalloc(pagecount * sizeof(struct page *), GFP_KERNEL);
|
||||
ret = 0;
|
||||
if (pagecount == 0)
|
||||
goto out_mm;
|
||||
pages = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL);
|
||||
ret = -ENOMEM;
|
||||
if (!pages)
|
||||
goto out_mm;
|
||||
|
||||
|
@ -684,13 +688,15 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
|
|||
pm.out = (u64 *)buf;
|
||||
pm.end = (u64 *)(buf + count);
|
||||
|
||||
if (!ptrace_may_attach(task)) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
unsigned long src = *ppos;
|
||||
unsigned long svpfn = src / PM_ENTRY_BYTES;
|
||||
unsigned long start_vaddr = svpfn << PAGE_SHIFT;
|
||||
unsigned long end_vaddr = TASK_SIZE_OF(task);
|
||||
pagemap_walk.pmd_entry = pagemap_pte_range;
|
||||
pagemap_walk.pte_hole = pagemap_pte_hole;
|
||||
pagemap_walk.mm = mm;
|
||||
pagemap_walk.private = ±
|
||||
|
||||
src = *ppos;
|
||||
svpfn = src / PM_ENTRY_BYTES;
|
||||
start_vaddr = svpfn << PAGE_SHIFT;
|
||||
end_vaddr = TASK_SIZE_OF(task);
|
||||
|
||||
/* watch out for wraparound */
|
||||
if (svpfn > TASK_SIZE_OF(task) >> PAGE_SHIFT)
|
||||
|
@ -702,15 +708,13 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
|
|||
* user buffer is tracked in "pm", and the walk
|
||||
* will stop when we hit the end of the buffer.
|
||||
*/
|
||||
ret = walk_page_range(start_vaddr, end_vaddr,
|
||||
&pagemap_walk);
|
||||
ret = walk_page_range(start_vaddr, end_vaddr, &pagemap_walk);
|
||||
if (ret == PM_END_OF_BUFFER)
|
||||
ret = 0;
|
||||
/* don't need mmap_sem for these, but this looks cleaner */
|
||||
*ppos += (char *)pm.out - buf;
|
||||
if (!ret)
|
||||
ret = (char *)pm.out - buf;
|
||||
}
|
||||
|
||||
out_pages:
|
||||
for (; pagecount; pagecount--) {
|
||||
|
|
Loading…
Reference in New Issue