Skip to content

Commit 5d7e0d2

Browse files
akpm00torvalds
authored andcommitted
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 <[email protected]> Cc: Matt Mackall <[email protected]> Signed-off-by: Andrew Morton <[email protected]> [ Personally, I'd just remove the functionality entirely - Linus ] Signed-off-by: Linus Torvalds <[email protected]>
1 parent ca31e14 commit 5d7e0d2

File tree

1 file changed

+38
-34
lines changed

1 file changed

+38
-34
lines changed

fs/proc/task_mmu.c

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -602,11 +602,6 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
602602
return err;
603603
}
604604

605-
static struct mm_walk pagemap_walk = {
606-
.pmd_entry = pagemap_pte_range,
607-
.pte_hole = pagemap_pte_hole
608-
};
609-
610605
/*
611606
* /proc/pid/pagemap - an array mapping virtual pages to pfns
612607
*
@@ -641,6 +636,11 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
641636
struct pagemapread pm;
642637
int pagecount;
643638
int ret = -ESRCH;
639+
struct mm_walk pagemap_walk;
640+
unsigned long src;
641+
unsigned long svpfn;
642+
unsigned long start_vaddr;
643+
unsigned long end_vaddr;
644644

645645
if (!task)
646646
goto out;
@@ -659,11 +659,15 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
659659
if (!mm)
660660
goto out_task;
661661

662-
ret = -ENOMEM;
662+
663663
uaddr = (unsigned long)buf & PAGE_MASK;
664664
uend = (unsigned long)(buf + count);
665665
pagecount = (PAGE_ALIGN(uend) - uaddr) / PAGE_SIZE;
666-
pages = kmalloc(pagecount * sizeof(struct page *), GFP_KERNEL);
666+
ret = 0;
667+
if (pagecount == 0)
668+
goto out_mm;
669+
pages = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL);
670+
ret = -ENOMEM;
667671
if (!pages)
668672
goto out_mm;
669673

@@ -684,33 +688,33 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
684688
pm.out = (u64 *)buf;
685689
pm.end = (u64 *)(buf + count);
686690

687-
if (!ptrace_may_attach(task)) {
688-
ret = -EIO;
689-
} else {
690-
unsigned long src = *ppos;
691-
unsigned long svpfn = src / PM_ENTRY_BYTES;
692-
unsigned long start_vaddr = svpfn << PAGE_SHIFT;
693-
unsigned long end_vaddr = TASK_SIZE_OF(task);
694-
695-
/* watch out for wraparound */
696-
if (svpfn > TASK_SIZE_OF(task) >> PAGE_SHIFT)
697-
start_vaddr = end_vaddr;
698-
699-
/*
700-
* The odds are that this will stop walking way
701-
* before end_vaddr, because the length of the
702-
* user buffer is tracked in "pm", and the walk
703-
* will stop when we hit the end of the buffer.
704-
*/
705-
ret = walk_page_range(start_vaddr, end_vaddr,
706-
&pagemap_walk);
707-
if (ret == PM_END_OF_BUFFER)
708-
ret = 0;
709-
/* don't need mmap_sem for these, but this looks cleaner */
710-
*ppos += (char *)pm.out - buf;
711-
if (!ret)
712-
ret = (char *)pm.out - buf;
713-
}
691+
pagemap_walk.pmd_entry = pagemap_pte_range;
692+
pagemap_walk.pte_hole = pagemap_pte_hole;
693+
pagemap_walk.mm = mm;
694+
pagemap_walk.private = &pm;
695+
696+
src = *ppos;
697+
svpfn = src / PM_ENTRY_BYTES;
698+
start_vaddr = svpfn << PAGE_SHIFT;
699+
end_vaddr = TASK_SIZE_OF(task);
700+
701+
/* watch out for wraparound */
702+
if (svpfn > TASK_SIZE_OF(task) >> PAGE_SHIFT)
703+
start_vaddr = end_vaddr;
704+
705+
/*
706+
* The odds are that this will stop walking way
707+
* before end_vaddr, because the length of the
708+
* user buffer is tracked in "pm", and the walk
709+
* will stop when we hit the end of the buffer.
710+
*/
711+
ret = walk_page_range(start_vaddr, end_vaddr, &pagemap_walk);
712+
if (ret == PM_END_OF_BUFFER)
713+
ret = 0;
714+
/* don't need mmap_sem for these, but this looks cleaner */
715+
*ppos += (char *)pm.out - buf;
716+
if (!ret)
717+
ret = (char *)pm.out - buf;
714718

715719
out_pages:
716720
for (; pagecount; pagecount--) {

0 commit comments

Comments
 (0)