异常现场:
当你在SYS_KERNEL_LOG里看到如下log,那么就属于BUG at check_bytes_and_report一类问题了:
<6>[ 492.558572]-(0)[1163:system_server]=============================================================================
<6>[ 492.558599]-(0)[1163:system_server]BUG kmalloc-128 (Tainted: P W O ): Poison overwritten |
代码位置:
kernel-3.18/mm/slub.c
static int check_bytes_and_report(struct kmem_cache *s, struct page *page, u8 *object, char *what, u8 *start, unsigned int value, unsigned int bytes)
{
u8 *fault;
u8 *end;
fault = memchr_inv(start, value, bytes);
if (!fault)
return 1;
end = start + bytes;
while (end > fault && end[-1] == value)
end--;
slab_bug(s, "%s overwritten", what);
pr_err("INFO: 0x%p-0x%p. First byte 0x%x instead of 0x%x\n", fault, end - 1, fault[0], value);
print_trailer(s, page, object);
BUG(); /* 这里发生KE */
restore_bytes(s, what, value, fault, end);
return 0;
}
问题解读:
- use after free。通过kmalloc()申请内存,用完之后kfree()释放,但是后面又再使用了这块释放的内存。
- 内存踩坏。
- 通过kmalloc()申请的内存,使用时超出了当时申请的尺寸,将不属于你的内存踩坏了。
- 其他模块意外将slub空闲/使用中的内存踩坏。
- double free。对一块kmalloc()申请的内存连续调用2次kfree()函数。
- HW故障。比如DRAM/CPU不稳定,导致原有的内存发生跳变,bitflip。上面的例子就是发生了bitflip,6b跳变为69了。
如何检查slub里是否发生异常呢?这就需要额外的内存做守卫了,kernel已有这样的功能,只要打开CONFIG_SLUB_DEBUG,kfree()后的内存会被格式化成6b,还有red zone格式化成bb,padding为5a,还有每次申请和释放都会记录调用栈,记录谁申请/释放的。在每次的内存申请都会做相关检查,如果出现故障,就会抛出对应的log并主动调用BUG()
问题解决:
- use after free。需要通过slub记录的调用栈找凶手,检查代码逻辑才行。
- 内存踩坏。通过slub记录的调用栈或采用MMU保护,请参考:[FAQ14614]如何用MMU保护buddy system?
- double free。从slub记录的调用栈可以很明显看出。
- bitflip。直接要硬件交叉比对(CPU/DRAM),排查硬件故障,上面的log的例子直接HW交叉比对。