代码之家  ›  专栏  ›  技术社区  ›  doron

在空闲/删除时写入垃圾

c c++
  •  3
  • doron  · 技术社区  · 15 年前

    动态内存分配的一个问题是 delete / free 一块内存,仍然有指针指向它。当一个人取消引用这些指针中的一个时,很有可能事情可能“正常”了,但会使一个人容易受到内存损坏等的影响。

    为了帮助解决这些问题,一些平台 删除 / 自由的 写垃圾(比如 DEDEDEDE )在释放为已释放的堆单元之前将其释放为已释放的堆单元。这意味着,当您现在试图解除对已释放单元的指针的引用时,您可以或多或少地期望出现数据中止异常,这将导致程序崩溃。这将在使用调试库时发生。由于性能原因,发行库不执行此操作。

    有人能告诉我是否可以在使用glibc的标准Linux平台上获得这种行为,或者如何执行一些简单的操作来做到这一点。我想它能帮我更容易地找到一些虫子。

    我想补充一点,对于不同的构建启用或禁用此行为应该很简单。我能想到的最接近的是malloc钩子,不幸的是free没有把单元格大小作为参数。

    7 回复  |  直到 15 年前
        1
  •  3
  •   Steve Jessop    15 年前

    对于C++,您可以相当方便地进行这一操作:替换全局运算符 ::new ::delete :

    #include <cstdlib>
    #include <stdexcept>
    
    // value must be at least as big as sizeof(size_t),
    // and a multiple of the maximum alignment required for any
    // type by this implementation
    #define MAX_ALIGN 8
    
    size_t &stored_size(char *p) {
        return *reinterpret_cast<size_t*>(p);
    }
    
    void *operator new(size_t n) {
        char *p = static_cast<char*>(std::malloc(n+MAX_ALIGN));
        if (!p) throw std::bad_alloc();
        stored_size(p) = n;
        char *base = p + MAX_ALIGN;
        // set base with n bytes of eye-catchers for uninitialized memory
        return base;
    }
    
    void operator delete(void *ptr) {
        if (!ptr) return;
        char *base = static_cast<char*>(ptr);
        char *p = base - MAX_ALIGN;
        size_t n = stored_size(p);
        // set base with n bytes of eye-catchers for freed memory,
        // and make sure your compiler isn't clever enough to optimize that away.
        std::free(p);
    }
    

    如果您的程序注册了新的处理程序,那么您将希望从 ●新的 .

    要抓住malloc/free,您必须像在其他答案中那样做特定于linux的事情,但是同样的技巧可以解决您的问题,即您在 free 胡克,假设你不想到处寻找真正的 malloc .

        2
  •  2
  •   Community Mohan Dere    9 年前

    作为最后手段,你可以摆弄我的 non-intrusive heap debugger . 会的 防止取消对悬挂指针的引用,但是 检测双重删除和其他常见错误。

        3
  •  2
  •   nmichaels    15 年前

    从Linux malloc手册页:

       Recent  versions  of  Linux  libc  (later  than 5.4.23) and glibc
    

    (2.x条) 包括一个可调的malloc()实现 via环境 变量。当设置了MALLOC_CHECK_u时,一个特殊的 (效率较低) 所使用的实现旨在 简单的 错误,例如使用相同参数对free()的双重调用,或 结束- 单字节运行(由一个错误关闭)。不全是这样 错误可以是 但是,可以防止内存泄漏。如果 马尔- LOC_CHECK_u设置为0,任何检测到的堆损坏都是 默默地 忽略;如果设置为1,则会在 stderr;如果设置 到2,立即调用中止(3);如果设置为3,则诊断 消息 在stderr上打印,程序中止。使用非零 马尔- LOC_CHECK_u值可能很有用,因为否则崩溃可能 发生 很久以后,问题的真正原因是 很难 追踪。

        4
  •  1
  •   doron    15 年前

    下面的代码正是我想要的:

    #include <malloc.h>
    
    typedef void (*free_hook_t)(void*, const void*);
    
    static free_hook_t system_free_hook;
    
    static void my_free_hook (void *ptr, const void *caller)
         {
           __free_hook = system_free_hook;
           int size = malloc_usable_size(ptr);
           memset(ptr,0xDE, size);
           free (ptr);
           __free_hook = my_free_hook;
         }
    
    static void init_free_hook()
         {
         system_free_hook = __free_hook;
          __free_hook = my_free_hook;
         }
    
    /* Override initializing hook from the C library. */
    void (*__malloc_initialize_hook) (void) = init_free_hook;
    

    它是完全独立的,所以技术上可以包括或不需要。我错过的是 malloc_usable_size 功能。

    在Ubuntu 10.10上进行测试,这也在C++中使用。 new delete

        5
  •  0
  •   Puppy    15 年前

    不是在C++中。从没听说过聪明的指针?悬垂的指针是过去的问题。您可以从boost和标准库获得高质量的智能指针。明智地使用它们,并且永远不会泄漏内存或试图访问不再存在的对象。

    至于不用计算单元大小,我的理解是大多数实现分配一个块,然后有一个块头,然后返回内存,例如。

    void* malloc(int size) {
        blockheader b; // fill out the structure with e.g. blocksize
        void* mem = allocate_memory(size + sizeof(blockheader));
        memcpy(mem, &b, sizeof(blockheader)); // or memcpy(&b, mem, ...), I can't recall
        return (void*)((char*)mem + sizeof(blockheader)); // return the actual memory
    }
    

    然后在free()中,他们只需再次访问blockheader即可获得大小。你可以用马洛克钩子。

        6
  •  0
  •   BЈовић    15 年前

    在c++中,一旦删除指针指向的对象,对指针的每一次进一步访问都是UB。此外,在c++中,原始指针应该很少使用。

        7
  •  0
  •   nmichaels    15 年前

    您始终可以将空闲包装在以要释放的块大小作为参数的东西中,并使用宏切换到生产模式:

    #ifdef DEBUG
    #define FANCY_FREE(ptr, size) do {memset(ptr, 0xDE, size); free(ptr);} while (0)
    #else
    #define FANCY_FREE(ptr, size) free(ptr)
    #endif
    

    在调用之后将指针清空也是一个好主意 free 在他们身上。它不会捕获与指针别名相关的错误,但会捕获一些双重释放。