C语言 unistd. h read()中的奇怪类型转换行为

c3frrgcw  于 2022-12-11  发布在  其他
关注(0)|答案(2)|浏览(146)

请考虑以下C程序:

#include <stdio.h>
#include <unistd.h>

int main()
{
    char *buf[100] = {0};
    __int32_t buflen = 0x80000000;
    size_t len = read(0, buf, buflen);
    printf("%d", len);
}

当使用gcc 12.2.0编译时,我收到警告消息:

<snip>
foo.c:8:18: warning: ‘read’ specified size 18446744071562067968 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=]
    8 |     size_t len = read(0, buf, buflen);
<snip>

并且读取返回-1,即运行时的错误。
我不明白的是:我构建了一个最小(?)符号的int32。然后这个有符号的i32被传递给read(..., ..., size_t buflen),其中size_t是一个无符号整数。因此read应该将buflen“解释”为一个补零的size_t,即0x00_00_00_00_80_00_00_00,这正是我手动将buflen转换为size_t时所发生的情况。
为什么它超过了缓冲区大小,18446744071562067968(接近size_t)是从哪里来的?
一点背景:

  • 是的,我很清楚这会使缓冲区溢出。
  • 这应该是可能被利用的坏代码。

我尝试了buflen的几个值,有时行为不一致。我假设这是一些u.b.。我期望read将传递的参数解释为0x80000000
编辑:buflen被扩展到0xFFFFFFFF80000000。但是为什么呢?

qnyhuwrf

qnyhuwrf1#

ssize_t read(int fd, void *buf, size_t count);

您的size_t是64位长。(int32_t)0x80000000 == -2147483648。当您将此负值转换为64位版本时,它将得到带符号扩展。-2147483648的64位版本是0xffffffff80000000
它显示了使用正确的类型是多么的重要。相反,int32_t使用正确的size_t类型。
不使用内部__intxx_t类型。使用标准(在stdint.h中定义)intxx_t类型
附注:

  1. char *buf[100]定义了一个指向char的100个指针的数组。我不认为这正是您想要的。
    1.您传递的缓冲区比最大读取大小小得多。这会调用未定义的行为
r6vfmomb

r6vfmomb2#

read函数的最后一个参数应该是size_t类型(即buflen),但是,您给予它的对象类型是int32_t,因此,将有一个从int32_tsize_t的转换。
对于此类转换,C标准规定:
当整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则它不会改变
所以问题是:buflen的值是否可以在size_t类型的对象中表示?
您已指定值0x80000000,让我们检查一下它是什么:

int32_t buflen = 0x80000000;
printf("%" PRId32 "\n", buflen);

输出量:

-2147483648

哦...一个负值...因为size_t不能表示负值,所以上面的规则不适用于你的情况。我们需要标准中的另一个规则。它是:
否则,如果新型别是不带负数号的,则会重复加上或减去新型别所能表示的最大值加一或减一,直到值在新型别的范围内为止,以转换值。
并注明:
这些规则描述的是数学值的算术运算,而不是给定类型表达式的值
因为size_t是无符号的,所以这个规则适用。

printf("%zu\n", SIZE_MAX);

输出量:

18446744073709551615

所以根据这个标准我们需要做到:

-2147483648
+ 18446744073709551615
+                    1
  --------------------
  18446744071562067968
  ====================

值18446744071562067968可以在size_t对象中表示,因此将是传递给read的值
顺便说一句:
这里

printf("%d", len);

使用%d打印size_t对象。这是错误的(未定义的行为)。请将%zu用于size_t
也就是说... read的返回值不是size_t而是ssize_t,因此类型从一开始就错误。

相关问题