diff options
| -rw-r--r-- | include/linux/mm.h | 1 | ||||
| -rw-r--r-- | mm/memory.c | 20 |
2 files changed, 19 insertions, 2 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index edc8c7d56185..93302390a475 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -802,6 +802,7 @@ extern struct page * vmalloc_to_page(void *addr); 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 --git a/mm/memory.c b/mm/memory.c index 58ba5fe66604..b0c61dac23e6 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -747,8 +747,8 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long address, * 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) { pgd_t *pgd; pud_t *pud; @@ -784,6 +784,8 @@ follow_page(struct mm_struct *mm, unsigned long address, int write) 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); @@ -798,6 +800,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 |
