2

With the following code:

#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) {
    ...
}

I'm getting this error:

/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

If I get rid of the MIN and change the read to read(sockfd, recvBuf, recvBufLength) then it compiles without complaint.

Is this my mistake, or GCC's, or glibc's?

If not the former, then how can I circumvent the flawed length checking?


Edit:

Even expanding the MIN macro to a simple ternary operation still gives the error.

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

This is all in GCC 4.8.2, I'll try other versions shortly.

Max
  • 2,760
  • 1
  • 28
  • 47

2 Answers2

3

I think it is because the MIN macro expands as a block and not as an expression (there { } in the macro). I don't think it is allowed in every C standard (see C block becomes expression: ( {int a = 1; int b = 2; a+b;} ) equals 3).

Maybe try to put the result of MIN in a variable first

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

Interesting, I just learned something!

Community
  • 1
  • 1
nlko
  • 500
  • 1
  • 6
  • 9
  • 1
    FYI statement expressions are a [GNU extension](http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs) which are not part of the C or C++ language standards. – Adam Rosenfield Jan 23 '14 at 19:42
  • Alec are we sure that recvLength-received is > 0 ? (My idea is maybe we are triggering the Object Size Checking Built-in Functions -- http://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html) – nlko Jan 23 '14 at 20:30
  • @nlko Yes, all the variables are unsigned ints – Max Jan 24 '14 at 15:53
  • I was wondering if it could be an overflow (since it is unsigned int). The error seems to be related to the buffer being smaller than the provided length. (I found this also : http://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html) – nlko Jan 24 '14 at 16:06
1

I found a similar issue with read() in GCC 5.4 for x64. The buffer was:

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

And the size was a variable amount inside a uint16_t variable. Somehow the _FORTIFY_SOURCE macro (__bos0) was detecting that the destination buffer was smaller than the size. My program cannot behave that way given the conditions of its operation. The error was:

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]

So, I fixed it in the linking stage of my binary by disabling the protections:

CCFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0

It can also be done when compiling the .o file in case somebody needs to disable the problem in a given file only.