我试图不理解我们最近在使用Clang 5.0和Undefined Behavior Sanitizer(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(和 friend )是增量,可能会
由于
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中将其清除。代替
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;
}
最佳答案
向指针添加整数的定义是(N4659 expr.add / 4):
我在这里使用了图像以保留格式(这将在下面讨论)。
注意,这是一个新的措词,代替了先前标准中不太清晰的描述。
在您的代码中(当argc
为奇数时),我们最终得到的代码等效于:
uint8_t buffer[64];
uint8_t *ptr = buffer + 48;
ptr = ptr + (SIZE_MAX - 15);
对于应用于您的代码的标准报价中的变量,
i
是48
,j
是(SIZE_MAX - 15)
,n
是64
。现在的问题是0≤i + j≤n是否成立。如果我们将“i + j”解释为表示
i + j
的结果,则等于32
,小于n
。但是,如果这意味着数学结果,则它比n
大得多。该标准在此处将字体用于数学方程式,而不在源代码中使用该字体。
≤
也不是有效的运算符。所以我认为他们打算用这个方程式来描述数学值,即这是不确定的行为。