从未初始化的内存中读取每次都会返回不同的答案

问题描述:

在CppCon 2016的Nicholas Ormrod's talk中,他提到了Facebook上一个隐藏的bug,其中一个字节从未初始化(未写入)页面被读取了两次,因此有时第二次读取返回一个与第一次读取值(零)不同的(非零)值。从未初始化的内存中读取每次都会返回不同的答案

他提到他们使用了jemallocI also presume他们在Linux上运行。 jemalloc's manpage表示它总是优于而不是sbrk()

现在,jemalloc's only mmap() call使用标记MAP_PRIVATE | MAP_ANONYMOUS偶尔包含MAP_FIXED,特别是它不使用MAP_UNINITIALIZED。这意味着页面分配时为always zero-initialized

此外,对于匿名映射,即使madvise() with MADV_DONTNEED也会为"zero-fill-on-demand pages"返回匿名映射,我将其读为“零初始化页面”。

我的问题是:第二次读取会返回一个非零值,导致它们的错误怎么可能?

+0

解释视频:尼古拉斯描述的错误发生在页面有条件地返回到内核时,所以当你从页面读取字节时,内核说它是未初始化的内存,所以它可以返回'0'。但是,在第二次读取中,您*写*而不是读,所以内核意识到您确实需要内存并获取存储在那里的实际数据。 – Justin

+0

此问题没有提供足够的上下文来回答问题。它缺乏[mcve]。为了回答这个问题,回答者必须观看视频 – Justin

+0

@Justin我链接到视频的额外信息,但我在这里提到了bug的所有细节。 –

无论这家伙提供的解释是完全破坏的(至少在给定的上下文中)。无论如何,代码都有未定义的行为。

如果data指向至少分配了size() + 1大小的块,由于竞争条件(他提到线程使用之前),代码具有未定义的行为。

如果data的大小小于该值(例如,等于size()),则代码具有未定义的行为,因为超出了界限(并且争用条件成为争议点)。

+0

这听起来像是他们在FB上制作各种快捷方式,但是因为像Andrei Alexandrescu(CPP委员会)这样的人正在研究这些代码,所以我给他们尽管触发了UB,他们仍然知道自己在做什么(尽管他早些时候在谈话中也谈到了这一点)。所以我试图理解他的解释,而不是把它扔出窗外。 –

+0

此外,从任何未初始化的存储读取本身已经是未定义的行为。 –

+0

@YamMarcovic这是正确的,但我不知道存储是否未初始化,因为我不知道如何初始化存储初始化。我只从您的链接中观看了视频。然而,理智的假设是,它不是,所以这是第三个无知的行为点。 – SergeyA