read(3)“调用长度大于目标缓冲区的大小”

时间:2014-01-23 18:55:43

标签: c linux glibc

使用以下代码:

#define MIN(a,b) ({ typeof (a) _a = (a); typeof (b) _b = (b); _a < _b ? _a : _b; })
...
size_t const recvBufLength = 50*1024*1024;
char * const recvBuf = malloc(recvBufLength);
...
while ((r = read(sockfd, recvBuf, MIN(recvLength-received, recvBufLength))) > 0) {
    ...
}

我收到了这个错误:

/usr/include/x86_64-linux-gnu/bits/unistd.h: In function ‘main’:
/usr/include/x86_64-linux-gnu/bits/unistd.h:39:2: error: call to ‘__read_chk_warn’ declared with attribute warning: read called with bigger length than size of the destination buffer [-Werror]
  return __read_chk (__fd, __buf, __nbytes, __bos0 (__buf));
  ^
lto1: all warnings being treated as errors
lto-wrapper: gcc returned 1 exit status
/usr/bin/ld: lto-wrapper failed
collect2: error: ld returned 1 exit status

如果我删除MIN并将读取更改为read(sockfd, recvBuf, recvBufLength),那么它会毫无怨言地编译。

这是我的错误,还是GCC,还是glibc?

如果不是前者,那么我该如何规避有缺陷的长度检查?


修改

即使将MIN宏扩展为简单的三元运算仍然会出错。

read(sockfd, recvBuf, (recvLength-received)<recvBufLength?(recvLength-received):recvBufLength)

这一切都在GCC 4.8.2中,我很快就会尝试其他版本。

2 个答案:

答案 0 :(得分:3)

我认为这是因为MIN宏扩展为块而不是表达式(宏中有{})。我不认为每个C标准都允许这样做(见C block becomes expression: ( {int a = 1; int b = 2; a+b;} ) equals 3)。

也许尝试将MIN的结果放在变量中

val=MIN(recvLength-received, recvBufLength));
while ((r = read(sockfd, recvBuf, val) > 0) {
    ...
    val=MIN(recvLength-received, recvBufLength));
}

有趣的是,我刚刚学到了一些东西!

答案 1 :(得分:0)

我在 GCC 5.4 for x64 中发现了类似的 read() 问题。缓冲区是:

uint8_t msg[4096];
readBytes = read(fd, &msg[0], size);

大小是 uint16_t 变量中的一个可变数量。 _FORTIFY_SOURCE 宏 (__bos0) 以某种方式检测到目标缓冲区小于大小。鉴于其运行条件,我的程序不能以这种方式运行。错误是:

unistd.h:39:58: error: call to ‘__read_chk_warn’ declared with attribute warning: read called with bigger length than size of the destination buffer [-Werror]

因此,我在二进制文件的链接阶段通过禁用保护来修复它:

CCFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0

也可以在编译 .o 文件时完成,以防有人需要仅在给定文件中禁用该问题。