Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 80bba129 authored by Brice Goglin's avatar Brice Goglin Committed by Linus Torvalds
Browse files

mm: no get_user/put_user while holding mmap_sem in do_pages_stat?



Since commit 2f007e74, do_pages_stat()
gets the page address from user-space and puts the corresponding status
back while holding the mmap_sem for read.  There is no need to hold
mmap_sem there while some page-faults may occur.

This patch adds a temporary address and status buffer so as to only
hold mmap_sem while working on these kernel buffers.  This is
implemented by extracting do_pages_stat_array() out of do_pages_stat().

Signed-off-by: default avatarBrice Goglin <Brice.Goglin@inria.fr>
Cc: Christoph Lameter <clameter@sgi.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: Hugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 52b9582d
Loading
Loading
Loading
Loading
+46 −13
Original line number Diff line number Diff line
@@ -987,25 +987,18 @@ out:
/*
 * Determine the nodes of an array of pages and store it in an array of status.
 */
static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages,
			 const void __user * __user *pages,
			 int __user *status)
static void do_pages_stat_array(struct mm_struct *mm, unsigned long nr_pages,
				const void __user **pages, int *status)
{
	unsigned long i;
	int err;

	down_read(&mm->mmap_sem);

	for (i = 0; i < nr_pages; i++) {
		const void __user *p;
		unsigned long addr;
		unsigned long addr = (unsigned long)(*pages);
		struct vm_area_struct *vma;
		struct page *page;

		err = -EFAULT;
		if (get_user(p, pages+i))
			goto out;
		addr = (unsigned long) p;
		int err;

		vma = find_vma(mm, addr);
		if (!vma)
@@ -1024,12 +1017,52 @@ static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages,

		err = page_to_nid(page);
set_status:
		put_user(err, status+i);
		*status = err;

		pages++;
		status++;
	}

	up_read(&mm->mmap_sem);
}

/*
 * Determine the nodes of a user array of pages and store it in
 * a user array of status.
 */
static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages,
			 const void __user * __user *pages,
			 int __user *status)
{
#define DO_PAGES_STAT_CHUNK_NR 16
	const void __user *chunk_pages[DO_PAGES_STAT_CHUNK_NR];
	int chunk_status[DO_PAGES_STAT_CHUNK_NR];
	unsigned long i, chunk_nr = DO_PAGES_STAT_CHUNK_NR;
	int err;

	for (i = 0; i < nr_pages; i += chunk_nr) {
		if (chunk_nr + i > nr_pages)
			chunk_nr = nr_pages - i;

		err = copy_from_user(chunk_pages, &pages[i],
				     chunk_nr * sizeof(*chunk_pages));
		if (err) {
			err = -EFAULT;
			goto out;
		}

		do_pages_stat_array(mm, chunk_nr, chunk_pages, chunk_status);

		err = copy_to_user(&status[i], chunk_status,
				   chunk_nr * sizeof(*chunk_status));
		if (err) {
			err = -EFAULT;
			goto out;
		}
	}
	err = 0;

out:
	up_read(&mm->mmap_sem);
	return err;
}