内存管理-47-Poison荼毒
基于msm-5.4
一、简介
主要是利用头文件定义一些宏,若是向使用物理页的Poison需要使能开关 CONFIG_PAGE_POISONING, 它实现比较简单,只有一个较短的C文件。相关文件:
include/linux/poison.h
mm/page_poison.c。
C文件中对外只有一个接口, 就是 kernel_poison_pages(),它只在物理页分配释放中使用。
poison.h 头文件中还定义了一些其它荼毒值,
//链表list #define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) //0xdead000000000100UL #define LIST_POISON2 ((void *) 0x122 + POISON_POINTER_DELTA) //0xdead000000000122UL //定时器timer #define TIMER_ENTRY_STATIC ((void *) 0x300 + POISON_POINTER_DELTA) //0xdead000000000300UL //page_poison.c #define PAGE_POISON 0xaa //page_alloc.c #define TAIL_MAPPING ((void *) 0x400 + POISON_POINTER_DELTA) //0xdead000000000400UL //slab.c #define RED_INACTIVE 0x09F911029D74E35BULL /* when obj is inactive */ #define RED_ACTIVE 0xD84156C5635688C0ULL /* when obj is active */ #define SLUB_RED_INACTIVE 0xbb #define SLUB_RED_ACTIVE 0xcc #define POISON_INUSE 0x5a /* for use-uninitialised poisoning */ //对应ASCII就是'Z' #define POISON_FREE 0x6b /* for use-after-free poisoning */ //对应ASCII就是'k' #define POISON_END 0xa5 /* end-byte of poisoning */ //超出ASCII码范围 //init.c #define POISON_FREE_INITMEM 0xcc //fs/journal.c #define JBD_POISON_FREE 0x5b #define JBD2_POISON_FREE 0x5c //dmapool.c #define POOL_POISON_FREED 0xa7 /* !inuse */ #define POOL_POISON_ALLOCATED 0xa9 /* !initted */ //mutexes #define MUTEX_DEBUG_INIT 0x11 #define MUTEX_DEBUG_FREE 0x22 #define MUTEX_POISON_WW_CTX ((void *) 0x500 + POISON_POINTER_DELTA) //0xdead000000000500UL //security #define KEY_DESTROY 0xbd
二、物理页分配释放中的Poison
调用路径:
prep_new_page //page_alloc.c split_map_pages //compaction.c unset_migratetype_isolate //page_isolation.c post_alloc_hook //page_alloc.c 分配物理页,传参 (page, 1 << order, 1) 检查page flip __free_pages_ok //page_alloc.c free_pcp_prepare //page_alloc.c free_pages_prepare //page_alloc.c 释放物理页,传参 (page, 1 << order, 0) 进行荼毒 kernel_poison_pages //page_poison.c
分配释放内存时被荼毒成什么值由 PAGE_POISON 宏指定,默认是 0xaa.
1. kernel_poison_pages()
void kernel_poison_pages(struct page *page, int numpages, int enable) { /* debug版本默认是true,不退出 */ if (!page_poisoning_enabled()) return; /* 分配出去时是unpoison,释放后进行poison */ if (enable) unpoison_pages(page, numpages); //这里面若是有pageflip的话会进行打印的 else poison_pages(page, numpages); /* 将整个page清成0xaa */ }
1.1 unpoison_pages()
static void unpoison_pages(struct page *page, int n) { int i; /* 对对所有分配出来的物理页都执行unpoison */ for (i = 0; i < n; i++) unpoison_page(page + i); } static void unpoison_page(struct page *page) { void *addr; /* 关抢占关page_fault并获取此page对应物理内存的虚拟地址 */ addr = kmap_atomic(page); /* * 翻译: 启用页面投毒功能后,所有释放给buddy的页面都会被投毒。因此,页面是否被投毒没有额外的检查。 * * 这里会遍历每一个字节检查是否有page flip, 性能有害 #### */ check_poison_mem(page, addr, PAGE_SIZE); /* 只是开抢占开page_fault */ kunmap_atomic(addr); }
1.1.1 check_poison_mem()
/* unpoison_page: (page, addr, PAGE_SIZE) 参数page和page对应的虚拟地址 */ static void check_poison_mem(struct page *page, unsigned char *mem, size_t bytes) { static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 10); unsigned char *start; unsigned char *end; /* 默认不使能,不退出 */ if (IS_ENABLED(CONFIG_PAGE_POISONING_NO_SANITY)) return; /* * 定义在string.c中,返回除 0xaa 以外的第一个字符的地址,如果整个缓冲区仅包含 0xaa, * 则返回 NULL。若成功检查通过,这里就返回了。 */ start = memchr_inv(mem, PAGE_POISON, bytes); if (!start) return; /* 再从后往前找第一个不是0xaa的位置,保存在end指针中 */ for (end = mem + bytes - 1; end > start; end--) { if (*end != PAGE_POISON) break; } /* 默认5秒内最多打印10条, 返回0表示速率限制了,不应该做打印,直接返回,否则进行报错 */ if (!__ratelimit(&ratelimit)) return; /* 判断是否只是一个bit flip了。#### 这里只打印了页的物理地址,还可以将页内偏移给打印出来 */ else if (start == end && single_bit_flip(*start, PAGE_POISON)) pr_err("pagealloc: single bit error on page with phys start 0x%lx\n", (unsigned long)page_to_phys(page)); else /* 多个字节或一个字节的多个bit flip了,报这个错 */ pr_err("pagealloc: memory corruption on page with phys start 0x%lx\n", (unsigned long)page_to_phys(page)); /* dump此page内存在flip的范围的物理内存的值 */ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, start, end - start + 1, 1); /* 默认不使能 CONFIG_PANIC_ON_DATA_CORRUPTION, 这里并不会死机 */ BUG_ON(PANIC_CORRUPTION); /* 只是打印一个栈回溯 */ dump_stack(); }
1.2 poison_pages()
static void poison_pages(struct page *page, int n) { int i; /* 遍历所有物理页,对每个物理页都进行荼毒 */ for (i = 0; i < n; i++) poison_page(page + i); } /* 将整个page清成0xaa */ static void poison_page(struct page *page) { void *addr = kmap_atomic(page); //返回page对应的pfn的虚拟地址 /* KASAN still think the page is in-use, so skip it. */ kasan_disable_current(); //current->kasan_depth--; memset(addr, PAGE_POISON, PAGE_SIZE); //0xaa 整个page memset为0xaa kasan_enable_current(); //current->kasan_depth++; kunmap_atomic(addr); }
三、总结
1. 对物理页内存进行荼毒,释放内存页时将整个内存空间设置为0xaa,分配物理页时检测pageflip,但是负载较重,影响性能,默认只有debug版本才会使能 CONFIG_PAGE_POISONING。
posted on 2025-06-10 16:21 Hello-World3 阅读(81) 评论(0) 收藏 举报