嵌入式应用堆内存随机损坏
嵌入式应用堆内存随机损坏 —— 排查过程2026年6月 · ARM Linux 嵌入式设备一、问题全景Symptom: UI freeze after app startup No crash, no core dump generated │ ▼ Initial hypothesis: serial data corrupting store_data.alloc │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ [Dead end 1] [Dead end 2] Mid: heap random GDB hardware Valgrind/ASan corruption watchpoint toolchain QMap/QString board OOM not supported rewritten │ ┌──────────────────┼──────────────────┐ │ │ ▼ ▼ [Dead end 3] Root cause confirmed: Suspecting DeviceMonitor SharedDataStore readLen bug is the cross-thread access ONLY cause with NO lock │ ┌──────┴──────┐ ▼ ▼ Fix: recursive Build abc123def mutex protecting deployed, pending all shared data board verification二、排查时间线Day 1— 用户报告app 启动不久 alloc 被踩store_data 扩容时 abort。开始 GDB 远程调试。弯路— 尝试在 ARM 板子上用 GDB 硬件 watchpoint 抓 alloc 写入者。板子约 1GB 内存GDB 加载约 100MB 完整调试符号后吃掉 500MB板子 OOM 红屏。硬件断点不可用软件断点单步执行 CPU 99% 卡死。Day 2— 写 heap_monitor.sh 监控脚本用 ELF 符号表提取 vtable 地址GDB 扫堆定位 DeviceMonitor/DeviceController 对象轮询 alloc 字段。脚本能跑但 GDB attach/detach 太频繁会打挂板子。弯路— 对比 dev_monitor.cpp 和 dev_controller.cpp发现前者 COMM_MODE_TEXT0 分支用 bytesAvailable 而非 readLen 构造 QByteArray。修改后编译部署以为修好了。但这是表层问题。Day 3— 换板子release 版复现 UI 卡死。GDB attach 拿到主线程 backtraceMainWindow::refresh_SystemTimeSlot │ (timer-triggered system time refresh) ▼ QLabel::setText │ ▼ Qt internal memory allocation │ ▼ malloc / realloc │ ▼ glibc malloc_consolidate │ detects heap corruption ▼ abort() │ ▼ libSegFault.so catches SIGABRT │ signal handler calls malloc again ▼ futex deadlock │ all threads freeze one by one ▼ [UI completely frozen]关键发现— GDB frame 12/13 查看局部变量showText.d 0x19a5ef50恰好等于thisMainWindow 地址。QString 的内部 d 指针被写成了对象指针这不可能是串口垃圾数据能做到的——是结构性写错。Day 4— 重新审视代码架构。发现 AppModule::initObject() 中 data_store 的传递模式Main Thread SharedDataStore Worker Thread (AppModule) (data_store) (WorkerTask) │ │ │ │──new SharedStore──▶│ │ │ │ │ │──new WorkerTask────┼─────────────────────▶│ │ (passes data_store ptr) │ │ │ │ │──new MainWindow─────┼─▶ │ │ (data_store ptr) │ │ │ │ │ │ │◀──Get_Title() read──│ │ │ QMap access │ │ │ │ │──setValueSlot()────▶│ │ │ write QString │ │ │ │ │ │ ⚠ RACE CONDITION ⚠ │ │ Ref-count corruption → heap damage │WorkerTask 工作线程通过 data_store 调用的函数Get_Title Get_Value Get_Uint Get_ValueUint Get_MinValue Get_MaxValue Get_OwnerWindow Get_OwnerPage Get_HourToMin Get_ProcessTime Get_RemainingTime Get_TimingStatus getLangEnabled getTestTitle getRunStep get_UIBoardSelection getHasUserInputData getSelfTest isChannelAorBOn isExtendParam0On isExtendParam1On SendToDevice(int,QString,DataSendType)全部读取 sensor_data[] 或 active_locale与主线程的写入并发均存在竞态。┌── Main Thread ──┐ ┌── Worker Thread ──┐ │ │ │ │ │ setValueSlot() │ │ Get_Value() │ │ writes to │ RACE! │ reads from │ │ sensor_data[] │◄────────▶│ sensor_data[] │ │ .Cur_Value │ │ .Cur_Value │ │ │ │ │ │ Set_PumpSpeed() │ │ Get_Title() │ │ writes to │ RACE! │ reads │ │ sensor_data[] │◄────────▶│ active_locale │ │ │ │ QMap │ └──────────────────┘ └────────────────────┘ QString implicit sharing → ref-count is NOT atomic → double-free / use-after-freeDay 5— Valgrind/ASan 尝试均失败。Yocto 交叉编译链不带 libasanGCC 7.3 编译时未启用 sanitizer。x86 VM 上 Qt5 只有运行库没有开发包且 app 依赖通信口硬件无法直接跑。修复— 在 SharedDataStore 中启用已有 QMutex 改为递归锁// datastore.h: mutable QMutex mutex;// datastore.cpp constructor: mutex(QMutex::Recursive)// 每个读写共享数据的 public 函数首行QMutexLockerlocker(mutex);覆盖30 个函数setValueSlot 原有手动 tryLock/unlock 替换为 QMutexLocker。纯 emit 信号函数和 QSettings 访问函数无需加锁。三、技术总结错误思路为什么是弯路最终结论GDB 硬件 watchpoint 抓 alloc 写入指令ARM 板性能不足硬件断点不可用软件断点单步 CPU 99% 卡死改用轮询监控脚本 GDB 短暂 attach只修 DeviceMonitor 的 readLen bug这是真实 bug但不是唯一原因修了之后旧板子 release 版仍崩readLen 修复有价值但堆损坏根因更深怀疑串口垃圾数据污染堆解释不了 showText.d this 这种结构性写错跨线程 QString 引用计数竞态才能产生这种把 A 的地址写到 B的现象尝试 Valgrind / ASanYocto 工具链不支持x86 VM 环境不完整直接看代码逻辑找到 root cause四、补充隐患char 数组代码中大量使用裸char buf[2048]而非 QByteArray 或 std::arrayDeviceMonitor::recv_buffer[2048]— 类成员位于 store_data 之后仅 8 字节残留旧数据是 readLen bug 的直接原因DeviceController::recvBuffer[2048]— 栈局部变量每次重新分配避免了残留是正确写法建议统一使用 QByteArray readLen 精确控制消除硬编码长度。五、产出产出路径SharedDataStore 互斥锁修复datastore.cpp datastore.h30个函数已编译二进制BuildID abc123defVM: /build/product/app/AppModule监控脚本 heap_monitor.shtools/heap_monitor.sh不依赖 .debug 文件自适应任意版本分析文档tools/跨线程SharedData分析.md