
From: Greg Banks <gnb@melbourne.sgi.com>

Add check_user_page_readable() for kernel modules which need to follow user
space addresses but can't use get_user().

Signed-off-by: John Levon <levon@movementarian.org>
Signed-off-by: Greg Banks <gnb@melbourne.sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/include/linux/mm.h |    1 +
 25-akpm/mm/memory.c        |   20 ++++++++++++++++++--
 2 files changed, 19 insertions(+), 2 deletions(-)

diff -puN include/linux/mm.h~oprofile-add-check_user_page_readable include/linux/mm.h
--- 25/include/linux/mm.h~oprofile-add-check_user_page_readable	2004-12-03 20:56:07.705588760 -0800
+++ 25-akpm/include/linux/mm.h	2004-12-03 20:56:07.711587848 -0800
@@ -823,6 +823,7 @@ extern struct page * vmalloc_to_page(voi
 extern unsigned long vmalloc_to_pfn(void *addr);
 extern struct page * follow_page(struct mm_struct *mm, unsigned long address,
 		int write);
+extern int check_user_page_readable(struct mm_struct *mm, unsigned long address);
 int remap_pfn_range(struct vm_area_struct *, unsigned long,
 		unsigned long, unsigned long, pgprot_t);
 
diff -puN mm/memory.c~oprofile-add-check_user_page_readable mm/memory.c
--- 25/mm/memory.c~oprofile-add-check_user_page_readable	2004-12-03 20:56:07.707588456 -0800
+++ 25-akpm/mm/memory.c	2004-12-03 20:56:07.713587544 -0800
@@ -747,8 +747,8 @@ void zap_page_range(struct vm_area_struc
  * Do a quick page-table lookup for a single page.
  * mm->page_table_lock must be held.
  */
-struct page *
-follow_page(struct mm_struct *mm, unsigned long address, int write) 
+static struct page *
+__follow_page(struct mm_struct *mm, unsigned long address, int read, int write)
 {
 	pml4_t *pml4;
 	pgd_t *pgd;
@@ -791,6 +791,8 @@ follow_page(struct mm_struct *mm, unsign
 	if (pte_present(pte)) {
 		if (write && !pte_write(pte))
 			goto out;
+		if (read && !pte_read(pte))
+			goto out;
 		pfn = pte_pfn(pte);
 		if (pfn_valid(pfn)) {
 			page = pfn_to_page(pfn);
@@ -805,6 +807,20 @@ out:
 	return NULL;
 }
 
+struct page *
+follow_page(struct mm_struct *mm, unsigned long address, int write)
+{
+	return __follow_page(mm, address, /*read*/0, write);
+}
+
+int
+check_user_page_readable(struct mm_struct *mm, unsigned long address)
+{
+	return __follow_page(mm, address, /*read*/1, /*write*/0) != NULL;
+}
+
+EXPORT_SYMBOL(check_user_page_readable);
+
 /* 
  * Given a physical address, is there a useful struct page pointing to
  * it?  This may become more complex in the future if we start dealing
_
