struct 成员上的 free() 仅在 Debug 中导致 Hardfault

Posted

技术标签:

【中文标题】struct 成员上的 free() 仅在 Debug 中导致 Hardfault【英文标题】:free() on struct member causes Hardfault only in Debug 【发布时间】:2019-06-13 13:57:17 【问题描述】:

我正在研究 STM32F7。 当我在以下(简化)代码中点击free() 时触发了硬故障:

typedef struct

    uint8_t size;
    uint8_t* data;
my_struct;

void foo()

     my_struct msg;
     msg.size = 5;
     msg.data = malloc(msg.size);
     if(msg.data != NULL)
     
         free(msg.data); // Hardfault
     

free() 中逐步使用 GDB,我找到了导致 Hardfault 的汇编指令:

ldrd    r1, r3, [r5, #8]

r5 的值为0x5F0FE9D0

CFSR0x8200 并且MMFARBFAR 寄存器都包含0x5F0FE9D8

看网上的LDRDR问题,尝试在my_struct定义中添加__attribute__((__packed__))。 当通过指针/结构使用未对齐的内存访问时,它应该强制编译器生成 2xLDR

通过这样做,我在运行时不再有硬故障。好的...

出于好奇,我想在修改后通过 GDB 检查地址,结果大吃一惊!没有任何变化(我的意思是地址),尽管packed,我最终还是再次点击了 LDRD 指令,并生成了我的 Hardfault(但仅在 GDB 调试执行中)。

我在删除属性后启动了一个新的运行并比较了MMFARBFAR寄存器的值,当我不在GDB时我得到0x41AFFE60

为什么我在调试器中看不到 2xLDR? 更一般地说,为什么在使用和不使用 GDB 时我的行为不一样? packed 技巧是解决我问题的好方法吗?

附:我正在运行 FreeRTOS,并将 configCHECK_FOR_STACK_OVERFLOW 定义为 2 和 configASSERT,没有任何触发。

【问题讨论】:

正如其他人所指出的,您可能会遇到一些内存损坏。这可能是堆栈溢出到堆或其他静态变量中,具体取决于您的内存布局。但这只是猜测。我建议你仔细检查你的内存映射。关于(打包)属性,我不确定这会有什么帮助。如果有的话,恕我直言,这会让事情变得更糟。 【参考方案1】:

0x5F0FE9D80x41AFFE60 在 STM32F7 内存映射(参考手册的第 2 章)中都标记为保留。这意味着堆已损坏

为什么我在调试器中看不到 2xLDR?

因为free() 在预编译的静态库中,所以不会重新编译。

更一般地说,为什么在使用和不使用 GDB 时我的行为不同?

如果堆包含随机垃圾,或者因为它没有正确初始化,或者被一些不相关的代码覆盖,当你连接到板或从板上断开连接时,你可能会得到一些不同的垃圾。或者当某些环境因素发生变化时。

packed 技巧是解决我问题的好方法吗?

不,它只是靠运气设法隐藏了问题。有了损坏的堆,所有的赌注都没有了。

【讨论】:

【参考方案2】:

您从错误的一端开始调试。你检查过malloc返回的值吗?可能不是。

如果地址无效,通常意味着您的链接描述文件有误。

向我们展示一切。 malloc 的结果、实际代码和链接描述文件。

【讨论】:

我检查了 malloc 返回的地址,它在链接描述文件中定义的堆的有效范围内。不幸的是,由于我正在处理专有项目,因此我无法分享实际代码... 如果不显示真实代码,就没有任何帮助。您认为有人对使用 malloc 的 uC 代码感兴趣吗? 我并不是说这很重要或其他什么,我只是遵守 NDA。此外,我不希望 SO 调试我的代码,我只需要指针,你给了一些 :)

以上是关于struct 成员上的 free() 仅在 Debug 中导致 Hardfault的主要内容,如果未能解决你的问题,请参考以下文章

malloc struct vs. 的“成员”当结构非常简单时,整个结构

仅在一个构造函数上的类成员初始化程序中的隐式常量转换 [-Werror=overflow] 溢出

内存管理概述内存分配与释放地址映射机制(mm_struct, vm_area_struct)malloc/free 的实现

C结构初始化? [复制]

在 C 中正确使用 free() 函数

AddressSanitizer:heap-use-after-free 仅在发布存档上