#if 0
2018-05-05 22:17 GMT+03:00 Vadim Zhukov <[email protected]>:
> 2018-05-05 22:12 GMT+03:00 Vadim Zhukov <[email protected]>:
>> Hi all!
>>
>> Recently I was working on a program that uses stdio functions heavily.
>> While hunting for a bug, I've temporarily disabled buffering (via
>> setvbuf(f, NULL, _IONBF, 0)) and realized that program behavior was
>> changed heavily. The cause was that fread() stopped setting error flag
>> after hitting EAGAIN. It looks like its code is missing setting
>> EOF/error flags totally in case of unbuffered read, and first patch
>> (below) fixes this.
>>
>> I'm not sure if "r" variable value should be propagated to fp->_r, or
>> not. I've assumed that since things work without it now, this doesn't
>> need a change. And headache stops me from further investigation for now,
>> sorry.
>>
>> I understand that use stdio functions without buffering is somewhat
>> exotic, and that's for sure is the reason noone found this bug yet. But
>> this is still a bad thing, IMHO.
>>
>> While looking through the libc code I've also found that __sread() drops
>> __SOFF flag unconditionally for all read errors. IMHO, the EAGAIN case
>> shouldn't be affected here, since, obviously current offset doesn't
>> change and is still well-known.
>>
>> So... any comments, or ever okays? :)
>
> Doing "cvs diff" before mailing the patch works better than doing after.
> Sorry.
>
> --
> WBR,
> Vadim Zhukov
>
>
> Index: stdio/fread.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/stdio/fread.c,v
> retrieving revision 1.15
> diff -u -p -r1.15 fread.c
> --- stdio/fread.c 21 Sep 2016 04:38:56 -0000 1.15
> +++ stdio/fread.c 5 May 2018 19:14:27 -0000
> @@ -79,6 +79,10 @@ fread(void *buf, size_t size, size_t cou
> p += r;
> resid -= r;
> }
> + if (r == 0)
> + fp->_flags |= __SEOF;
> + else if (r < 0)
> + fp->_flags |= __SERR;
> FUNLOCKFILE(fp);
> return ((total - resid) / size);
> }
> Index: stdio/stdio.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/stdio/stdio.c,v
> retrieving revision 1.10
> diff -u -p -r1.10 stdio.c
> --- stdio/stdio.c 21 Sep 2016 04:38:56 -0000 1.10
> +++ stdio/stdio.c 5 May 2018 19:14:27 -0000
> @@ -32,6 +32,7 @@
> */
>
> #include <stdio.h>
> +#include <errno.h>
> #include <unistd.h>
> #include "local.h"
>
> @@ -49,7 +50,7 @@ __sread(void *cookie, char *buf, int n)
> /* if the read succeeded, update the current offset */
> if (ret >= 0)
> fp->_offset += ret;
> - else
> + else if (errno != EAGAIN)
> fp->_flags &= ~__SOFF; /* paranoia */
> return (ret);
> }
So, is anybody interested? Here is a program that demonstrates the bug.
The buffered and unbuffered input should produce same values here,
but it doesn't. The patch above fixes the situation.
--
WBR,
Vadim Zhukov
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int
main(int argc, char **argv) {
FILE *f1, *f2;
ssize_t nwf1, nwf2, nrf1, nrf2;
int pair1[2], pair2[2], rv = 0;
char msg[4096];
arc4random_buf(msg, sizeof(msg));
if (pipe2(pair1, O_NONBLOCK) == -1)
err(1, "pipe");
if (pipe2(pair2, O_NONBLOCK) == -1)
err(1, "pipe");
if ((f1 = fdopen(pair1[0], "r")) == NULL)
err(1, "fdopen");
if ((f2 = fdopen(pair2[0], "r")) == NULL)
err(1, "fdopen");
setvbuf(f2, NULL, _IONBF, 0);
nwf1 = write(pair1[1], msg, sizeof(msg));
printf("sent %zd bytes in buffered pipe\n", nwf1);
nwf2 = write(pair2[1], msg, sizeof(msg));
printf("sent %zd bytes in unbuffered pipe\n", nwf2);
printf("first read:\n");
nrf1 = fread(msg, 1, nwf1, f1);
printf(" buffered: fread()=>%zd, feof()=>%d, ferror()=>%d\n",
nrf1, feof(f1), ferror(f1));
if (feof(f1))
rv++;
if (ferror(f1))
rv++;
nrf2 = fread(msg, 1, nwf2, f2);
printf("unbuffered: fread()=>%zd, feof()=>%d, ferror()=>%d\n",
nrf2, feof(f2), ferror(f2));
if (feof(f2))
rv++;
if (ferror(f2))
rv++;
printf("second read:\n");
nrf1 = fread(msg, 1, nwf1, f1);
printf(" buffered: fread()=>%zd, feof()=>%d, ferror()=>%d\n",
nrf1, feof(f1), ferror(f1));
if (feof(f1))
rv++;
if (!ferror(f1))
rv++;
nrf2 = fread(msg, 1, nwf2, f2);
printf("unbuffered: fread()=>%zd, feof()=>%d, ferror()=>%d\n",
nrf2, feof(f2), ferror(f2));
if (feof(f2))
rv++;
if (!ferror(f2))
rv++;
printf("now closing write ends and doing third read:\n");
close(pair1[1]);
close(pair2[1]);
nrf1 = fread(msg, 1, nwf1, f1);
printf(" buffered: fread()=>%zd, feof()=>%d, ferror()=>%d\n",
nrf1, feof(f1), ferror(f1));
if (!feof(f1))
rv++;
if (!ferror(f1))
rv++;
nrf2 = fread(msg, 1, nwf2, f2);
printf("unbuffered: fread()=>%zd, feof()=>%d, ferror()=>%d\n",
nrf2, feof(f2), ferror(f2));
if (!feof(f2))
rv++;
if (!ferror(f2))
rv++;
fclose(f1);
fclose(f2);
close(pair1[0]);
close(pair2[0]);
return rv;
}