在free / delete上编写Garbage

动态内存分配的一个问题是可以delete / free内存块并且仍然有指向它的指针。 当一个人解释其中一个指针时,很可能事情可能“起作用”,但却容易受到内存损坏等的影响。

为了帮助解决这些问题,一些平台在释放它作为一个释放的单元格之前,将delete / free写入垃圾(类似于DEDEDEDE )放入释放的堆单元格中。 这意味着当一个人试图解除引用一个释放单元格的指针时,可能会或多或少总是期望一个data_abortexception,这会导致程序崩溃。 这将在使用调试库时。 由于性能原因,发布库不会执行此操作。

有人可以告诉我,如果可以使用glibc在标准Linux平台上获得这种行为,或者如何执行一些简单的操作来执行此操作。 我认为它会帮助我更轻松地找到一些错误。

我想补充一点,为不同的版本启用或禁用此行为应该是微不足道的。 我能想到的最接近的是malloc钩子,遗憾的是free不会将单元格大小作为参数。

对于C ++,您可以相当便携地执行此操作:替换全局运算符::new::delete

 #include  #include  // 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(p); } void *operator new(size_t n) { char *p = static_cast(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(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); } 

如果您的程序注册了新的处理程序,那么您将需要从::new调用它们。

要捕捉malloc / free,你必须像其他答案那样做特定于linux的事情,但同样的技巧可以解决你的问题,你没有在free钩子中的大小,假设你不想寻找对于真正的malloc存储的大小。

作为最后的手段,你可以摆弄我的非侵入式堆调试器 。 它不会阻止您取消引用悬空指针,但检测到双重删除和其他常见错误。

从Linux malloc手册页:

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

(2.x)包含一个malloc()实现,它可以通过环境变量进行调整。 当设置MALLOC_CHECK_时,使用一种特殊的(效率较低的)实现,它被设计为容忍简单错误,例如具有相同参数的free()的双重调用,或者单个字节的溢出(off-by-一个错误)。 但是,并非所有此类错误都可以得到保护,并且可能导致内存泄漏。 如果MAL- LOC_CHECK_设置为0,则会自动忽略任何检测到的堆损坏; 如果设置为1,则在stderr上打印诊断消息; 如果设置为2,则立即调用abort(3); 如果设置为3,则在stderr上打印诊断消息并中止程序。 使用非零MAL-LOC_CHECK_值可能很有用,因为否则崩溃可能会在很晚之后发生,因此很难找到导致问题的真正原因。

以下代码完全符合我的要求:

 #include  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 ++,其中一个使用newdelete

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

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

 void* malloc(int size) { blockheader b; // fill out the structure with eg 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以获取大小。 因此你可以使用malloc钩子。

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

你总是可以将一些带有块大小的东西自由包装作为参数释放,并使用宏切换到生产模式:

 #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调用之后,将指针置空也是一个好主意。 它没有捕获指针别名相关的错误,但它会捕获一些双重释放。