You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无源码修改通过GDB单线程暂停自动复现多线程竞态条件

竞态条件自动化复现方案

GDB 原生支持你需要的调度控制能力

你设想的逻辑完全可以通过GDB的非停止模式实现,不需要修改源码,也不需要手动交互,可直接作为测试脚本运行。
你提到的set scheduler-locking = on是「仅运行当前选中线程,暂停其余所有线程」,而你需要的「仅暂停命中断点的写线程1,其余线程全部继续运行」是GDB非停止模式的默认行为,不需要反向配置。
可直接使用如下无交互GDB脚本:

# 开启非停止模式:仅断点命中的线程会被暂停,其余线程不受影响继续调度
set non-stop on
# 关闭交互分页,避免脚本阻塞
set pagination off
# 加载目标二进制程序
file ./your_target_binary
# 在线程1执行写入内存地址A的指令位置下断点
# 可直接写指令地址:break *0x401589,也可写 函数名+偏移:break write_func+27
break *<线程1写地址A的指令位置>
# 配置断点触发后自动执行的逻辑
commands
  # 向地址A写入非法垃圾值,比如构造不可访问的指针值触发段错误
  set *(uint64_t*)<内存地址A> = 0xdeadbeefdeadbeef
  # 保持当前线程(线程1)暂停,继续运行其余所有线程
  continue -a
end
# 捕获段错误信号,确认竞态触发成功
catch signal SIGSEGV
commands
  print "竞态复现成功:读线程读取到垃圾数据触发段错误"
  quit 1
end
# 启动程序
run

脚本运行直接执行gdb -x race_trigger.gdb -batch即可,全程无人工操作,退出码1代表复现成功,0代表未触发,可直接接入集成测试流程。
如果目标程序开启了ASLR,不需要修改程序,可在脚本中先启动程序停在入口点,自动通过/proc/<pid>/maps计算堆、栈、动态库的实际基址,再换算出断点位置和地址A的实际内存地址即可。

非GDB轻量方案(更适合CI集成)

如果不想引入GDB依赖,可选择以下两种无源码修改的方案:

  • 基于ptrace实现轻量注入脚本:用Python的python-ptrace库可以实现和上述GDB逻辑完全一致的效果,代码量不超过100行,运行开销远低于GDB。核心逻辑为:启动目标进程后在对应指令位置写入软断点指令,命中断点后仅将对应线程置为TASK_STOPPED状态,修改地址A的内容为垃圾值后,其余线程继续运行,监听进程收到的SIGSEGV信号即可判断复现成功。
  • 动态插桩注入:如果目标程序逻辑复杂、断点位置不好定位,可使用动态插桩框架在写入地址A的指令位置插入探针,探针触发时阻塞当前写线程、修改目标内存值后放行其余线程,稳定性更高。

为了保证100%复现率,可在放行其余线程后,临时将读线程2的调度优先级设置为最高,避免系统调度波动导致读线程未及时读取垃圾值,完全消除测试flaky问题。

内容的提问来源于stack exchange,提问作者Yunus King

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:39:15