Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kernel/memremap, kasan: make ZONE_DEVICE with work with KASAN

KASAN learns about hotadded memory via the memory hotplug notifier.
devm_memremap_pages() intentionally skips calling memory hotplug
notifiers. So KASAN doesn't know anything about new memory added by
devm_memremap_pages(). This causes a crash when KASAN tries to access
non-existent shadow memory:

BUG: unable to handle kernel paging request at ffffed0078000000
RIP: 0010:check_memory_region+0x82/0x1e0
Call Trace:
memcpy+0x1f/0x50
pmem_do_bvec+0x163/0x720
pmem_make_request+0x305/0xac0
generic_make_request+0x54f/0xcf0
submit_bio+0x9c/0x370
submit_bh_wbc+0x4c7/0x700
block_read_full_page+0x5ef/0x870
do_read_cache_page+0x2b8/0xb30
read_dev_sector+0xbd/0x3f0
read_lba.isra.0+0x277/0x670
efi_partition+0x41a/0x18f0
check_partition+0x30d/0x5e9
rescan_partitions+0x18c/0x840
__blkdev_get+0x859/0x1060
blkdev_get+0x23f/0x810
__device_add_disk+0x9c8/0xde0
pmem_attach_disk+0x9a8/0xf50
nvdimm_bus_probe+0xf3/0x3c0
driver_probe_device+0x493/0xbd0
bus_for_each_drv+0x118/0x1b0
__device_attach+0x1cd/0x2b0
bus_probe_device+0x1ac/0x260
device_add+0x90d/0x1380
nd_async_device_register+0xe/0x50
async_run_entry_fn+0xc3/0x5d0
process_one_work+0xa0a/0x1810
worker_thread+0x87/0xe80
kthread+0x2d7/0x390
ret_from_fork+0x3a/0x50

Add kasan_add_zero_shadow()/kasan_remove_zero_shadow() - post mm_init()
interface to map/unmap kasan_zero_page at requested virtual addresses.
And use it to add/remove the shadow memory for hotplugged/unplugged
device memory.

Link: http://lkml.kernel.org/r/20180629164932.740-1-aryabinin@virtuozzo.com
Fixes: 41e94a851304 ("add devm_memremap_pages")
Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Reported-by: Dave Chinner <david@fromorbit.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Tested-by: Dan Williams <dan.j.williams@intel.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Alexander Potapenko <glider@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Andrey Ryabinin and committed by
Linus Torvalds
0207df4f 50f8b92f

+325 -14
+12 -1
include/linux/kasan.h
··· 20 20 extern pud_t kasan_zero_pud[PTRS_PER_PUD]; 21 21 extern p4d_t kasan_zero_p4d[MAX_PTRS_PER_P4D]; 22 22 23 - void kasan_populate_zero_shadow(const void *shadow_start, 23 + int kasan_populate_zero_shadow(const void *shadow_start, 24 24 const void *shadow_end); 25 25 26 26 static inline void *kasan_mem_to_shadow(const void *addr) ··· 70 70 71 71 int kasan_module_alloc(void *addr, size_t size); 72 72 void kasan_free_shadow(const struct vm_struct *vm); 73 + 74 + int kasan_add_zero_shadow(void *start, unsigned long size); 75 + void kasan_remove_zero_shadow(void *start, unsigned long size); 73 76 74 77 size_t ksize(const void *); 75 78 static inline void kasan_unpoison_slab(const void *ptr) { ksize(ptr); } ··· 126 123 127 124 static inline int kasan_module_alloc(void *addr, size_t size) { return 0; } 128 125 static inline void kasan_free_shadow(const struct vm_struct *vm) {} 126 + 127 + static inline int kasan_add_zero_shadow(void *start, unsigned long size) 128 + { 129 + return 0; 130 + } 131 + static inline void kasan_remove_zero_shadow(void *start, 132 + unsigned long size) 133 + {} 129 134 130 135 static inline void kasan_unpoison_slab(const void *ptr) { } 131 136 static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }
+10
kernel/memremap.c
··· 5 5 #include <linux/types.h> 6 6 #include <linux/pfn_t.h> 7 7 #include <linux/io.h> 8 + #include <linux/kasan.h> 8 9 #include <linux/mm.h> 9 10 #include <linux/memory_hotplug.h> 10 11 #include <linux/swap.h> ··· 138 137 mem_hotplug_begin(); 139 138 arch_remove_memory(align_start, align_size, pgmap->altmap_valid ? 140 139 &pgmap->altmap : NULL); 140 + kasan_remove_zero_shadow(__va(align_start), align_size); 141 141 mem_hotplug_done(); 142 142 143 143 untrack_pfn(NULL, PHYS_PFN(align_start), align_size); ··· 241 239 goto err_pfn_remap; 242 240 243 241 mem_hotplug_begin(); 242 + error = kasan_add_zero_shadow(__va(align_start), align_size); 243 + if (error) { 244 + mem_hotplug_done(); 245 + goto err_kasan; 246 + } 247 + 244 248 error = arch_add_memory(nid, align_start, align_size, altmap, false); 245 249 if (!error) 246 250 move_pfn_range_to_zone(&NODE_DATA(nid)->node_zones[ZONE_DEVICE], ··· 275 267 return __va(res->start); 276 268 277 269 err_add_memory: 270 + kasan_remove_zero_shadow(__va(align_start), align_size); 271 + err_kasan: 278 272 untrack_pfn(NULL, PHYS_PFN(align_start), align_size); 279 273 err_pfn_remap: 280 274 err_radix:
+303 -13
mm/kasan/kasan_init.c
··· 17 17 #include <linux/memblock.h> 18 18 #include <linux/mm.h> 19 19 #include <linux/pfn.h> 20 + #include <linux/slab.h> 20 21 21 22 #include <asm/page.h> 22 23 #include <asm/pgalloc.h> 24 + 25 + #include "kasan.h" 23 26 24 27 /* 25 28 * This page serves two purposes: ··· 35 32 36 33 #if CONFIG_PGTABLE_LEVELS > 4 37 34 p4d_t kasan_zero_p4d[MAX_PTRS_PER_P4D] __page_aligned_bss; 35 + static inline bool kasan_p4d_table(pgd_t pgd) 36 + { 37 + return pgd_page(pgd) == virt_to_page(lm_alias(kasan_zero_p4d)); 38 + } 39 + #else 40 + static inline bool kasan_p4d_table(pgd_t pgd) 41 + { 42 + return 0; 43 + } 38 44 #endif 39 45 #if CONFIG_PGTABLE_LEVELS > 3 40 46 pud_t kasan_zero_pud[PTRS_PER_PUD] __page_aligned_bss; 47 + static inline bool kasan_pud_table(p4d_t p4d) 48 + { 49 + return p4d_page(p4d) == virt_to_page(lm_alias(kasan_zero_pud)); 50 + } 51 + #else 52 + static inline bool kasan_pud_table(p4d_t p4d) 53 + { 54 + return 0; 55 + } 41 56 #endif 42 57 #if CONFIG_PGTABLE_LEVELS > 2 43 58 pmd_t kasan_zero_pmd[PTRS_PER_PMD] __page_aligned_bss; 59 + static inline bool kasan_pmd_table(pud_t pud) 60 + { 61 + return pud_page(pud) == virt_to_page(lm_alias(kasan_zero_pmd)); 62 + } 63 + #else 64 + static inline bool kasan_pmd_table(pud_t pud) 65 + { 66 + return 0; 67 + } 44 68 #endif 45 69 pte_t kasan_zero_pte[PTRS_PER_PTE] __page_aligned_bss; 70 + 71 + static inline bool kasan_pte_table(pmd_t pmd) 72 + { 73 + return pmd_page(pmd) == virt_to_page(lm_alias(kasan_zero_pte)); 74 + } 75 + 76 + static inline bool kasan_zero_page_entry(pte_t pte) 77 + { 78 + return pte_page(pte) == virt_to_page(lm_alias(kasan_zero_page)); 79 + } 46 80 47 81 static __init void *early_alloc(size_t size, int node) 48 82 { ··· 87 47 BOOTMEM_ALLOC_ACCESSIBLE, node); 88 48 } 89 49 90 - static void __init zero_pte_populate(pmd_t *pmd, unsigned long addr, 50 + static void __ref zero_pte_populate(pmd_t *pmd, unsigned long addr, 91 51 unsigned long end) 92 52 { 93 53 pte_t *pte = pte_offset_kernel(pmd, addr); ··· 103 63 } 104 64 } 105 65 106 - static void __init zero_pmd_populate(pud_t *pud, unsigned long addr, 66 + static int __ref zero_pmd_populate(pud_t *pud, unsigned long addr, 107 67 unsigned long end) 108 68 { 109 69 pmd_t *pmd = pmd_offset(pud, addr); ··· 118 78 } 119 79 120 80 if (pmd_none(*pmd)) { 121 - pmd_populate_kernel(&init_mm, pmd, 122 - early_alloc(PAGE_SIZE, NUMA_NO_NODE)); 81 + pte_t *p; 82 + 83 + if (slab_is_available()) 84 + p = pte_alloc_one_kernel(&init_mm, addr); 85 + else 86 + p = early_alloc(PAGE_SIZE, NUMA_NO_NODE); 87 + if (!p) 88 + return -ENOMEM; 89 + 90 + pmd_populate_kernel(&init_mm, pmd, p); 123 91 } 124 92 zero_pte_populate(pmd, addr, next); 125 93 } while (pmd++, addr = next, addr != end); 94 + 95 + return 0; 126 96 } 127 97 128 - static void __init zero_pud_populate(p4d_t *p4d, unsigned long addr, 98 + static int __ref zero_pud_populate(p4d_t *p4d, unsigned long addr, 129 99 unsigned long end) 130 100 { 131 101 pud_t *pud = pud_offset(p4d, addr); ··· 153 103 } 154 104 155 105 if (pud_none(*pud)) { 156 - pud_populate(&init_mm, pud, 157 - early_alloc(PAGE_SIZE, NUMA_NO_NODE)); 106 + pmd_t *p; 107 + 108 + if (slab_is_available()) { 109 + p = pmd_alloc(&init_mm, pud, addr); 110 + if (!p) 111 + return -ENOMEM; 112 + } else { 113 + pud_populate(&init_mm, pud, 114 + early_alloc(PAGE_SIZE, NUMA_NO_NODE)); 115 + } 158 116 } 159 117 zero_pmd_populate(pud, addr, next); 160 118 } while (pud++, addr = next, addr != end); 119 + 120 + return 0; 161 121 } 162 122 163 - static void __init zero_p4d_populate(pgd_t *pgd, unsigned long addr, 123 + static int __ref zero_p4d_populate(pgd_t *pgd, unsigned long addr, 164 124 unsigned long end) 165 125 { 166 126 p4d_t *p4d = p4d_offset(pgd, addr); ··· 192 132 } 193 133 194 134 if (p4d_none(*p4d)) { 195 - p4d_populate(&init_mm, p4d, 196 - early_alloc(PAGE_SIZE, NUMA_NO_NODE)); 135 + pud_t *p; 136 + 137 + if (slab_is_available()) { 138 + p = pud_alloc(&init_mm, p4d, addr); 139 + if (!p) 140 + return -ENOMEM; 141 + } else { 142 + p4d_populate(&init_mm, p4d, 143 + early_alloc(PAGE_SIZE, NUMA_NO_NODE)); 144 + } 197 145 } 198 146 zero_pud_populate(p4d, addr, next); 199 147 } while (p4d++, addr = next, addr != end); 148 + 149 + return 0; 200 150 } 201 151 202 152 /** ··· 215 145 * @shadow_start - start of the memory range to populate 216 146 * @shadow_end - end of the memory range to populate 217 147 */ 218 - void __init kasan_populate_zero_shadow(const void *shadow_start, 148 + int __ref kasan_populate_zero_shadow(const void *shadow_start, 219 149 const void *shadow_end) 220 150 { 221 151 unsigned long addr = (unsigned long)shadow_start; ··· 261 191 } 262 192 263 193 if (pgd_none(*pgd)) { 264 - pgd_populate(&init_mm, pgd, 265 - early_alloc(PAGE_SIZE, NUMA_NO_NODE)); 194 + p4d_t *p; 195 + 196 + if (slab_is_available()) { 197 + p = p4d_alloc(&init_mm, pgd, addr); 198 + if (!p) 199 + return -ENOMEM; 200 + } else { 201 + pgd_populate(&init_mm, pgd, 202 + early_alloc(PAGE_SIZE, NUMA_NO_NODE)); 203 + } 266 204 } 267 205 zero_p4d_populate(pgd, addr, next); 268 206 } while (pgd++, addr = next, addr != end); 207 + 208 + return 0; 209 + } 210 + 211 + static void kasan_free_pte(pte_t *pte_start, pmd_t *pmd) 212 + { 213 + pte_t *pte; 214 + int i; 215 + 216 + for (i = 0; i < PTRS_PER_PTE; i++) { 217 + pte = pte_start + i; 218 + if (!pte_none(*pte)) 219 + return; 220 + } 221 + 222 + pte_free_kernel(&init_mm, (pte_t *)page_to_virt(pmd_page(*pmd))); 223 + pmd_clear(pmd); 224 + } 225 + 226 + static void kasan_free_pmd(pmd_t *pmd_start, pud_t *pud) 227 + { 228 + pmd_t *pmd; 229 + int i; 230 + 231 + for (i = 0; i < PTRS_PER_PMD; i++) { 232 + pmd = pmd_start + i; 233 + if (!pmd_none(*pmd)) 234 + return; 235 + } 236 + 237 + pmd_free(&init_mm, (pmd_t *)page_to_virt(pud_page(*pud))); 238 + pud_clear(pud); 239 + } 240 + 241 + static void kasan_free_pud(pud_t *pud_start, p4d_t *p4d) 242 + { 243 + pud_t *pud; 244 + int i; 245 + 246 + for (i = 0; i < PTRS_PER_PUD; i++) { 247 + pud = pud_start + i; 248 + if (!pud_none(*pud)) 249 + return; 250 + } 251 + 252 + pud_free(&init_mm, (pud_t *)page_to_virt(p4d_page(*p4d))); 253 + p4d_clear(p4d); 254 + } 255 + 256 + static void kasan_free_p4d(p4d_t *p4d_start, pgd_t *pgd) 257 + { 258 + p4d_t *p4d; 259 + int i; 260 + 261 + for (i = 0; i < PTRS_PER_P4D; i++) { 262 + p4d = p4d_start + i; 263 + if (!p4d_none(*p4d)) 264 + return; 265 + } 266 + 267 + p4d_free(&init_mm, (p4d_t *)page_to_virt(pgd_page(*pgd))); 268 + pgd_clear(pgd); 269 + } 270 + 271 + static void kasan_remove_pte_table(pte_t *pte, unsigned long addr, 272 + unsigned long end) 273 + { 274 + unsigned long next; 275 + 276 + for (; addr < end; addr = next, pte++) { 277 + next = (addr + PAGE_SIZE) & PAGE_MASK; 278 + if (next > end) 279 + next = end; 280 + 281 + if (!pte_present(*pte)) 282 + continue; 283 + 284 + if (WARN_ON(!kasan_zero_page_entry(*pte))) 285 + continue; 286 + pte_clear(&init_mm, addr, pte); 287 + } 288 + } 289 + 290 + static void kasan_remove_pmd_table(pmd_t *pmd, unsigned long addr, 291 + unsigned long end) 292 + { 293 + unsigned long next; 294 + 295 + for (; addr < end; addr = next, pmd++) { 296 + pte_t *pte; 297 + 298 + next = pmd_addr_end(addr, end); 299 + 300 + if (!pmd_present(*pmd)) 301 + continue; 302 + 303 + if (kasan_pte_table(*pmd)) { 304 + if (IS_ALIGNED(addr, PMD_SIZE) && 305 + IS_ALIGNED(next, PMD_SIZE)) 306 + pmd_clear(pmd); 307 + continue; 308 + } 309 + pte = pte_offset_kernel(pmd, addr); 310 + kasan_remove_pte_table(pte, addr, next); 311 + kasan_free_pte(pte_offset_kernel(pmd, 0), pmd); 312 + } 313 + } 314 + 315 + static void kasan_remove_pud_table(pud_t *pud, unsigned long addr, 316 + unsigned long end) 317 + { 318 + unsigned long next; 319 + 320 + for (; addr < end; addr = next, pud++) { 321 + pmd_t *pmd, *pmd_base; 322 + 323 + next = pud_addr_end(addr, end); 324 + 325 + if (!pud_present(*pud)) 326 + continue; 327 + 328 + if (kasan_pmd_table(*pud)) { 329 + if (IS_ALIGNED(addr, PUD_SIZE) && 330 + IS_ALIGNED(next, PUD_SIZE)) 331 + pud_clear(pud); 332 + continue; 333 + } 334 + pmd = pmd_offset(pud, addr); 335 + pmd_base = pmd_offset(pud, 0); 336 + kasan_remove_pmd_table(pmd, addr, next); 337 + kasan_free_pmd(pmd_base, pud); 338 + } 339 + } 340 + 341 + static void kasan_remove_p4d_table(p4d_t *p4d, unsigned long addr, 342 + unsigned long end) 343 + { 344 + unsigned long next; 345 + 346 + for (; addr < end; addr = next, p4d++) { 347 + pud_t *pud; 348 + 349 + next = p4d_addr_end(addr, end); 350 + 351 + if (!p4d_present(*p4d)) 352 + continue; 353 + 354 + if (kasan_pud_table(*p4d)) { 355 + if (IS_ALIGNED(addr, P4D_SIZE) && 356 + IS_ALIGNED(next, P4D_SIZE)) 357 + p4d_clear(p4d); 358 + continue; 359 + } 360 + pud = pud_offset(p4d, addr); 361 + kasan_remove_pud_table(pud, addr, next); 362 + kasan_free_pud(pud_offset(p4d, 0), p4d); 363 + } 364 + } 365 + 366 + void kasan_remove_zero_shadow(void *start, unsigned long size) 367 + { 368 + unsigned long addr, end, next; 369 + pgd_t *pgd; 370 + 371 + addr = (unsigned long)kasan_mem_to_shadow(start); 372 + end = addr + (size >> KASAN_SHADOW_SCALE_SHIFT); 373 + 374 + if (WARN_ON((unsigned long)start % 375 + (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) || 376 + WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE))) 377 + return; 378 + 379 + for (; addr < end; addr = next) { 380 + p4d_t *p4d; 381 + 382 + next = pgd_addr_end(addr, end); 383 + 384 + pgd = pgd_offset_k(addr); 385 + if (!pgd_present(*pgd)) 386 + continue; 387 + 388 + if (kasan_p4d_table(*pgd)) { 389 + if (IS_ALIGNED(addr, PGDIR_SIZE) && 390 + IS_ALIGNED(next, PGDIR_SIZE)) 391 + pgd_clear(pgd); 392 + continue; 393 + } 394 + 395 + p4d = p4d_offset(pgd, addr); 396 + kasan_remove_p4d_table(p4d, addr, next); 397 + kasan_free_p4d(p4d_offset(pgd, 0), pgd); 398 + } 399 + } 400 + 401 + int kasan_add_zero_shadow(void *start, unsigned long size) 402 + { 403 + int ret; 404 + void *shadow_start, *shadow_end; 405 + 406 + shadow_start = kasan_mem_to_shadow(start); 407 + shadow_end = shadow_start + (size >> KASAN_SHADOW_SCALE_SHIFT); 408 + 409 + if (WARN_ON((unsigned long)start % 410 + (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) || 411 + WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE))) 412 + return -EINVAL; 413 + 414 + ret = kasan_populate_zero_shadow(shadow_start, shadow_end); 415 + if (ret) 416 + kasan_remove_zero_shadow(shadow_start, 417 + size >> KASAN_SHADOW_SCALE_SHIFT); 418 + return ret; 269 419 }