【问题标题】:Investigating a bad free() pointer reference调查错误的 free() 指针引用
【发布时间】:2011-06-01 13:12:01
【问题描述】:

由于对错误指针的 free() 调用,我的程序在单一测试中严重崩溃,并显示以下错误消息:

*** glibc detected *** /home/user/main.out: free(): invalid pointer: 0x006d0065 ***

代码大致如下:

// constructor is MyObj( const std::string & ) 
// and copies the string in its own std:string member
MyObject *obj = new MyObject("some string arg");
if(obj->isValid)
{
   log("Success\n");
}
delete obj; // if I remove this, the program doesn't crash...

我的调查是这样的:

  1. 我试图跟踪我创建并释放的指针,但它发生在不同的地址空间中,我得到(成功)这样的删除:Delete buffer @0x850ed0。所以我怀疑有什么东西试图释放一个静态字符指针。此外,地址在多次尝试后保持不变,这让我在这个谓词中感到欣慰。

  2. 我无法使用 GDB,因为我正在使用的平台似乎已经退出:

    Program received signal SIGABRT, Aborted.
    [Switching to Thread 0x344314e8 (LWP 1443)]
    0x2a3dd658 in raise () from /lib/libc.so.6
    (gdb) bt
    #0 0x2a3dd658 in raise () from /lib/libc.so.6
    #1 0x2a3dea2c in abort () from /lib/libc.so.6
    Backtrace stopped: frame did not save the PC

  3. 我尝试使用 hexdump 转储程序中 free() 失败的地址:hexdump -C ~/main.out -s 0x6d0000 -n 2000

    它给了我这个(故障是free(0x6d0065)):

    006d0060 e8 32 06 00 2c e6 02 5f 5a 4e 4b 53 73 34 66 69 |.2..,.._ZNKSs4fi| 006d0070 6e 64 45 63 6a 00 ab 00 00 00 01 3e 58 00 00 1d |ndEcj......>X...|

    这看起来像一个std::string函数,这很奇怪...... 我认为这可能是对 hexdump 的不好使用,因为当程序加载到内存中时内存会被重新定位。

  4. 我也试过 readelf ~/main.out -a | grep 6d0065 也无济于事(没有成功)

我不是在这些条件下调试的专家;你知道我怎样才能得到这个地址对程序的意义吗?

编辑:

  • 程序在嵌入式平台(SH4)上运行; valgrind 不支持(非常遗憾...)。

  • 关于这个类做什么的更多细节:它使用CUrl library在互联网上检索一个XML文件,然后继续使用pugixml库解析它。

【问题讨论】:

  • 你通过valgrind运行你的程序了吗?
  • 这几乎可以肯定是由于MyObject 的构造函数、析构函数或两者中的错误。请出示相关代码。
  • MyObject obj = new MyObject("some string arg"); 甚至无法编译。请张贴您的实际代码。看起来“大致”类似于导致问题的代码不会帮助我们帮助您解决问题。
  • 我删除了c 标签。这不可能是 C。
  • @Gui13:那么,我们什么时候才能看到你的MyObject 定义?

标签: c++ gdb free


【解决方案1】:

在黑暗中疯狂射击......您有一个没有虚拟析构函数的基类,并且您实际上正在实例化一个派生类并将新的结果存储在指向基类的指针中。您通过基指针删除,在这种特殊情况下未定义的行为导致了这种效果。

为什么我会相信?传递给free 的指针没有被malloc 返回,因为它是一个无法正确对齐的奇数地址,并且malloc 始终提供对齐的内存块。这意味着new返回的指针和存储的指针的内存有一个偏移量,这表明它可能是一个基类。如果该类有一个虚拟析构函数,那么delete 将能够确定最派生的对象类型,并且在这样做时,它将更正指针以引用使用new 分配的地址。如果没有虚拟析构函数,delete 将被转换为 obj->~MyObject(); free( obj ); 而无需更正偏移量。

另一个可能导致相同类型问题的事情是使用new[] 进行分配,并使用delete 解除分配(new[] 将分配额外的空间来存储元素的数量,通常在返回的指针之前)。同样的问题是,通过delete 的实现传递给free 的指针未更正以匹配malloc 返回的指针...但在这种特殊情况下,偏移量可能不是奇数。

【讨论】:

  • 请注意:使用关联容器存储new[] 的数组长度也是完全合法的。
  • 对,@DeadMG,它只是变种之一,无论如何,这是在黑暗中拍摄,UB 还在继续,没有看到代码,并且有可用的数据没有可以做很多事情。我倾向于第一种选择:基类中的非虚拟析构函数。
  • 这是一些疯狂的——但绝不是毫无根据的——猜测。不错!
  • 你知道,这其实是有道理的。
  • MyObj 有效地派生自基类。我会调查一下,非常感谢。
【解决方案2】:

你的班级 MyObject 正在做一些非常非常糟糕的事情。

而且您总是可以(嗯,几乎总是)使用调试器。在某些领域(例如实时编程)有时会有点挑战,但这里似乎并非如此。

要以有意义的方式使用调试器,您的代码必须在启用调试的情况下进行编译,GNU 编译器的 -g 选项。

编辑:关于“做一些非常非常糟糕的事情”,我的猜测是该类正在删除它没有业务删除的东西。例如:

class MyObject {
public:
    MyObject(const char * strarg) : str(strarg) {}
    ~MyObject() { delete str; }
private:
    const char * str;
};

【讨论】:

  • 这不是答案,我们已经在 cmets 中说了所有这些。
【解决方案3】:

好的,终于解决了这个问题。

对于那些感兴趣的人,它是一个没有在使用它的类的构造函数中归零的结构。

架构是这样的:

MyObj ---> BaseClass ---> structure member not initialized

由于这个结构包含一些指向字符串的指针,NULL 检查失败,我将释放一个坏指针。

可悲的是,基类通常会填满结构的每个字段,覆盖默认的垃圾值。只有继承了基类才遇到问题。

回想起来,我想我可以通过有效的 GDB 调试更快地发现它方式。 我想我得研究一下这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-18
    • 2018-09-17
    • 1970-01-01
    • 2018-03-18
    • 1970-01-01
    相关资源
    最近更新 更多