162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/kernel.h> 362306a36Sopenharmony_ci#include <linux/string.h> 462306a36Sopenharmony_ci#include <linux/mm.h> 562306a36Sopenharmony_ci#include <linux/mmdebug.h> 662306a36Sopenharmony_ci#include <linux/highmem.h> 762306a36Sopenharmony_ci#include <linux/poison.h> 862306a36Sopenharmony_ci#include <linux/ratelimit.h> 962306a36Sopenharmony_ci#include <linux/kasan.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cibool _page_poisoning_enabled_early; 1262306a36Sopenharmony_ciEXPORT_SYMBOL(_page_poisoning_enabled_early); 1362306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(_page_poisoning_enabled); 1462306a36Sopenharmony_ciEXPORT_SYMBOL(_page_poisoning_enabled); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int __init early_page_poison_param(char *buf) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci return kstrtobool(buf, &_page_poisoning_enabled_early); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ciearly_param("page_poison", early_page_poison_param); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void poison_page(struct page *page) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci void *addr = kmap_atomic(page); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* KASAN still think the page is in-use, so skip it. */ 2762306a36Sopenharmony_ci kasan_disable_current(); 2862306a36Sopenharmony_ci memset(kasan_reset_tag(addr), PAGE_POISON, PAGE_SIZE); 2962306a36Sopenharmony_ci kasan_enable_current(); 3062306a36Sopenharmony_ci kunmap_atomic(addr); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid __kernel_poison_pages(struct page *page, int n) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (i = 0; i < n; i++) 3862306a36Sopenharmony_ci poison_page(page + i); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic bool single_bit_flip(unsigned char a, unsigned char b) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned char error = a ^ b; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return error && !(error & (error - 1)); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void check_poison_mem(struct page *page, unsigned char *mem, size_t bytes) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 10); 5162306a36Sopenharmony_ci unsigned char *start; 5262306a36Sopenharmony_ci unsigned char *end; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci start = memchr_inv(mem, PAGE_POISON, bytes); 5562306a36Sopenharmony_ci if (!start) 5662306a36Sopenharmony_ci return; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci for (end = mem + bytes - 1; end > start; end--) { 5962306a36Sopenharmony_ci if (*end != PAGE_POISON) 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (!__ratelimit(&ratelimit)) 6462306a36Sopenharmony_ci return; 6562306a36Sopenharmony_ci else if (start == end && single_bit_flip(*start, PAGE_POISON)) 6662306a36Sopenharmony_ci pr_err("pagealloc: single bit error\n"); 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci pr_err("pagealloc: memory corruption\n"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, start, 7162306a36Sopenharmony_ci end - start + 1, 1); 7262306a36Sopenharmony_ci dump_stack(); 7362306a36Sopenharmony_ci dump_page(page, "pagealloc: corrupted page details"); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void unpoison_page(struct page *page) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci void *addr; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci addr = kmap_atomic(page); 8162306a36Sopenharmony_ci kasan_disable_current(); 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Page poisoning when enabled poisons each and every page 8462306a36Sopenharmony_ci * that is freed to buddy. Thus no extra check is done to 8562306a36Sopenharmony_ci * see if a page was poisoned. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci check_poison_mem(page, kasan_reset_tag(addr), PAGE_SIZE); 8862306a36Sopenharmony_ci kasan_enable_current(); 8962306a36Sopenharmony_ci kunmap_atomic(addr); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_civoid __kernel_unpoison_pages(struct page *page, int n) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int i; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for (i = 0; i < n; i++) 9762306a36Sopenharmony_ci unpoison_page(page + i); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#ifndef CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC 10162306a36Sopenharmony_civoid __kernel_map_pages(struct page *page, int numpages, int enable) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci /* This function does nothing, all work is done via poison pages */ 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci#endif 106