检查指针是不是指向堆上分配的内存

Posted

技术标签:

【中文标题】检查指针是不是指向堆上分配的内存【英文标题】:Check if a pointer points to allocated memory on the heap检查指针是否指向堆上分配的内存 【发布时间】:2011-03-05 03:39:37 【问题描述】:

我想知道一个指针是否指向一块用 malloc/new 分配的内存。我意识到任意地址的答案是“不,你不能”,但我确实认为可以覆盖 malloc/free 并跟踪分配的内存范围。

您知道提供此特定工具的内存管理库吗? 你对生产代码了解吗?

Valgrind 很棒,但它的工具太多(慢),正如 Will 所说,我们不想像这样使用 Valgrind(使软崩溃就足够了)。Mudflap 是非常好的解决方案,但专用于 GCC,可悲的是,检查并不仅仅返回布尔值(请参阅下面的答案)。 请注意,检查内存写入是否合法是security issue。所以追求表现是有动力的。

【问题讨论】:

+1,这是一个关键问题,需要测试 POD 内存(即没有构造函数和析构函数)是否被正确分配和释放。我猜想 C++ 库在其堆管理机制中有答案,因为它需要跟踪分配的内存块及其大小。但我不知道它是否暴露了这些数据(如果没有,是否有可能入侵获取数据)-我正在寻找答案。 为什么你需要它?是否知道指针是否有效或知道它是否是一般的堆指针? 【参考方案1】:

证明它可能无法有效地完成:

char * p1 = malloc(1);
free( p1 );
char * p2 = malloc(1);   // probably allocates same block as first malloc

现在 p1 和 p2 都指向堆上的同一个内存,但只有 p2 是有效的。

【讨论】:

从哲学上讲,这可能令人失望,但如果它最终 p2 指向与 p1 相同的地址,则 p1 仍然是可修改内存的地址。所以这对我来说很好。 @Ugo 即使 C 和 C++ 标准都说 p1 不能用于访问该内存? Weeeeel 可能 p2 与 p1 的地址相同...取决于您的 malloc 的工作方式(在 valgrind 下运行几乎可以保证 p2 != p1 这是故意的,因此您可以在免费后检测到使用,但是好的 malloc 仍然可能会或可能不会(其中一些维护最近释放的特定大小的对象的 FIFO 队列,他们不会返回 p1...) 更进一步:如果通过一些任意事件序列,指向旧指针 p1 的地址最终位于新分配块的中间而不是完全匹配 p2 会怎样?那么 p1 仍然可以说是指向堆的一部分,但是 p1 本身不会以我能想象的任何合理方式有用。 除了你不应该这样做或依赖这种行为这一事实之外,我看不出这是如何回答这个问题的......也许我错过了一些东西。【参考方案2】:

没有标准的方法可以做到这一点,但各种 malloc 调试工具可能有办法做到这一点。例如,如果你使用valgrind,你可以使用VALGRIND_CHECK_MEM_IS_ADDRESSABLE来检查这个和相关的东西

【讨论】:

不错!我不知道与图书馆的链接不是标准的;) @Ugo 可以说,与库的链接不是标准的。但除此之外,valgrind 等工具使用的库中的内容绝对是非标准的,这就是它们特定于平台的原因。 @matias C++ 和 C 标准都没有真正描述这样的事情 - 但这不是这个问题的重点。 如果你使用 valgrind,你不会检查事情是否可寻址,你只是让 valgrind 在你有错误时爆炸,然后你修复它:) @Neil/jalf/Chris 你是对的,链接不是标准化的,但你仍然在做标准的 c++。与平台特定的代码相同,在代码中的某处分配视频帧缓冲区地址不会使您的代码不标准,它只会使其特定于平台。【参考方案3】:

您可以使用 LD_PRELOAD,并将 malloc 包装在您自己的函数中。

【讨论】:

【参考方案4】:

内存分配有一个(虚拟)地址和一个长度。

指针只包含地址。

如果你单独跟踪长度,你可以检查它的包含,例如:

int check_contained(const char* src,size_t srclen,const char* sub,size_t sublen) 
   return (sub >= src) && (sub+sublen < src+srclen);

Symbian 有一个 AllocLen 函数,但没有 POSIX 或 win32 等价物。

【讨论】:

能否请您更新 AllocLen 的链接?它接缝死了:/ @MateuszPiotrowski Symbian 已死,现在看来文档也是如此:(【参考方案5】:

如果性能对您的应用程序来说不是真正的问题,您可以自己执行此操作:

定义 MyMalloc(...) 和 MyFree(...) ,在其中调用 malloc/free 时,您会更新(有序)对列表 address -- malloc 的结果,blockSize -- amt内存请求。然后当你需要检查一个指针 p 时,你寻找一个满足:address

可以/应该检查其他条件,如果您想实际使用该指针,这只会判断地址是否正在使用。

【讨论】:

【参考方案6】:

我做了类似的事情,但不记得它是如何编码的,而且我手头没有代码。

但基本思想是覆盖基类的newdelete。在new 中设置了一个静态标志(例如bool inDynamicAlloc=true)。这个标志在基类的构造函数中被质疑。 如果为真,则在堆上分配对象,否则在堆栈上分配。

构造函数随后重置标志。

希望这会有所帮助。

【讨论】:

【参考方案7】:

Mudflap (for gcc) 看起来很甜蜜。你必须编译你的软件,但它会检查任何错误的指针访问(堆/堆栈/静态)。它旨在用于生产代码,其速度估计在 x1.5 到 x5 之间。您还可以禁用读取访问检查以加快速度。 用户检查可以使用

void __mf_check (void *ptr, __mf_size_t sz, int type, const char *location)

调用此函数会导致:无,fork 到 gdb,segv 或中止,具体取决于环境参数。

【讨论】:

【参考方案8】:

您可以使用conservative garbage collector 用来确定类指针对象是否指向堆的相同技术。实际上,您可能可以从 bdwgc 本身抄录源代码。这将是一项艰巨的任务,但您可以根据需要控制和移植。 (事实上​​,大部分移植工作已经完成。)

【讨论】:

【参考方案9】:

查看我们的CheckPointer 工具,该工具将检查每个指针访问的有效性。它不是特别快,但它会捕获即使 Valgrind 也无法捕获的错误(例如,指向已释放堆栈帧的指针等)

Another answer to this question 显示了对指针有效性进行纯内存范围检查将无法检测到问题的情况。他是对的,因为如果只有内存范围地址,您就无法可靠地检查重新分配的存储块是否被滥用。这称为暂时错误。通过将分配事件与内存块以及范围相关联,您可以检测到这一点。 Checkpointer 会执行此操作,并会检测到错误。

【讨论】:

以上是关于检查指针是不是指向堆上分配的内存的主要内容,如果未能解决你的问题,请参考以下文章

java内存区域

在堆上创建的变量,指向同一个变量的2个指针有不同的地址?

指针 && 动态内存分配

C如何检查内存地址是不是仍在范围内[关闭]

两个或多个线程如何在它们分配的堆上共享内存?

堆上分配的内存大小