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

TOMOYO Linux Cross Reference
Linux/arch/arm64/mm/ptdump.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ 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-only
  2 /*
  3  * Copyright (c) 2014, The Linux Foundation. All rights reserved.
  4  * Debug helper to dump the current kernel pagetables of the system
  5  * so that we can see what the various memory ranges are set to.
  6  *
  7  * Derived from x86 and arm implementation:
  8  * (C) Copyright 2008 Intel Corporation
  9  *
 10  * Author: Arjan van de Ven <arjan@linux.intel.com>
 11  */
 12 #include <linux/debugfs.h>
 13 #include <linux/errno.h>
 14 #include <linux/fs.h>
 15 #include <linux/io.h>
 16 #include <linux/init.h>
 17 #include <linux/mm.h>
 18 #include <linux/ptdump.h>
 19 #include <linux/sched.h>
 20 #include <linux/seq_file.h>
 21 
 22 #include <asm/fixmap.h>
 23 #include <asm/kasan.h>
 24 #include <asm/memory.h>
 25 #include <asm/pgtable-hwdef.h>
 26 #include <asm/ptdump.h>
 27 
 28 
 29 #define pt_dump_seq_printf(m, fmt, args...)     \
 30 ({                                              \
 31         if (m)                                  \
 32                 seq_printf(m, fmt, ##args);     \
 33 })
 34 
 35 #define pt_dump_seq_puts(m, fmt)        \
 36 ({                                      \
 37         if (m)                          \
 38                 seq_printf(m, fmt);     \
 39 })
 40 
 41 /*
 42  * The page dumper groups page table entries of the same type into a single
 43  * description. It uses pg_state to track the range information while
 44  * iterating over the pte entries. When the continuity is broken it then
 45  * dumps out a description of the range.
 46  */
 47 struct pg_state {
 48         struct ptdump_state ptdump;
 49         struct seq_file *seq;
 50         const struct addr_marker *marker;
 51         const struct mm_struct *mm;
 52         unsigned long start_address;
 53         int level;
 54         u64 current_prot;
 55         bool check_wx;
 56         unsigned long wx_pages;
 57         unsigned long uxn_pages;
 58 };
 59 
 60 struct prot_bits {
 61         u64             mask;
 62         u64             val;
 63         const char      *set;
 64         const char      *clear;
 65 };
 66 
 67 static const struct prot_bits pte_bits[] = {
 68         {
 69                 .mask   = PTE_VALID,
 70                 .val    = PTE_VALID,
 71                 .set    = " ",
 72                 .clear  = "F",
 73         }, {
 74                 .mask   = PTE_USER,
 75                 .val    = PTE_USER,
 76                 .set    = "USR",
 77                 .clear  = "   ",
 78         }, {
 79                 .mask   = PTE_RDONLY,
 80                 .val    = PTE_RDONLY,
 81                 .set    = "ro",
 82                 .clear  = "RW",
 83         }, {
 84                 .mask   = PTE_PXN,
 85                 .val    = PTE_PXN,
 86                 .set    = "NX",
 87                 .clear  = "x ",
 88         }, {
 89                 .mask   = PTE_SHARED,
 90                 .val    = PTE_SHARED,
 91                 .set    = "SHD",
 92                 .clear  = "   ",
 93         }, {
 94                 .mask   = PTE_AF,
 95                 .val    = PTE_AF,
 96                 .set    = "AF",
 97                 .clear  = "  ",
 98         }, {
 99                 .mask   = PTE_NG,
100                 .val    = PTE_NG,
101                 .set    = "NG",
102                 .clear  = "  ",
103         }, {
104                 .mask   = PTE_CONT,
105                 .val    = PTE_CONT,
106                 .set    = "CON",
107                 .clear  = "   ",
108         }, {
109                 .mask   = PTE_TABLE_BIT,
110                 .val    = PTE_TABLE_BIT,
111                 .set    = "   ",
112                 .clear  = "BLK",
113         }, {
114                 .mask   = PTE_UXN,
115                 .val    = PTE_UXN,
116                 .set    = "UXN",
117                 .clear  = "   ",
118         }, {
119                 .mask   = PTE_GP,
120                 .val    = PTE_GP,
121                 .set    = "GP",
122                 .clear  = "  ",
123         }, {
124                 .mask   = PTE_ATTRINDX_MASK,
125                 .val    = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
126                 .set    = "DEVICE/nGnRnE",
127         }, {
128                 .mask   = PTE_ATTRINDX_MASK,
129                 .val    = PTE_ATTRINDX(MT_DEVICE_nGnRE),
130                 .set    = "DEVICE/nGnRE",
131         }, {
132                 .mask   = PTE_ATTRINDX_MASK,
133                 .val    = PTE_ATTRINDX(MT_NORMAL_NC),
134                 .set    = "MEM/NORMAL-NC",
135         }, {
136                 .mask   = PTE_ATTRINDX_MASK,
137                 .val    = PTE_ATTRINDX(MT_NORMAL),
138                 .set    = "MEM/NORMAL",
139         }, {
140                 .mask   = PTE_ATTRINDX_MASK,
141                 .val    = PTE_ATTRINDX(MT_NORMAL_TAGGED),
142                 .set    = "MEM/NORMAL-TAGGED",
143         }
144 };
145 
146 struct pg_level {
147         const struct prot_bits *bits;
148         char name[4];
149         int num;
150         u64 mask;
151 };
152 
153 static struct pg_level pg_level[] __ro_after_init = {
154         { /* pgd */
155                 .name   = "PGD",
156                 .bits   = pte_bits,
157                 .num    = ARRAY_SIZE(pte_bits),
158         }, { /* p4d */
159                 .name   = "P4D",
160                 .bits   = pte_bits,
161                 .num    = ARRAY_SIZE(pte_bits),
162         }, { /* pud */
163                 .name   = "PUD",
164                 .bits   = pte_bits,
165                 .num    = ARRAY_SIZE(pte_bits),
166         }, { /* pmd */
167                 .name   = "PMD",
168                 .bits   = pte_bits,
169                 .num    = ARRAY_SIZE(pte_bits),
170         }, { /* pte */
171                 .name   = "PTE",
172                 .bits   = pte_bits,
173                 .num    = ARRAY_SIZE(pte_bits),
174         },
175 };
176 
177 static void dump_prot(struct pg_state *st, const struct prot_bits *bits,
178                         size_t num)
179 {
180         unsigned i;
181 
182         for (i = 0; i < num; i++, bits++) {
183                 const char *s;
184 
185                 if ((st->current_prot & bits->mask) == bits->val)
186                         s = bits->set;
187                 else
188                         s = bits->clear;
189 
190                 if (s)
191                         pt_dump_seq_printf(st->seq, " %s", s);
192         }
193 }
194 
195 static void note_prot_uxn(struct pg_state *st, unsigned long addr)
196 {
197         if (!st->check_wx)
198                 return;
199 
200         if ((st->current_prot & PTE_UXN) == PTE_UXN)
201                 return;
202 
203         WARN_ONCE(1, "arm64/mm: Found non-UXN mapping at address %p/%pS\n",
204                   (void *)st->start_address, (void *)st->start_address);
205 
206         st->uxn_pages += (addr - st->start_address) / PAGE_SIZE;
207 }
208 
209 static void note_prot_wx(struct pg_state *st, unsigned long addr)
210 {
211         if (!st->check_wx)
212                 return;
213         if ((st->current_prot & PTE_RDONLY) == PTE_RDONLY)
214                 return;
215         if ((st->current_prot & PTE_PXN) == PTE_PXN)
216                 return;
217 
218         WARN_ONCE(1, "arm64/mm: Found insecure W+X mapping at address %p/%pS\n",
219                   (void *)st->start_address, (void *)st->start_address);
220 
221         st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
222 }
223 
224 static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
225                       u64 val)
226 {
227         struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
228         static const char units[] = "KMGTPE";
229         u64 prot = 0;
230 
231         /* check if the current level has been folded dynamically */
232         if ((level == 1 && mm_p4d_folded(st->mm)) ||
233             (level == 2 && mm_pud_folded(st->mm)))
234                 level = 0;
235 
236         if (level >= 0)
237                 prot = val & pg_level[level].mask;
238 
239         if (st->level == -1) {
240                 st->level = level;
241                 st->current_prot = prot;
242                 st->start_address = addr;
243                 pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
244         } else if (prot != st->current_prot || level != st->level ||
245                    addr >= st->marker[1].start_address) {
246                 const char *unit = units;
247                 unsigned long delta;
248 
249                 if (st->current_prot) {
250                         note_prot_uxn(st, addr);
251                         note_prot_wx(st, addr);
252                 }
253 
254                 pt_dump_seq_printf(st->seq, "0x%016lx-0x%016lx   ",
255                                    st->start_address, addr);
256 
257                 delta = (addr - st->start_address) >> 10;
258                 while (!(delta & 1023) && unit[1]) {
259                         delta >>= 10;
260                         unit++;
261                 }
262                 pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
263                                    pg_level[st->level].name);
264                 if (st->current_prot && pg_level[st->level].bits)
265                         dump_prot(st, pg_level[st->level].bits,
266                                   pg_level[st->level].num);
267                 pt_dump_seq_puts(st->seq, "\n");
268 
269                 if (addr >= st->marker[1].start_address) {
270                         st->marker++;
271                         pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
272                 }
273 
274                 st->start_address = addr;
275                 st->current_prot = prot;
276                 st->level = level;
277         }
278 
279         if (addr >= st->marker[1].start_address) {
280                 st->marker++;
281                 pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
282         }
283 
284 }
285 
286 void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
287 {
288         unsigned long end = ~0UL;
289         struct pg_state st;
290 
291         if (info->base_addr < TASK_SIZE_64)
292                 end = TASK_SIZE_64;
293 
294         st = (struct pg_state){
295                 .seq = s,
296                 .marker = info->markers,
297                 .mm = info->mm,
298                 .level = -1,
299                 .ptdump = {
300                         .note_page = note_page,
301                         .range = (struct ptdump_range[]){
302                                 {info->base_addr, end},
303                                 {0, 0}
304                         }
305                 }
306         };
307 
308         ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
309 }
310 
311 static void __init ptdump_initialize(void)
312 {
313         unsigned i, j;
314 
315         for (i = 0; i < ARRAY_SIZE(pg_level); i++)
316                 if (pg_level[i].bits)
317                         for (j = 0; j < pg_level[i].num; j++)
318                                 pg_level[i].mask |= pg_level[i].bits[j].mask;
319 }
320 
321 static struct ptdump_info kernel_ptdump_info __ro_after_init = {
322         .mm             = &init_mm,
323 };
324 
325 bool ptdump_check_wx(void)
326 {
327         struct pg_state st = {
328                 .seq = NULL,
329                 .marker = (struct addr_marker[]) {
330                         { 0, NULL},
331                         { -1, NULL},
332                 },
333                 .level = -1,
334                 .check_wx = true,
335                 .ptdump = {
336                         .note_page = note_page,
337                         .range = (struct ptdump_range[]) {
338                                 {_PAGE_OFFSET(vabits_actual), ~0UL},
339                                 {0, 0}
340                         }
341                 }
342         };
343 
344         ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
345 
346         if (st.wx_pages || st.uxn_pages) {
347                 pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
348                         st.wx_pages, st.uxn_pages);
349 
350                 return false;
351         } else {
352                 pr_info("Checked W+X mappings: passed, no W+X pages found\n");
353 
354                 return true;
355         }
356 }
357 
358 static int __init ptdump_init(void)
359 {
360         u64 page_offset = _PAGE_OFFSET(vabits_actual);
361         u64 vmemmap_start = (u64)virt_to_page((void *)page_offset);
362         struct addr_marker m[] = {
363                 { PAGE_OFFSET,          "Linear Mapping start" },
364                 { PAGE_END,             "Linear Mapping end" },
365 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
366                 { KASAN_SHADOW_START,   "Kasan shadow start" },
367                 { KASAN_SHADOW_END,     "Kasan shadow end" },
368 #endif
369                 { MODULES_VADDR,        "Modules start" },
370                 { MODULES_END,          "Modules end" },
371                 { VMALLOC_START,        "vmalloc() area" },
372                 { VMALLOC_END,          "vmalloc() end" },
373                 { vmemmap_start,        "vmemmap start" },
374                 { VMEMMAP_END,          "vmemmap end" },
375                 { PCI_IO_START,         "PCI I/O start" },
376                 { PCI_IO_END,           "PCI I/O end" },
377                 { FIXADDR_TOT_START,    "Fixmap start" },
378                 { FIXADDR_TOP,          "Fixmap end" },
379                 { -1,                   NULL },
380         };
381         static struct addr_marker address_markers[ARRAY_SIZE(m)] __ro_after_init;
382 
383         kernel_ptdump_info.markers = memcpy(address_markers, m, sizeof(m));
384         kernel_ptdump_info.base_addr = page_offset;
385 
386         ptdump_initialize();
387         ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
388         return 0;
389 }
390 device_initcall(ptdump_init);
391 

~ [ 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