使用 SSE 4.2 的 strncmp 如何在加载 16 个字节时避免读取超出页面边界?

How does strncmp using SSE 4.2 avoid reading beyond the page boundaries when loading 16 bytes?

glibc 现在使用 SSE 4.2 优化 strncmp:

这可以在调试器中看到:

   0xf7f20218 <__strncmp_sse4_2+40>    movdqu xmm2, xmmword ptr [edx]
   0xf7f2021c <__strncmp_sse4_2+44>    mov    ecx, eax
 ► 0xf7f2021e <__strncmp_sse4_2+46>    and    ecx, 0xfff
   0xf7f20224 <__strncmp_sse4_2+52>    cmp    ecx, 0xff0
   0xf7f2022a <__strncmp_sse4_2+58>    ja     __strncmp_sse4_2+125                    <__strncmp_sse4_2+125>

我不是很喜欢 SSE 4.2 的字符串,但我的理解是它允许一次最多比较 16 个字节。 movdqu xmm2, xmmword ptr [edx] 从其中一个字符串加载 16 个字节。

我的问题是:如果页面末尾有一个短字符串(比如 3 个字节),并且在页面限制内以 NULL 终止,但页面之外剩余的 13 个字节中的一些则不能这会导致段错误,因为我们现在正尝试加载超出我们有权访问的页面?

这个问题是在模拟器上工作时出现的,它捕获了不受约束的访问(即,我的应用程序从未写入的内存读取):

strncmp(0x8064dd8, 0x7ffeff48, 0x4)
WARNING Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x818ba90 (strncmp+0x0 in libc.so.6 (0x8ba90))

这令人费解,因为:

也就是说,这似乎不是调用方的错误,而是 strncmp 的意外行为。调试 strncmp 使我找到了 SSE 4.2,它部分解释了为什么它的读取超出 n 设置的限制:它只是使用 SSE 4.2 一次加载许多字节,即使它根本不需要它们。

问题:

Is this correct? Does strncmp_sse4_2 read more than n bytes?

是的。

Even if it does: Doing 16 bytes at a time should stop at 0x7ffeff58. Why does it read till 0x7ffeff60?

您假设它从您传入的地址开始使用 movdqu。它可能没有。它可能首先将指针对齐到缓存行。

If so, how does this not potentially cause a page fault?

如果您有一个 16 字节对齐的指针 p,这意味着 p+15 指向与 p 相同的页面,因此您可以从 p 读取 16 个字节逍遥法外。

If so, how do we tell distinguish acceptable read of uninitialized data from cases indicating bugs? E.g. how would Valgrind avoid reporting this as an uninitialized read?

Valgrind 通过插入它自己的 strcmp 副本来做到这一点(对于动态链接的二进制文件)。如果没有这样的干预,valgrind produces false positives(或者更确切地说,valgrind 会产生无人关心或无能为力的真阳性)。