调查一个错误的free()指针引用

时间:2011-06-01 13:12:01

标签: c++ gdb free

由于对错误指针的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也无济于事(没有打击)

  5. 在这些条件下,我不是专业的调试人员;你知道我怎么能得到这个地址对程序意味着什么吗?

    修改:

    • 该程序在嵌入式平台(SH4)上运行;这是valgrind不支持的(很遗憾......)。

    • 关于此类的功能的更多详细信息:它使用CUrl library在Internet上检索XML文件,然后继续使用pugixml库进行解析。

3 个答案:

答案 0 :(得分:1)

黑暗中的狂野镜头......你有一个没有虚拟析构函数的基类,你实际上是在实例化派生类并将new的结果存储在指向基类的指针中。您通过基指针删除,并且在此特定情况下未定义的行为导致该效果。

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

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

答案 1 :(得分:0)

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

你总能(好吧,几乎总是)使用调试器。在某些领域,例如实时编程,这有点具有挑战性,但这似乎并非如此。

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

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

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

答案 2 :(得分:0)

好的,最后把这个错误钉了下来。

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

架构是这样的:

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

由于此结构包含一些指向字符串的指针,因此NULL检查失败并且我释放了一个错误的指针。

令人遗憾的是,基类通常会填满结构的每个字段,覆盖默认的垃圾值。只有当我继承基类时才遇到问题。

回过头来看,我想我可以通过有效的GDB调试更快地找到 way 。 我想我将不得不调查这个问题。