自(2012/2010/2013)以来,msvcrt是否使用不同的堆来进行内存分配?

7

我之前读到过这个问题,但无法在MSDN或其他任何网站上找到有关crt更改的信息。

我认为在VS2012的VC++版本中,msvcrt已经以一种不再使用私有堆进行分配而是改用进程堆的方式进行了更改。

据我所知,它针对的是在多个库中分配内存并静态链接到crt的问题,以及在另一个库中释放它所分配的内存的问题。

在我看来,这是一项重大变革,我想知道为什么我找不到任何提到它的文档。要么就是我自己编造的(我觉得不太可能,因为我之前和同事讨论过这个话题)。


1
CRT源代码是否仍随VS一起提供?我手头没有方便查看,但我认为它仍然存在。如果是这样,那将是一个无法超越的参考。 - WhozCraig
1
由于它现在使用GetProcessHeap,这是否意味着支持混合使用同一VS版本的静态和动态CRT? - paulm
@paulm,据我所知,这确实是含义。 - Samuel
1个回答

15

没错。 这个更改是在 VS2012 中进行的,似乎是未来的行为。 您可以在 CRT 的源代码中找到相关代码,在 vc/crt/src/heapinit.c 源代码文件中找到:

int __cdecl _heap_init (void)
{
        if ( (_crtheap = GetProcessHeap()) == NULL )
            return 0;

        return 1;
}

之前的版本在这里使用HeapCreate(),同时还有VS5和VS6的兼容性处理。由于这不是公开宣传的,因此其确切原因并不那么清楚。VS2012的一个重要细节是,它最初发布时没有XP支持。这就免去了必须显式启用低碎片堆的要求。LFH已自动启用于Vista及以上版本,因此默认进程堆已经很好了。

使用默认进程堆的一个非常大的优点是解决了DLL拥有自己的CRT副本并因此使用自己的分配器所带来的非常严重的问题。当一个DLL需要释放另一个DLL分配的内存时,这通常会导致非常糟糕的结果。只要重新构建DLL以针对较新版本的CRT,现在就不存在这样的问题了,因为它们自动使用完全相同的堆,因此在模块边界传递指针是安全的。

也有一些相关的担忧。我不确定更新1中发生了什么,更新1是带回XP支持的更新。假设CRT源代码是准确的(看起来是这样),那么你的程序很可能在XP上运行时未启用LFH。

此外,还存在潜在的安全问题,因为winapi也使用默认进程堆。因此,恶意软件现在可能利用你的代码中的缓冲区溢出漏洞并达到winapi缓冲区。如果您之前已对您的代码进行了安全分析和/或模糊测试,则应重新进行这些测试。我假设Microsoft在强烈依赖其安全CRT实现以避免安全问题,但这只是一个猜测。希望James McNellis能够提供更深入的见解。


非常好的解释,感谢您对XP和安全性的额外提示。既然已经确认,我现在会专注于查找有关此问题的MSDN文章。 - Samuel
相信MSDN没有在任何地方提到这一点。 - Hans Passant
我发现了一篇针对VS2012的文章,似乎完全忽略了进程堆对crt的影响:http://msdn.microsoft.com/en-us/library/ms235460%28v=vs.110%29.aspx,因为它仍然指出跨dll边界仍然是一个问题。这让我感到困惑。 - Samuel
1
是的,它非常老旧。请注意它提到了版本4.1和5.0,那已经是三个时代之前的事情了。关于在模块之间传递CRT状态(例如区域设置、errno等)的担忧仍然存在。 - Hans Passant
@Samuel:这完全取决于CRT在Windows堆中添加了多少管理。在_DEBUG代码中,由于其泄漏报告功能和边界检查,这是非常重要的;在发布模式下,似乎分配只是转发到HeapAlloc。但最好始终在您的模块上使用相同的共享CRT库。 - gast128

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接