glibc2.18 PowerPC平台malloc崩溃:_int_malloc传NULL参数异常求助
遇到malloc异常崩溃,从glibc2.18的__libc_malloc源码逻辑来看,调用_int_malloc前已经通过if(!ar_ptr) return 0;判断ar_ptr不为NULL,但gdb回溯结果显示_int_malloc的第一个参数av=0x0(NULL),出现明显矛盾。
环境信息
- glibc版本:2.18
- CPU架构:PowerPC
gdb回溯信息
#13 _int_malloc (av=av@entry=0x0, bytes=bytes@entry=18) at malloc.c:3300 fb = <optimized out> pp = <optimized out> nb = 24 idx = 1 bin = <optimized out> victim = <optimized out> size = <optimized out> victim_index = <optimized out> remainder = <optimized out> remainder_size = <optimized out> block = <optimized out> bit = <optimized out> map = <optimized out> fwd = <optimized out> bck = <optimized out> errstr = 0x0 __func__ = "_int_malloc" #14 0x0fec80cc in __GI___libc_malloc (bytes=18) at malloc.c:2869 ar_ptr = 0x0 victim = <optimized out> hook = <optimized out> __func__ = "__libc_malloc"
glibc2.18对应__libc_malloc源码片段
void* __libc_malloc(size_t bytes) { mstate ar_ptr; void *victim; void *(*hook) (size_t, const void *) = force_reg (__malloc_hook); if (__builtin_expect (hook != NULL, 0)) return (*hook)(bytes, RETURN_ADDRESS (0)); arena_lookup(ar_ptr); arena_lock(ar_ptr, bytes); if(!ar_ptr) return 0; victim = _int_malloc(ar_ptr, bytes); if(!victim) { ar_ptr = arena_get_retry(ar_ptr, bytes); if (__builtin_expect(ar_ptr != NULL, 1)) { victim = _int_malloc(ar_ptr, bytes); (void)mutex_unlock(&ar_ptr->mutex); } } else (void)mutex_unlock(&ar_ptr->mutex); assert(!victim || chunk_is_mmapped(mem2chunk(victim)) || ar_ptr == arena_for_chunk(mem2chunk(victim))); return victim; }
矛盾原因分析
1. 内存篡改(栈/堆 corruption)
在arena_lookup(ar_ptr)获取有效ar_ptr后,到if(!ar_ptr) return 0;之间的代码(比如arena_lock执行过程)可能触发了内存越界写入,直接篡改了栈上的ar_ptr变量,使其变为NULL。PowerPC架构的栈布局或编译器对栈变量的分配方式,可能让ar_ptr更容易被相邻内存的越写影响。
2. 编译器优化导致逻辑重排
虽然源码中先判断if(!ar_ptr)再调用_int_malloc,但开启O2/O3等优化级别时,编译器可能对代码进行重排:比如认为ar_ptr在判断后不会被修改,提前生成_int_malloc的调用指令,或者将判断逻辑合并到其他代码中,导致实际执行时绕过了NULL检查。
3. 多线程竞态条件
多线程环境下,arena的全局状态可能被其他线程破坏:比如其他线程在当前线程的arena_lock到if(!ar_ptr)之间,修改了arena相关的内存,导致当前线程的ar_ptr变为NULL。尽管glibc的arena机制自带锁,但不排除PowerPC架构下锁实现存在bug,或者自定义内存钩子、第三方库修改了arena逻辑,破坏了锁的有效性。
4. 栈帧破坏导致回溯失真
实际ar_ptr不为NULL,但栈帧被内存篡改破坏,导致gdb读取参数时获取到错误的NULL值。不过这种情况概率较低,因为回溯中__libc_malloc的ar_ptr也显示为0x0,说明栈中该变量确实被设置为NULL。
内容的提问来源于stack exchange,提问作者shuaishuai

