Glibc-2.24中pthread_mutex_lock传入0x2无效地址的排查与调试咨询
POSIX线程崩溃问题排查(mutex参数异常为0x2)
问题背景
当前排查一个信号处理线程崩溃问题,崩溃发生在POSIX pthread API __GI___pthread_mutex_lock(mutex=0x2),mutex参数被意外设置为无效地址0x2。以下是core文件的GDB回溯信息:
Thread 2 (LWP 26849): #0 __nptl_deallocate_tsd () at pthread_create.c:200 #1 0xf72af5da in start_thread (arg=0x0) at pthread_create.c:346 #2 0xf698d5f2 in ?? () at ../sysdeps/unix/sysv/linux/arm/clone.S:86 from /tools/toolchain/arm/armbe-none-linux-gnueabi_2017/usr/armeb-buildroot-linux-gnueabi/sysroot/lib32/libc.so.6 Backtrace stopped: previous frame identical to this frame (corrupt stack?) Thread 1 (LWP 26850): #0 __libc_do_syscall () at ../sysdeps/unix/sysv/linux/arm/libc-do-syscall.S:47 #1 0xf691d2d2 in __libc_signal_restore_set (set=0xf65fe560) at ../sysdeps/unix/sysv/linux/nptl-signals.h:79 #2 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 #3 0xf691dfba in __GI_abort () at abort.c:89 #4 0xf691861a in __assert_fail_base (fmt=0xf69c5f80 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0xf72b8b1c "mutex->__data.__owner == 0", assertion@entry=0x2 <error: Cannot access memory at address 0x2>, file=file@entry=0xf65fef50 "", line=1, line@entry=4137570440, function=function@entry=0xf72b8b90 <__PRETTY_FUNCTION__.10382> "__pthread_mutex_lock") at assert.c:92 #5 0xf69186ae in __GI___assert_fail (assertion=0x2 <error: Cannot access memory at address 0x2>, file=0xf65fef50 "", line=4137570440, line@entry=81, function=0xf72b8b90 <__PRETTY_FUNCTION__.10382> "__pthread_mutex_lock") at assert.c:101 #6 0xf72b15d6 in __GI___pthread_mutex_lock (mutex=0x2) at pthread_mutex_lock.c:81 #7 0xf6a29820 in handle_process_exit () at signal_handler.c:258 #8 0xf6a29ba0 in signal_handler (param=0) at signal_handler.c:331 #9 0xf6a25e98 in task_init (param=0x2efe24) at task/tasks.c:403 #10 0xf72af5ce in start_thread (arg=0x1) at pthread_create.c:335 #11 0xf698d5f2 in ?? () at ../sysdeps/unix/sysv/linux/arm/clone.S:86 from /tools/toolchain/arm/armbe-none-linux-gnueabi_2017/usr/armeb-buildroot-linux-gnueabi/sysroot/lib32/libc.so.6 ---Type <return> to continue, or q <return> to quit--- Backtrace stopped: previous frame identical to this frame (corrupt stack?) (gdb) f 7 #7 0xf6a29820 in handle_process_exit () at sig/silpi_signal_handler.c:258 258 sig/signal_handler.c: No such file or directory. (gdb) p @tmr_exit_mutex Unknown address space specifier: "tmr_exit_mutex" (gdb) p &tmr_exit_mutex $1 = (pthread_mutex_t *) 0xf6a43fb8 <tmr_exit_mutex> (gdb) f 6 #6 0xf72b15d6 in __GI___pthread_mutex_lock (mutex=0x2) at pthread_mutex_lock.c:81 81 pthread_mutex_lock.c: No such file or directory. (gdb) p mutex $2 = (pthread_mutex_t *) 0x2 (gdb) p &tmr_exit_mutex $3 = (pthread_mutex_t *) 0xf6a43fb8 <tmr_exit_mutex> (gdb) ptype tmr_exit_mutex type = union { struct __pthread_mutex_s __data; char __size[24]; long __align; } (gdb) p tmr_exit_mutex $4 = {__data = {__lock = 0, __count = 0, __owner = 0, __kind = 0, __nusers = 0, {__spins = 0, __list = {__next = 0x0}}}, __size = '\000' <repeats 23 times>, __align = 0} (gdb)
注:handle_process_exit()中调用pthread_mutex_lock(&tmr_exit_mutex),tmr_exit_mutex地址0xf6a43fb8正常,但传入__GI___pthread_mutex_lock后变为0x2。查看Glibc-2.24的pthread_mutex_lock.c源码,地址损坏发生在第81行,第65行的assert (sizeof(mutex->__size) >= sizeof(mutex->__data));未触发。
1. 可能的原因
- 栈帧损坏:回溯中两个线程均提示
corrupt stack?,说明调用栈已被破坏。handle_process_exit调用pthread_mutex_lock时,栈上的参数(&tmr_exit_mutex)被覆盖为0x2,大概率是信号处理函数中存在栈溢出、内存越界写操作,覆盖了函数调用的参数存储区域。 - 寄存器值被篡改:ARM架构下函数参数通常通过寄存器(如r0)传递,如果
handle_process_exit调用pthread_mutex_lock前,寄存器r0被意外修改(比如其他线程非法内存访问、硬件异常、代码错误操作),会导致传入的mutex地址变为0x2。 - 信号异步干扰:崩溃发生在信号处理线程,若信号处理过程中触发其他异步信号,或在信号处理函数中调用了非异步安全的操作,可能破坏栈或寄存器状态,导致参数传递异常。
- 线程局部存储(TLS)损坏:Thread2的回溯涉及
__nptl_deallocate_tsd(线程局部存储释放),TLS区域损坏可能间接影响主线程的栈或参数传递逻辑。 - 内存越界写全局区域:虽然
tmr_exit_mutex地址正常,但如果有代码越界写入破坏了handle_process_exit中引用该变量的地址计算逻辑,也可能导致参数异常,但这种概率较低。
2. GDB及开源工具调试方法
- GDB栈帧与寄存器分析:
- 进入
handle_process_exit栈帧(frame 7),用x/16x $sp查看栈内存,定位原本存放&tmr_exit_mutex的位置,检查是否被修改为0x2,同时观察相邻内存的异常值,判断越界写的来源。 - 执行
info registers查看ARM寄存器状态,重点确认调用pthread_mutex_lock前r0寄存器的值是否等于&tmr_exit_mutex,定位参数何时被篡改。 - 启用
set backtrace past-main和set backtrace past-entry,尝试获取更完整的栈回溯,确认栈损坏的起始点。
- 进入
- 内存错误检测工具:
- 使用
valgrind --tool=memcheck运行程序,检测内存越界、栈溢出等问题,重点关注信号处理函数中的内存操作。 - 用
AddressSanitizer编译程序(添加-fsanitize=address参数),精准定位内存越界写的位置,适合支持该工具的目标平台。
- 使用
- 断点与观察点设置:
- 在
handle_process_exit调用pthread_mutex_lock的位置设置断点,执行到断点时检查参数是否正确,再单步进入pthread_mutex_lock,观察参数何时变为0x2。 - 对栈上参数的存储地址设置写观察点(如
watch *$sp+4,根据ARM栈传递规则调整偏移),触发时查看调用栈,找到修改该地址的代码。
- 在
- 反汇编分析:
- 对
handle_process_exit和pthread_mutex_lock执行disassemble命令,查看参数传递的汇编逻辑(寄存器/栈传递),分析参数被篡改的具体环节。
- 对
3. 无法复现时需收集的日志/信息
- 完整Core文件:确保系统开启core dump(执行
ulimit -c unlimited),收集崩溃时的core文件,包含进程内存、寄存器、栈帧的完整状态。 - 系统日志:收集
/var/log/messages、dmesg输出,排查是否有硬件错误(如内存ECC异常、总线故障)、OOM Killer记录或其他进程的异常干扰。 - 进程业务日志:
- 添加日志记录
handle_process_exit调用pthread_mutex_lock前&tmr_exit_mutex的地址,以及函数的进入/退出日志,确认参数传递前的状态。 - 记录崩溃前的信号处理情况,比如收到的信号类型、处理次数、相关业务操作。
- 添加日志记录
- 线程状态快照:定期用
pstack/gstack工具收集线程栈信息,或在信号处理函数中添加线程栈打印逻辑,保存崩溃前的线程运行状态。 - 环境与编译信息:记录编译器版本、Glibc版本(此处为2.24)、目标平台架构(ARM)、工具链版本,排查是否存在已知的库或编译器bug。
- 内存映射信息:收集崩溃进程的
/proc/[pid]/maps文件,查看内存布局,确认0x2地址所在区域及tmr_exit_mutex所在内存段的权限。
内容的提问来源于stack exchange,提问作者strikersps
相关产品推荐
相关产品推荐

