1

我试图理解我们最近在使用 Clang 5.0 和 Undefined Behavior Sanitizer (UBsan) 时清除的问题。我们有代码可以向前或向后处理缓冲区。简化的情况类似于下面显示的代码

0-len可能看起来有点不寻常,但早期的 Microsoft .Net 编译器需要它。Clang 5.0 和 UBsan产生整数溢出结果

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;

根据C 中的指针比较。它们是有符号的还是无符号的?(其中还讨论了 C++),指针既不是有符号的也不是无符号的。我们的偏移量是无符号的,我们依靠无符号整数换行来实现反向跨步。

该代码在 GCC UBsan 和 Clang 4 以及更早的 UBsan 下运行良好。在 LLVM 开发人员的帮助下,我们最终为 Clang 5.0 清除了它。而不是size_t我们需要使用ptrdiff_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;
}
4

2 回答 2

2
于 2017-12-18T03:28:49.480 回答
1

C 标准将类型定义ptrdiff_t为由指针差异运算符产生的类型。系统可能有 32 位size_t和 64 位ptrdiff_t;这样的定义自然适合使用 64 位线性或准线性指针但确实要求单个对象每个小于 4GiB 的系统。

如果已知对象每个小于 2GiB,存储类型的值ptrdiff_t而不是size_t可能会使程序不必要地低效。然而,在这种情况下,代码不应该使用size_t来保存可能为负的指针差异,而是使用int32_t[如果每个对象小于 2GiB,这将足够大]。即使ptrdiff_t是 64 位,类型的值int32_t也会在从任何指针中添加或减去之前进行适当的符号扩展。

于 2017-12-18T21:31:34.620 回答