1 // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 3 * Debug helper to dump the current kernel pag 4 * so that we can see what the various memory 5 * 6 * (C) Copyright 2008 Intel Corporation 7 * 8 * Author: Arjan van de Ven <arjan@linux.intel 9 */ 10 11 #include <linux/debugfs.h> 12 #include <linux/kasan.h> 13 #include <linux/mm.h> 14 #include <linux/init.h> 15 #include <linux/sched.h> 16 #include <linux/seq_file.h> 17 #include <linux/highmem.h> 18 #include <linux/pci.h> 19 #include <linux/ptdump.h> 20 21 #include <asm/e820/types.h> 22 23 /* 24 * The dumper groups pagetable entries of the 25 * that it needs to keep some state when walki 26 * when a "break" in the continuity is found. 27 */ 28 struct pg_state { 29 struct ptdump_state ptdump; 30 int level; 31 pgprotval_t current_prot; 32 pgprotval_t effective_prot; 33 pgprotval_t prot_levels[5]; 34 unsigned long start_address; 35 const struct addr_marker *marker; 36 unsigned long lines; 37 bool to_dmesg; 38 bool check_wx; 39 unsigned long wx_pages; 40 struct seq_file *seq; 41 }; 42 43 struct addr_marker { 44 unsigned long start_address; 45 const char *name; 46 unsigned long max_lines; 47 }; 48 49 /* Address space markers hints */ 50 51 #ifdef CONFIG_X86_64 52 53 enum address_markers_idx { 54 USER_SPACE_NR = 0, 55 KERNEL_SPACE_NR, 56 #ifdef CONFIG_MODIFY_LDT_SYSCALL 57 LDT_NR, 58 #endif 59 LOW_KERNEL_NR, 60 VMALLOC_START_NR, 61 VMEMMAP_START_NR, 62 #ifdef CONFIG_KASAN 63 KASAN_SHADOW_START_NR, 64 KASAN_SHADOW_END_NR, 65 #endif 66 CPU_ENTRY_AREA_NR, 67 #ifdef CONFIG_X86_ESPFIX64 68 ESPFIX_START_NR, 69 #endif 70 #ifdef CONFIG_EFI 71 EFI_END_NR, 72 #endif 73 HIGH_KERNEL_NR, 74 MODULES_VADDR_NR, 75 MODULES_END_NR, 76 FIXADDR_START_NR, 77 END_OF_SPACE_NR, 78 }; 79 80 static struct addr_marker address_markers[] = 81 [USER_SPACE_NR] = { 0, 82 [KERNEL_SPACE_NR] = { (1UL << 63 83 [LOW_KERNEL_NR] = { 0UL, 84 [VMALLOC_START_NR] = { 0UL, 85 [VMEMMAP_START_NR] = { 0UL, 86 #ifdef CONFIG_KASAN 87 /* 88 * These fields get initialized with t 89 * KASAN_SHADOW_{START,END} values in 90 */ 91 [KASAN_SHADOW_START_NR] = { 0UL, 92 [KASAN_SHADOW_END_NR] = { 0UL, 93 #endif 94 #ifdef CONFIG_MODIFY_LDT_SYSCALL 95 [LDT_NR] = { 0UL, 96 #endif 97 [CPU_ENTRY_AREA_NR] = { CPU_ENTRY_ 98 #ifdef CONFIG_X86_ESPFIX64 99 [ESPFIX_START_NR] = { ESPFIX_BAS 100 #endif 101 #ifdef CONFIG_EFI 102 [EFI_END_NR] = { EFI_VA_END 103 #endif 104 [HIGH_KERNEL_NR] = { __START_KE 105 [MODULES_VADDR_NR] = { MODULES_VA 106 [MODULES_END_NR] = { MODULES_EN 107 [FIXADDR_START_NR] = { FIXADDR_ST 108 [END_OF_SPACE_NR] = { -1, 109 }; 110 111 #define INIT_PGD ((pgd_t *) &init_top_p 112 113 #else /* CONFIG_X86_64 */ 114 115 enum address_markers_idx { 116 USER_SPACE_NR = 0, 117 KERNEL_SPACE_NR, 118 VMALLOC_START_NR, 119 VMALLOC_END_NR, 120 #ifdef CONFIG_HIGHMEM 121 PKMAP_BASE_NR, 122 #endif 123 #ifdef CONFIG_MODIFY_LDT_SYSCALL 124 LDT_NR, 125 #endif 126 CPU_ENTRY_AREA_NR, 127 FIXADDR_START_NR, 128 END_OF_SPACE_NR, 129 }; 130 131 static struct addr_marker address_markers[] = 132 [USER_SPACE_NR] = { 0, 133 [KERNEL_SPACE_NR] = { PAGE_OFFSE 134 [VMALLOC_START_NR] = { 0UL, 135 [VMALLOC_END_NR] = { 0UL, 136 #ifdef CONFIG_HIGHMEM 137 [PKMAP_BASE_NR] = { 0UL, 138 #endif 139 #ifdef CONFIG_MODIFY_LDT_SYSCALL 140 [LDT_NR] = { 0UL, 141 #endif 142 [CPU_ENTRY_AREA_NR] = { 0UL, 143 [FIXADDR_START_NR] = { 0UL, 144 [END_OF_SPACE_NR] = { -1, 145 }; 146 147 #define INIT_PGD (swapper_pg_dir) 148 149 #endif /* !CONFIG_X86_64 */ 150 151 /* Multipliers for offsets within the PTEs */ 152 #define PTE_LEVEL_MULT (PAGE_SIZE) 153 #define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEV 154 #define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEV 155 #define P4D_LEVEL_MULT (PTRS_PER_PUD * PUD_LEV 156 #define PGD_LEVEL_MULT (PTRS_PER_P4D * P4D_LEV 157 158 #define pt_dump_seq_printf(m, to_dmesg, fmt, a 159 ({ 160 if (to_dmesg) 161 printk(KERN_INFO fmt, ##args); 162 else 163 if (m) 164 seq_printf(m, fmt, ##a 165 }) 166 167 #define pt_dump_cont_printf(m, to_dmesg, fmt, 168 ({ 169 if (to_dmesg) 170 printk(KERN_CONT fmt, ##args); 171 else 172 if (m) 173 seq_printf(m, fmt, ##a 174 }) 175 176 /* 177 * Print a readable form of a pgprot_t to the 178 */ 179 static void printk_prot(struct seq_file *m, pg 180 { 181 static const char * const level_name[] 182 { "pgd", "p4d", "pud", "pmd", 183 184 if (!(pr & _PAGE_PRESENT)) { 185 /* Not present */ 186 pt_dump_cont_printf(m, dmsg, " 187 } else { 188 if (pr & _PAGE_USER) 189 pt_dump_cont_printf(m, 190 else 191 pt_dump_cont_printf(m, 192 if (pr & _PAGE_RW) 193 pt_dump_cont_printf(m, 194 else 195 pt_dump_cont_printf(m, 196 if (pr & _PAGE_PWT) 197 pt_dump_cont_printf(m, 198 else 199 pt_dump_cont_printf(m, 200 if (pr & _PAGE_PCD) 201 pt_dump_cont_printf(m, 202 else 203 pt_dump_cont_printf(m, 204 205 /* Bit 7 has a different meani 206 if (level <= 3 && pr & _PAGE_P 207 pt_dump_cont_printf(m, 208 else 209 pt_dump_cont_printf(m, 210 if ((level == 4 && pr & _PAGE_ 211 ((level == 3 || level == 2 212 pt_dump_cont_printf(m, 213 else 214 pt_dump_cont_printf(m, 215 if (pr & _PAGE_GLOBAL) 216 pt_dump_cont_printf(m, 217 else 218 pt_dump_cont_printf(m, 219 if (pr & _PAGE_NX) 220 pt_dump_cont_printf(m, 221 else 222 pt_dump_cont_printf(m, 223 } 224 pt_dump_cont_printf(m, dmsg, "%s\n", l 225 } 226 227 static void note_wx(struct pg_state *st, unsig 228 { 229 unsigned long npages; 230 231 npages = (addr - st->start_address) / 232 233 #ifdef CONFIG_PCI_BIOS 234 /* 235 * If PCI BIOS is enabled, the PCI BIO 236 * Inform about it, but avoid the warn 237 */ 238 if (pcibios_enabled && st->start_addre 239 addr <= PAGE_OFFSET + BIOS_END) { 240 pr_warn_once("x86/mm: PCI BIOS 241 return; 242 } 243 #endif 244 /* Account the WX pages */ 245 st->wx_pages += npages; 246 WARN_ONCE(__supported_pte_mask & _PAGE 247 "x86/mm: Found insecure W+X 248 (void *)st->start_address); 249 } 250 251 static void effective_prot(struct ptdump_state 252 { 253 struct pg_state *st = container_of(pt_ 254 pgprotval_t prot = val & PTE_FLAGS_MAS 255 pgprotval_t effective; 256 257 if (level > 0) { 258 pgprotval_t higher_prot = st-> 259 260 effective = (higher_prot & pro 261 ((higher_prot | pr 262 } else { 263 effective = prot; 264 } 265 266 st->prot_levels[level] = effective; 267 } 268 269 /* 270 * This function gets called on a break in a c 271 * of PTE entries; the next one is different s 272 * print what we collected so far. 273 */ 274 static void note_page(struct ptdump_state *pt_ 275 u64 val) 276 { 277 struct pg_state *st = container_of(pt_ 278 pgprotval_t new_prot, new_eff; 279 pgprotval_t cur, eff; 280 static const char units[] = "BKMGTPE"; 281 struct seq_file *m = st->seq; 282 283 new_prot = val & PTE_FLAGS_MASK; 284 if (!val) 285 new_eff = 0; 286 else 287 new_eff = st->prot_levels[leve 288 289 /* 290 * If we have a "break" in the series, 291 * we have now. "break" is either chan 292 * address space marker. 293 */ 294 cur = st->current_prot; 295 eff = st->effective_prot; 296 297 if (st->level == -1) { 298 /* First entry */ 299 st->current_prot = new_prot; 300 st->effective_prot = new_eff; 301 st->level = level; 302 st->marker = address_markers; 303 st->lines = 0; 304 pt_dump_seq_printf(m, st->to_d 305 st->marker- 306 } else if (new_prot != cur || new_eff 307 addr >= st->marker[1].start 308 const char *unit = units; 309 unsigned long delta; 310 int width = sizeof(unsigned lo 311 312 if (st->check_wx && (eff & _PA 313 note_wx(st, addr); 314 315 /* 316 * Now print the actual finish 317 */ 318 if (!st->marker->max_lines || 319 st->lines < st->marker->ma 320 pt_dump_seq_printf(m, 321 "0x 322 wid 323 wid 324 325 delta = addr - st->sta 326 while (!(delta & 1023) 327 delta >>= 10; 328 unit++; 329 } 330 pt_dump_cont_printf(m, 331 de 332 printk_prot(m, st->cur 333 st->to_dme 334 } 335 st->lines++; 336 337 /* 338 * We print markers for specia 339 * such as the start of vmallo 340 * This helps in the interpret 341 */ 342 if (addr >= st->marker[1].star 343 if (st->marker->max_li 344 st->lines > st->ma 345 unsigned long 346 st->li 347 pt_dump_seq_pr 348 349 350 351 } 352 st->marker++; 353 st->lines = 0; 354 pt_dump_seq_printf(m, 355 st- 356 } 357 358 st->start_address = addr; 359 st->current_prot = new_prot; 360 st->effective_prot = new_eff; 361 st->level = level; 362 } 363 } 364 365 bool ptdump_walk_pgd_level_core(struct seq_fil 366 struct mm_stru 367 bool checkwx, 368 { 369 const struct ptdump_range ptdump_range 370 #ifdef CONFIG_X86_64 371 {0, PTRS_PER_PGD * PGD_LEVEL_MULT / 2} 372 {GUARD_HOLE_END_ADDR, ~0UL}, 373 #else 374 {0, ~0UL}, 375 #endif 376 {0, 0} 377 }; 378 379 struct pg_state st = { 380 .ptdump = { 381 .note_page = note 382 .effective_prot = effe 383 .range = ptdu 384 }, 385 .level = -1, 386 .to_dmesg = dmesg, 387 .check_wx = checkwx, 388 .seq = m 389 }; 390 391 ptdump_walk_pgd(&st.ptdump, mm, pgd); 392 393 if (!checkwx) 394 return true; 395 if (st.wx_pages) { 396 pr_info("x86/mm: Checked W+X m 397 st.wx_pages); 398 399 return false; 400 } else { 401 pr_info("x86/mm: Checked W+X m 402 403 return true; 404 } 405 } 406 407 void ptdump_walk_pgd_level(struct seq_file *m, 408 { 409 ptdump_walk_pgd_level_core(m, mm, mm-> 410 } 411 412 void ptdump_walk_pgd_level_debugfs(struct seq_ 413 bool user) 414 { 415 pgd_t *pgd = mm->pgd; 416 #ifdef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION 417 if (user && boot_cpu_has(X86_FEATURE_P 418 pgd = kernel_to_user_pgdp(pgd) 419 #endif 420 ptdump_walk_pgd_level_core(m, mm, pgd, 421 } 422 EXPORT_SYMBOL_GPL(ptdump_walk_pgd_level_debugf 423 424 void ptdump_walk_user_pgd_level_checkwx(void) 425 { 426 #ifdef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION 427 pgd_t *pgd = INIT_PGD; 428 429 if (!(__supported_pte_mask & _PAGE_NX) 430 !boot_cpu_has(X86_FEATURE_PTI)) 431 return; 432 433 pr_info("x86/mm: Checking user space p 434 pgd = kernel_to_user_pgdp(pgd); 435 ptdump_walk_pgd_level_core(NULL, &init 436 #endif 437 } 438 439 bool ptdump_walk_pgd_level_checkwx(void) 440 { 441 if (!(__supported_pte_mask & _PAGE_NX) 442 return true; 443 444 return ptdump_walk_pgd_level_core(NULL 445 } 446 447 static int __init pt_dump_init(void) 448 { 449 /* 450 * Various markers are not compile-tim 451 * here. 452 */ 453 #ifdef CONFIG_X86_64 454 address_markers[LOW_KERNEL_NR].start_a 455 address_markers[VMALLOC_START_NR].star 456 address_markers[VMEMMAP_START_NR].star 457 #ifdef CONFIG_MODIFY_LDT_SYSCALL 458 address_markers[LDT_NR].start_address 459 #endif 460 #ifdef CONFIG_KASAN 461 address_markers[KASAN_SHADOW_START_NR] 462 address_markers[KASAN_SHADOW_END_NR].s 463 #endif 464 #endif 465 #ifdef CONFIG_X86_32 466 address_markers[VMALLOC_START_NR].star 467 address_markers[VMALLOC_END_NR].start_ 468 # ifdef CONFIG_HIGHMEM 469 address_markers[PKMAP_BASE_NR].start_a 470 # endif 471 address_markers[FIXADDR_START_NR].star 472 address_markers[CPU_ENTRY_AREA_NR].sta 473 # ifdef CONFIG_MODIFY_LDT_SYSCALL 474 address_markers[LDT_NR].start_address 475 # endif 476 #endif 477 return 0; 478 } 479 __initcall(pt_dump_init); 480
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.