aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/oprofile/backtrace.c46
-rw-r--r--arch/i386/oprofile/backtrace.c38
-rw-r--r--include/linux/mm.h1
-rw-r--r--mm/memory.c29
4 files changed, 26 insertions, 88 deletions
diff --git a/arch/arm/oprofile/backtrace.c b/arch/arm/oprofile/backtrace.c
index df35c452a8bf..7c22c12618cc 100644
--- a/arch/arm/oprofile/backtrace.c
+++ b/arch/arm/oprofile/backtrace.c
@@ -49,42 +49,22 @@ static struct frame_tail* kernel_backtrace(struct frame_tail *tail)
49 49
50static struct frame_tail* user_backtrace(struct frame_tail *tail) 50static struct frame_tail* user_backtrace(struct frame_tail *tail)
51{ 51{
52 struct frame_tail buftail; 52 struct frame_tail buftail[2];
53 53
54 /* hardware pte might not be valid due to dirty/accessed bit emulation 54 /* Also check accessibility of one struct frame_tail beyond */
55 * so we use copy_from_user and benefit from exception fixups */ 55 if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
56 if (copy_from_user(&buftail, tail, sizeof(struct frame_tail))) 56 return NULL;
57 if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail)))
57 return NULL; 58 return NULL;
58 59
59 oprofile_add_trace(buftail.lr); 60 oprofile_add_trace(buftail[0].lr);
60 61
61 /* frame pointers should strictly progress back up the stack 62 /* frame pointers should strictly progress back up the stack
62 * (towards higher addresses) */ 63 * (towards higher addresses) */
63 if (tail >= buftail.fp) 64 if (tail >= buftail[0].fp)
64 return NULL; 65 return NULL;
65 66
66 return buftail.fp-1; 67 return buftail[0].fp-1;
67}
68
69/* Compare two addresses and see if they're on the same page */
70#define CMP_ADDR_EQUAL(x,y,offset) ((((unsigned long) x) >> PAGE_SHIFT) \
71 == ((((unsigned long) y) + offset) >> PAGE_SHIFT))
72
73/* check that the page(s) containing the frame tail are present */
74static int pages_present(struct frame_tail *tail)
75{
76 struct mm_struct * mm = current->mm;
77
78 if (!check_user_page_readable(mm, (unsigned long)tail))
79 return 0;
80
81 if (CMP_ADDR_EQUAL(tail, tail, 8))
82 return 1;
83
84 if (!check_user_page_readable(mm, ((unsigned long)tail) + 8))
85 return 0;
86
87 return 1;
88} 68}
89 69
90/* 70/*
@@ -118,7 +98,6 @@ static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs)
118void arm_backtrace(struct pt_regs * const regs, unsigned int depth) 98void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
119{ 99{
120 struct frame_tail *tail; 100 struct frame_tail *tail;
121 unsigned long last_address = 0;
122 101
123 tail = ((struct frame_tail *) regs->ARM_fp) - 1; 102 tail = ((struct frame_tail *) regs->ARM_fp) - 1;
124 103
@@ -132,13 +111,6 @@ void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
132 return; 111 return;
133 } 112 }
134 113
135 while (depth-- && tail && !((unsigned long) tail & 3)) { 114 while (depth-- && tail && !((unsigned long) tail & 3))
136 if ((!CMP_ADDR_EQUAL(last_address, tail, 0)
137 || !CMP_ADDR_EQUAL(last_address, tail, 8))
138 && !pages_present(tail))
139 return;
140 last_address = (unsigned long) tail;
141 tail = user_backtrace(tail); 115 tail = user_backtrace(tail);
142 }
143} 116}
144
diff --git a/arch/i386/oprofile/backtrace.c b/arch/i386/oprofile/backtrace.c
index 65dfd2edb671..21654be3f73f 100644
--- a/arch/i386/oprofile/backtrace.c
+++ b/arch/i386/oprofile/backtrace.c
@@ -12,6 +12,7 @@
12#include <linux/sched.h> 12#include <linux/sched.h>
13#include <linux/mm.h> 13#include <linux/mm.h>
14#include <asm/ptrace.h> 14#include <asm/ptrace.h>
15#include <asm/uaccess.h>
15 16
16struct frame_head { 17struct frame_head {
17 struct frame_head * ebp; 18 struct frame_head * ebp;
@@ -21,26 +22,22 @@ struct frame_head {
21static struct frame_head * 22static struct frame_head *
22dump_backtrace(struct frame_head * head) 23dump_backtrace(struct frame_head * head)
23{ 24{
24 oprofile_add_trace(head->ret); 25 struct frame_head bufhead[2];
25 26
26 /* frame pointers should strictly progress back up the stack 27 /* Also check accessibility of one struct frame_head beyond */
27 * (towards higher addresses) */ 28 if (!access_ok(VERIFY_READ, head, sizeof(bufhead)))
28 if (head >= head->ebp) 29 return NULL;
30 if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead)))
29 return NULL; 31 return NULL;
30 32
31 return head->ebp; 33 oprofile_add_trace(bufhead[0].ret);
32}
33
34/* check that the page(s) containing the frame head are present */
35static int pages_present(struct frame_head * head)
36{
37 struct mm_struct * mm = current->mm;
38 34
39 /* FIXME: only necessary once per page */ 35 /* frame pointers should strictly progress back up the stack
40 if (!check_user_page_readable(mm, (unsigned long)head)) 36 * (towards higher addresses) */
41 return 0; 37 if (head >= bufhead[0].ebp)
38 return NULL;
42 39
43 return check_user_page_readable(mm, (unsigned long)(head + 1)); 40 return bufhead[0].ebp;
44} 41}
45 42
46/* 43/*
@@ -97,15 +94,6 @@ x86_backtrace(struct pt_regs * const regs, unsigned int depth)
97 return; 94 return;
98 } 95 }
99 96
100#ifdef CONFIG_SMP 97 while (depth-- && head)
101 if (!spin_trylock(&current->mm->page_table_lock))
102 return;
103#endif
104
105 while (depth-- && head && pages_present(head))
106 head = dump_backtrace(head); 98 head = dump_backtrace(head);
107
108#ifdef CONFIG_SMP
109 spin_unlock(&current->mm->page_table_lock);
110#endif
111} 99}
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 972e2ce8e07c..aa8de20e2e80 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -944,7 +944,6 @@ extern struct page * vmalloc_to_page(void *addr);
944extern unsigned long vmalloc_to_pfn(void *addr); 944extern unsigned long vmalloc_to_pfn(void *addr);
945extern struct page * follow_page(struct mm_struct *mm, unsigned long address, 945extern struct page * follow_page(struct mm_struct *mm, unsigned long address,
946 int write); 946 int write);
947extern int check_user_page_readable(struct mm_struct *mm, unsigned long address);
948int remap_pfn_range(struct vm_area_struct *, unsigned long, 947int remap_pfn_range(struct vm_area_struct *, unsigned long,
949 unsigned long, unsigned long, pgprot_t); 948 unsigned long, unsigned long, pgprot_t);
950 949
diff --git a/mm/memory.c b/mm/memory.c
index 622a4ef5409f..51f7c0a220d4 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -809,8 +809,7 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address,
809 * Do a quick page-table lookup for a single page. 809 * Do a quick page-table lookup for a single page.
810 * mm->page_table_lock must be held. 810 * mm->page_table_lock must be held.
811 */ 811 */
812static struct page *__follow_page(struct mm_struct *mm, unsigned long address, 812struct page *follow_page(struct mm_struct *mm, unsigned long address, int write)
813 int read, int write, int accessed)
814{ 813{
815 pgd_t *pgd; 814 pgd_t *pgd;
816 pud_t *pud; 815 pud_t *pud;
@@ -846,16 +845,12 @@ static struct page *__follow_page(struct mm_struct *mm, unsigned long address,
846 if (pte_present(pte)) { 845 if (pte_present(pte)) {
847 if (write && !pte_write(pte)) 846 if (write && !pte_write(pte))
848 goto out; 847 goto out;
849 if (read && !pte_read(pte))
850 goto out;
851 pfn = pte_pfn(pte); 848 pfn = pte_pfn(pte);
852 if (pfn_valid(pfn)) { 849 if (pfn_valid(pfn)) {
853 page = pfn_to_page(pfn); 850 page = pfn_to_page(pfn);
854 if (accessed) { 851 if (write && !pte_dirty(pte) &&!PageDirty(page))
855 if (write && !pte_dirty(pte) &&!PageDirty(page)) 852 set_page_dirty(page);
856 set_page_dirty(page); 853 mark_page_accessed(page);
857 mark_page_accessed(page);
858 }
859 return page; 854 return page;
860 } 855 }
861 } 856 }
@@ -864,22 +859,6 @@ out:
864 return NULL; 859 return NULL;
865} 860}
866 861
867inline struct page *
868follow_page(struct mm_struct *mm, unsigned long address, int write)
869{
870 return __follow_page(mm, address, 0, write, 1);
871}
872
873/*
874 * check_user_page_readable() can be called frm niterrupt context by oprofile,
875 * so we need to avoid taking any non-irq-safe locks
876 */
877int check_user_page_readable(struct mm_struct *mm, unsigned long address)
878{
879 return __follow_page(mm, address, 1, 0, 0) != NULL;
880}
881EXPORT_SYMBOL(check_user_page_readable);
882
883static inline int 862static inline int
884untouched_anonymous_page(struct mm_struct* mm, struct vm_area_struct *vma, 863untouched_anonymous_page(struct mm_struct* mm, struct vm_area_struct *vma,
885 unsigned long address) 864 unsigned long address)