#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;
}

Reply via email to