- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
如果允许高性能算法中的许多方法在输入缓冲区末尾之后读取少量数据,它们就可以(并且已经)被简化。在这里,“少量”通常意味着最多 W - 1
字节超过末尾,其中 W
是算法的字节大小(例如,对于以 64 位块处理输入的算法,最多 7 个字节)。
很明显,写入超过输入缓冲区的末尾永远是不安全的,一般来说,因为您可能会破坏缓冲区1之外的数据。很明显,越过缓冲区的末尾读取到另一个页面可能会触发段错误/访问冲突,因为下一页可能无法读取。
然而,在读取对齐值的特殊情况下,页面错误似乎是不可能的,至少在 x86 上是这样。在该平台上,页面(以及内存保护标志)具有 4K 粒度(更大的页面,例如 2MiB 或 1GiB,是可能的,但这些是 4K 的倍数),因此对齐读取将仅访问与有效页面相同的页面中的字节缓冲区的一部分。
这是一些循环的规范示例,该循环对齐其输入并读取超过缓冲区末尾的 7 个字节:
int processBytes(uint8_t *input, size_t size) {
uint64_t *input64 = (uint64_t *)input, end64 = (uint64_t *)(input + size);
int res;
if (size < 8) {
// special case for short inputs that we aren't concerned with here
return shortMethod();
}
// check the first 8 bytes
if ((res = match(*input)) >= 0) {
return input + res;
}
// align pointer to the next 8-byte boundary
input64 = (ptrdiff_t)(input64 + 1) & ~0x7;
for (; input64 < end64; input64++) {
if ((res = match(*input64)) > 0) {
return input + res < input + size ? input + res : -1;
}
}
return -1;
}
内部函数
int match(uint64_t bytes)
未显示,但它会查找与特定模式匹配的字节,如果找到则返回最低的此类位置 (0-7),否则返回 -1。
floor((size - 7) / 8)
个 8 字节的块进行循环2。此循环最多可以读取缓冲区末尾之后的 7 个字节(7 字节情况发生在
input & 0xF == 1
时)。然而,返回调用有一个检查,它排除在缓冲区末尾之外发生的任何虚假匹配。
Reading from a pointer beyond its allocated size is definitely not allowedin the standard. I appreciate language lawyer answers, and even occasionally writethem myself, and I'll even be happy when someone digs up the chapterand verse which shows the code above is undefined behavior and hencenot safe in the strictest sense (and I'll copy the details here). Ultimately though, that's not whatI'm after. As a practical matter, many common idioms involving pointerconversion, structure access though such pointers and so aretechnically undefined, but are widespread in high quality and highperformance code. Often there is no alternative, or the alternativeruns at half speed or less.
If you wish, consider a modified version of this question, which is:
After the above code has been compiled to x86/x86-64 assembly, and the user has verified that it is compiled in the expected way (i.e.,the compiler hasn't used a provable partially out-of-bounds access todo something reallyclever,is executing the compiled program safe?
In that respect, this question is both a C question and a x86 assembly question. Most of the code using this trick that I've seen is written in C, and C is still the dominant language for high performance libraries, easily eclipsing lower level stuff like asm, and higher level stuff like <everything else>. At least outside of the hardcore numerical niche where FORTRAN still plays ball. So I'm interested in the C-compiler-and-below view of the question, which is why I didn't formulate it as a pure x86 assembly question.
All that said, while I am only moderately interested in a link to thestandard showing this is UD, I am very interested in any details ofactual implementations that can use this particular UD to produceunexpected code. Now I don't think this can happen without some deeppretty deep cross-procedure analysis, but the gcc overflow stuffsurprised a lot of people too...
match()
函数以特定的幂等方式运行 - 特别是返回值支持重叠检查。因此“查找第一个字节匹配模式”有效,因为所有
match()
调用仍然是有序的。但是,“计数字节匹配模式”方法不起作用,因为某些字节可能会被重复计算。顺便说一句:即使没有按顺序限制,某些函数(例如“返回最小字节”调用)也可以工作,但需要检查所有字节。
--partial-loads-ok
,它控制此类读取实际上是否报告为错误。默认值为 yes,这意味着通常不会将此类加载视为立即错误,但会努力跟踪加载字节的后续使用,其中一些有效,一些无效,并标记错误如果使用了超出范围的字节。在上述示例中,在
match()
中访问整个字的情况下,即使结果最终被丢弃,这种分析也会得出访问字节的结论。 Valgrind
cannot in general 确定是否实际使用了部分加载中的无效字节(并且通常检测可能非常困难)。
最佳答案
是的,它在 x86 asm 中是安全的,并且 现有的 libc strlen(3)
实现在手写 asm 中利用了这一点。 甚至 glibc's fallback C ,但它在没有 LTO 的情况下编译,因此它永远无法内联。它基本上是使用 C 作为便携式汇编程序来为一个函数创建机器代码,而不是作为具有内联的更大 C 程序的一部分。但这主要是因为它也有潜在的严格别名 UB,请参阅我在链接问答中的回答。您可能还需要一个 GNU C __attribute__((may_alias))
typedef 而不是普通的 unsigned long
作为更宽的类型,例如 0x2313 已经使用 0x2313 等。
这是安全的,因为 对齐的加载永远不会跨越更高的对齐边界 ,并且内存保护发生在对齐的页面上,因此至少有 4k 边界1
任何触及至少 1 个有效字节的自然对齐加载都不会出错。 检查您是否离下一页边界足够远以执行 16 字节加载也是安全的,例如 __m128i
。有关更多详细信息,请参阅以下部分。
据我所知,在为 x86 编译的 C 中通常也是安全的。在对象外读取当然是 C 中的未定义行为,但在 C-targeting-x86 中有效。我不认为编译器明确/故意定义行为,但实际上它是这样工作的。
我认为这不是激进的编译器会 assume can't happen while optimizing 的那种UB,但是编译器编写者在这一点上的确认会很好,特别是对于在编译时很容易证明访问结束的情况目的。 (请参阅@RossRidge 评论中的讨论:此答案的先前版本断言它绝对安全,但 LLVM 博客文章并没有真正以这种方式阅读)。
这在 asm 中是必需的,以便一次处理一个隐式长度的字符串快于 1 个字节。在 C 中,理论上编译器可以知道如何优化这样的循环,但实际上他们不知道,所以你必须做这样的 hack。在此更改之前,我怀疑人们关心的编译器通常会避免破坏包含此潜在 UB 的代码。
当知道对象有多长的代码看不到重读时,就不会有危险。编译器必须使 asm 适用于我们实际读取的数组元素的情况。 我可以看到的可能的 future 编译器的似是而非的危险是: 内联后,编译器可能会看到 UB 并决定绝不能采用这条执行路径。或者必须在最终非完整 vector 之前找到终止条件,并在完全展开时将其排除。
你得到的数据是不可预测的垃圾,但不会有任何其他潜在的副作用。只要您的程序不受垃圾字节的影响,就可以了。 (例如,使用 bithacks to find if one of the bytes of a if (p & 4095 > (4096 - 16)) do_special_case_fallback
are zero ,然后使用字节循环来查找第一个零字节,无论超出它的垃圾是什么。)
这在 x86 asm 中不安全的异常情况
uint64_t
。这种情况不会发生在普通代码中。
volatile T*
是循环的典型示例 ,该循环处理隐式长度缓冲区,因此无法在不读取缓冲区末尾的情况下进行矢量化。如果您需要避免读取超过终止的
strlen
字节,则一次只能读取一个字节。
0
(最小无符号字节)的 vector ,因此只有当四个 vector 中的任何一个具有零时,最终 vector 才会具有零元素。在发现字符串的末尾在该缓存行中的某个位置后,它会分别重新检查四个 vector 中的每一个以查看位置。 (使用典型的
pminub
对全零 vector 和
pcmpeqb
/
pmovmskb
来查找 vector 中的位置。)glibc 曾经有几个不同的 0x25236412 上的 CPU
bsf
的未对齐 deref 在为 x86 编译的 C 中也是安全的。
converting a string to upper-case with SSE2。 3年前写那部分时,我有点太傲慢了。您需要一个
int *
typedef 或
__attribute__((aligned(1)))
来确保安全。
memcpy
之类的类型),但不直接取消引用它们。
That is not true
__m128i*
转到下一个对齐的 vector 。如果
p = (p+16) & -16
不是 16 字节对齐,这将部分重叠,但有时进行冗余工作是设置高效循环的最紧凑方式。避免它可能意味着一次循环 1 个字节直到对齐边界,这当然更糟。
p
(LEA/XOR/TEST),它告诉您 16 字节加载的最后一个字节与第一个字节具有相同的页面地址位。或者
((p + 15) ^ p) & 0xFFF...F000 == 0
(LEA/OR/CMP 具有更好的 ILP)检查加载的最后一个字节地址 <= 包含第一个字节的页面的最后一个字节。
p+15 <= p|0xFFF
(MOV/AND/CMP),即
p & 4095 > (4096 - 16)
检查页内偏移距页尾是否足够远。
p & (pgsize-1) < (pgsize - vecwidth)
而不是
unsigned int
,尽管要消除有关不是 64 位干净的代码的警告,您可能需要转换为
uintptr_t
。使用
(unsigned)(uintptr_t)p
(MOV/SHL/CMP) 可以进一步节省代码大小,因为
((unsigned int)p << 20) > ((4096 - vectorlen) << 20)
是 3 个字节,而
shl reg, 20
是 5,或者任何其他寄存器的 6。 (使用 EAX 也将允许
and eax, imm32
的 no-modrm 短格式。)
cmp eax, 0xfff
使其安全地进行未对齐的访问。
关于c - 在 x86 和 x64 上的同一页面内读取缓冲区的末尾是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37800739/
我有一个 if 语句,如下所示 if (not(fullpath.lower().endswith(".pdf")) or not (fullpath.lower().endswith(tup
然而,在 PHP 中,可以: only appears if $foo is true. only appears if $foo is false. 在 Javascript 中,能否在一个脚
XML有很多好处。它既是机器可读的,也是人类可读的,它具有标准化的格式,并且用途广泛。 它也有一些缺点。它是冗长的,不是传输大量数据的非常有效的方法。 XML最有用的方面之一是模式语言。使用模式,您可
由于长期使用 SQL2000,我并没有真正深入了解公用表表达式。 我给出的答案here (#4025380)和 here (#4018793)违背了潮流,因为他们没有使用 CTE。 我很欣赏它们对于递
我有一个应用程序: void deleteObj(id){ MyObj obj = getObjById(id); if (obj == null) { throw n
我的代码如下。可能我以类似的方式多次使用它,即简单地说,我正在以这种方式管理 session 和事务: List users= null; try{ sess
在开发J2EE Web应用程序时,我通常会按以下方式组织我的包结构 com.jameselsey.. 控制器-控制器/操作转到此处 服务-事务服务类,由控制器调用 域-应用程序使用的我的域类/对象 D
这更多是出于好奇而不是任何重要问题,但我只是想知道 memmove 中的以下片段文档: Copying takes place as if an intermediate buffer were us
路径压缩涉及将根指定为路径上每个节点的新父节点——这可能会降低根的等级,并可能降低路径上所有节点的等级。有办法解决这个问题吗?有必要处理这个吗?或者,也许可以将等级视为树高的上限而不是确切的高度? 谢
我有两个类,A 和 B。A 是 B 的父类,我有一个函数接收指向 A 类型类的指针,检查它是否也是 B 类型,如果是将调用另一个函数,该函数接受一个指向类型 B 的类的指针。当函数调用另一个函数时,我
有没有办法让 valgrind 使用多个处理器? 我正在使用 valgrind 的 callgrind 进行一些瓶颈分析,并注意到我的应用程序中的资源使用行为与在 valgrind/callgrind
假设我们要使用 ReaderT [(a,b)]超过 Maybe monad,然后我们想在列表中进行查找。 现在,一个简单且不常见的方法是: 第一种可能性 find a = ReaderT (looku
我的代码似乎有问题。我需要说的是: if ( $('html').attr('lang').val() == 'fr-FR' ) { // do this } else { // do
根据this文章(2018 年 4 月)AKS 在可用性集中运行时能够跨故障域智能放置 Pod,但尚不考虑更新域。很快就会使用更新域将 Pod 放入 AKS 中吗? 最佳答案 当您设置集群时,它已经自
course | section | type comart2 : bsit201 : lec comart2 :
我正在开发自己的 SDK,而这又依赖于某些第 3 方 SDK。例如 - OkHttp。 我应该将 OkHttp 添加到我的 build.gradle 中,还是让我的 SDK 用户包含它?在这种情况下,
随着 Rust 越来越充实,我对它的兴趣开始激起。我喜欢它支持代数数据类型,尤其是那些匹配的事实,但是对其他功能习语有什么想法吗? 例如标准库中是否有标准过滤器/映射/归约函数的集合,更重要的是,您能
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 9 年前。 Improve
我一直在研究 PHP 中的对象。我见过的所有示例甚至在它们自己的对象上都使用了对象构造函数。 PHP 会强制您这样做吗?如果是,为什么? 例如: firstname = $firstname;
...比关联数组? 关联数组会占用更多内存吗? $arr = array(1, 1, 1); $arr[10] = 1; $arr[] = 1; // <- index is 11; does the
我是一名优秀的程序员,十分优秀!