【发布时间】:2018-05-31 08:00:00
【问题描述】:
我试图理解我们最近在使用 Clang 5.0 和未定义行为清理器 (UBsan) 时解决的问题。我们有代码可以向前或向后处理缓冲区。简化后的大小写为similar to the code shown below。
0-len 可能看起来有点不寻常,但早期的 Microsoft .Net 编译器需要它。 Clang 5.0 和 UBsan produced integer overflow findings:
adv-simd.h:1138:26: runtime error: addition of unsigned offset to 0x000003f78cf0 overflowed to 0x000003f78ce0
adv-simd.h:1140:26: runtime error: addition of unsigned offset to 0x000003f78ce0 overflowed to 0x000003f78cd0
adv-simd.h:1142:26: runtime error: addition of unsigned offset to 0x000003f78cd0 overflowed to 0x000003f78cc0
...
第 1138、1140、1142 行(和朋友)是增量,可能
由于0-len而后退。
ptr += inc;
根据Pointer comparisons in C. Are they signed or unsigned?(也讨论了C++),指针既不是有符号的也不是无符号的。我们的偏移量是无符号的,我们依靠无符号整数换行来实现反向跨步。
代码在 GCC UBsan 和 Clang 4 以及更早的 UBsan 下运行良好。我们最终使用help with the LLVM devs 为 Clang 5.0 清除了它。我们需要使用ptrdiff_t,而不是size_t。
我的问题是,构造中的整数溢出/未定义行为在哪里? ptr + <unsigned> 是如何导致有符号整数溢出并导致未定义行为的?
这是一个镜像真实代码的 MSVC。
#include <cstddef>
#include <cstdint>
using namespace std;
uint8_t buffer[64];
int main(int argc, char* argv[])
{
uint8_t * ptr = buffer;
size_t len = sizeof(buffer);
size_t inc = 16;
// This sets up processing the buffer in reverse.
// A flag controls it in the real code.
if (argc%2 == 1)
{
ptr += len - inc;
inc = 0-inc;
}
while (len > 16)
{
// process blocks
ptr += inc;
len -= 16;
}
return 0;
}
【问题讨论】:
-
指针本身既没有签名也没有签名。但是指针加法是有符号的,因为当指针指向数组内的某个位置时,指针可以递增或递减。根据此问题中的信息无法进一步说明,因为它不足以确定是否存在未定义的行为,除非提供 minimal reproducible example。
-
如果你有一个指向数组第三个元素的指针,你可以给它加上-1,最后得到一个指向数组第二个元素的指针。指针添加已签名。
-
我记得几个月前(或者可能是去年)在与此 MCVE 中相同的问题中存在一个重要的实时错误
标签: c++ pointers clang undefined-behavior ubsan