首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >指针加法和整数溢出与Clang5.0和UBsan?

指针加法和整数溢出与Clang5.0和UBsan?
EN

Stack Overflow用户
提问于 2017-12-18 00:00:57
回答 2查看 399关注 0票数 1

我试图理解我们最近在使用Clang5.0和未定义的行为消毒器(UBsan)时清除的一个问题。我们有代码在正向或向后方向处理一个缓冲区。简化的情况是类似于下面显示的代码

0-len看起来可能有点不寻常,但它是微软早期的.Net编译器所需要的。Clang5.0和UBsan 生成整数溢出发现

代码语言:javascript
复制
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而向后跨出的。

代码语言:javascript
复制
ptr += inc;

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

该代码在GCC UBsan和Clang 4以及更早的UBsan下都很好。我们最终使用帮助LLVM开发人员为Clang5.0清除了它。而不是size_t,我们需要使用ptrdiff_t

我的问题是,结构中的整数溢出/未定义行为在哪里?ptr + <unsigned>是如何导致有符号整数溢出并导致未定义行为的?

下面是一个反映真实代码的MSVC。

代码语言:javascript
复制
#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;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-12-18 03:28:49

将整数添加到指针的定义是(N4659 expr.add/4):

为了保留格式,我在这里使用了一个图像(这将在下面讨论)。

请注意,这是一个新的措辞,它取代了以前标准中不那么明确的描述。

在您的代码中(当argc是奇数),我们得到的代码相当于:

代码语言:javascript
复制
uint8_t buffer[64];
uint8_t *ptr = buffer + 48;
ptr = ptr + (SIZE_MAX - 15);

对于应用于代码的标准引号中的变量,i48j(SIZE_MAX - 15)n64

现在的问题是,如果我们把"i + j“解释为i + j表达式的结果,那么它是否等于32,它小于n。但是如果它意味着数学结果,那么它要比n大得多。

该标准在这里使用一种用于数学方程的字体,而不对源代码使用该字体。也不是有效的运算符。所以我认为他们打算用这个方程来描述数学值,也就是说,这是未定义的行为。

票数 2
EN

Stack Overflow用户

发布于 2017-12-18 21:31:34

C标准将类型ptrdiff_t定义为指针差操作符产生的类型.一个系统有一个32位的size_t和一个64位的ptrdiff_t是可能的;这样的定义对于一个使用64位线性或准线性指针但每个对象都小于4 4GiB的系统来说是很自然的。

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47860626

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档