~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/mm/ptdump.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 
  3 #include <linux/pagewalk.h>
  4 #include <linux/debugfs.h>
  5 #include <linux/ptdump.h>
  6 #include <linux/kasan.h>
  7 
  8 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
  9 /*
 10  * This is an optimization for KASAN=y case. Since all kasan page tables
 11  * eventually point to the kasan_early_shadow_page we could call note_page()
 12  * right away without walking through lower level page tables. This saves
 13  * us dozens of seconds (minutes for 5-level config) while checking for
 14  * W+X mapping or reading kernel_page_tables debugfs file.
 15  */
 16 static inline int note_kasan_page_table(struct mm_walk *walk,
 17                                         unsigned long addr)
 18 {
 19         struct ptdump_state *st = walk->private;
 20 
 21         st->note_page(st, addr, 4, pte_val(kasan_early_shadow_pte[0]));
 22 
 23         walk->action = ACTION_CONTINUE;
 24 
 25         return 0;
 26 }
 27 #endif
 28 
 29 static int ptdump_pgd_entry(pgd_t *pgd, unsigned long addr,
 30                             unsigned long next, struct mm_walk *walk)
 31 {
 32         struct ptdump_state *st = walk->private;
 33         pgd_t val = READ_ONCE(*pgd);
 34 
 35 #if CONFIG_PGTABLE_LEVELS > 4 && \
 36                 (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS))
 37         if (pgd_page(val) == virt_to_page(lm_alias(kasan_early_shadow_p4d)))
 38                 return note_kasan_page_table(walk, addr);
 39 #endif
 40 
 41         if (st->effective_prot)
 42                 st->effective_prot(st, 0, pgd_val(val));
 43 
 44         if (pgd_leaf(val)) {
 45                 st->note_page(st, addr, 0, pgd_val(val));
 46                 walk->action = ACTION_CONTINUE;
 47         }
 48 
 49         return 0;
 50 }
 51 
 52 static int ptdump_p4d_entry(p4d_t *p4d, unsigned long addr,
 53                             unsigned long next, struct mm_walk *walk)
 54 {
 55         struct ptdump_state *st = walk->private;
 56         p4d_t val = READ_ONCE(*p4d);
 57 
 58 #if CONFIG_PGTABLE_LEVELS > 3 && \
 59                 (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS))
 60         if (p4d_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pud)))
 61                 return note_kasan_page_table(walk, addr);
 62 #endif
 63 
 64         if (st->effective_prot)
 65                 st->effective_prot(st, 1, p4d_val(val));
 66 
 67         if (p4d_leaf(val)) {
 68                 st->note_page(st, addr, 1, p4d_val(val));
 69                 walk->action = ACTION_CONTINUE;
 70         }
 71 
 72         return 0;
 73 }
 74 
 75 static int ptdump_pud_entry(pud_t *pud, unsigned long addr,
 76                             unsigned long next, struct mm_walk *walk)
 77 {
 78         struct ptdump_state *st = walk->private;
 79         pud_t val = READ_ONCE(*pud);
 80 
 81 #if CONFIG_PGTABLE_LEVELS > 2 && \
 82                 (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS))
 83         if (pud_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pmd)))
 84                 return note_kasan_page_table(walk, addr);
 85 #endif
 86 
 87         if (st->effective_prot)
 88                 st->effective_prot(st, 2, pud_val(val));
 89 
 90         if (pud_leaf(val)) {
 91                 st->note_page(st, addr, 2, pud_val(val));
 92                 walk->action = ACTION_CONTINUE;
 93         }
 94 
 95         return 0;
 96 }
 97 
 98 static int ptdump_pmd_entry(pmd_t *pmd, unsigned long addr,
 99                             unsigned long next, struct mm_walk *walk)
100 {
101         struct ptdump_state *st = walk->private;
102         pmd_t val = READ_ONCE(*pmd);
103 
104 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
105         if (pmd_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pte)))
106                 return note_kasan_page_table(walk, addr);
107 #endif
108 
109         if (st->effective_prot)
110                 st->effective_prot(st, 3, pmd_val(val));
111         if (pmd_leaf(val)) {
112                 st->note_page(st, addr, 3, pmd_val(val));
113                 walk->action = ACTION_CONTINUE;
114         }
115 
116         return 0;
117 }
118 
119 static int ptdump_pte_entry(pte_t *pte, unsigned long addr,
120                             unsigned long next, struct mm_walk *walk)
121 {
122         struct ptdump_state *st = walk->private;
123         pte_t val = ptep_get_lockless(pte);
124 
125         if (st->effective_prot)
126                 st->effective_prot(st, 4, pte_val(val));
127 
128         st->note_page(st, addr, 4, pte_val(val));
129 
130         return 0;
131 }
132 
133 static int ptdump_hole(unsigned long addr, unsigned long next,
134                        int depth, struct mm_walk *walk)
135 {
136         struct ptdump_state *st = walk->private;
137 
138         st->note_page(st, addr, depth, 0);
139 
140         return 0;
141 }
142 
143 static const struct mm_walk_ops ptdump_ops = {
144         .pgd_entry      = ptdump_pgd_entry,
145         .p4d_entry      = ptdump_p4d_entry,
146         .pud_entry      = ptdump_pud_entry,
147         .pmd_entry      = ptdump_pmd_entry,
148         .pte_entry      = ptdump_pte_entry,
149         .pte_hole       = ptdump_hole,
150 };
151 
152 void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm, pgd_t *pgd)
153 {
154         const struct ptdump_range *range = st->range;
155 
156         mmap_write_lock(mm);
157         while (range->start != range->end) {
158                 walk_page_range_novma(mm, range->start, range->end,
159                                       &ptdump_ops, pgd, st);
160                 range++;
161         }
162         mmap_write_unlock(mm);
163 
164         /* Flush out the last page */
165         st->note_page(st, 0, -1, 0);
166 }
167 
168 static int check_wx_show(struct seq_file *m, void *v)
169 {
170         if (ptdump_check_wx())
171                 seq_puts(m, "SUCCESS\n");
172         else
173                 seq_puts(m, "FAILED\n");
174 
175         return 0;
176 }
177 
178 DEFINE_SHOW_ATTRIBUTE(check_wx);
179 
180 static int ptdump_debugfs_init(void)
181 {
182         debugfs_create_file("check_wx_pages", 0400, NULL, NULL, &check_wx_fops);
183 
184         return 0;
185 }
186 
187 device_initcall(ptdump_debugfs_init);
188 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php