Hi,

Scott Cheloha wrote on Fri, Jul 28, 2017 at 08:33:16PM -0500:

> Unlikely to happen during normal use, but setvbuf(3) can fail
> to allocate your buffer:
> 
>       /* prog.c */
>       #include <stdio.h>
> 
>       int
>       main(int argc, char *argv[])
>       {
>               if (setvbuf(stdout, NULL, _IOFBF, 0))
>                       perror("setvbuf");   
>               return 0;
>       }
> 
> This seems to force the allocation failure on my box:
> 
>       $ ksh -c "ulimit -d 230 ; ./prog"
> 
> Not sure what the correct wording is for the ERRORS section.
> 
> Is it appropriate to specify that you can only get an allocation
> failure under the particular circumstances described? Or is it
> better to just write the standard line?, i.e.
> 
>       "Insufficient storage space is available."
> 
> As a reader I would want to know more precisely how the
> interface can fail.  Still seems kind of messy.  Maybe my
> wording could be improved.

Your patch is obviosly wrong.

If at all, the required wording would be something like

  The setvbuf() function may also fail and set errno for
  any of the errors specified for the routine malloc(3).

But that isn't fair game either.


Lets take a step back and look at the big picture, in four steps:

 1. The setvbuf(3) function is standardized by POSIX.
    POSIX fails to say that setvbuf() sets ENOMEM on allocation
    failure, which means that doing so is implementation
    dependent, see paragraph 1.2 of
    http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap01.html
    and paragraph 2.3 of
    http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
    In a nutshell, this means that POSIX provisions for error numbers
    are mostly advisory because implementations are allowed to differ
    in either direction, yet some POSIX provisions *are* mandatory.
    Figuring out what exactly is mandatory in each particular case
    is tricky.
    My impression is that not much care was taken when setting up
    the POSIX specifications for error numbers.  They are often
    obviously incomplete and sometimes unclear.

 2. The said, strictly speaking, if including the sentence
    shown above into ERRORS, we should also include this sentence
    below STANDARDS:

      The setvbuf() function conforms to ISO/IEC 9899:1999 ("ISO C99")
      and IEEE Std 1003.1-2008 ("POSIX.1").

      Setting errno for malloc(3) failure is an extension to
      the POSIX standard.

    Strictly speaking, both standards must be mentioned, because
    not all the behaviour described in the manual is mandated by
    C99.  For example, setting errno *at all* is already an extension
    to the C standard, yet it is mandated by POSIX.

    That said, almost none of our manual pages cite two standards
    in this way, and practically none properly document which
    error numbers are extensions.  Given the sloppiness of POSIX
    with respect to error numbers, that hardly comes as a surprise.

 3. That said, fiddling with these details is petty, there are much
    worse problems.  For example, the existing ERRORS section in
    setvbuf(3) is nothing but one big lie.  If the stream is not
    associated with a valid file descriptor, setvbuf() will *NOT*
    fail:

schwarze@isnote $ cat setvbuf.c                                                
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int
main(void)
{
        char     buf[16];
        FILE    *fp;
        int      fd;

        if ((fd = open("testfile.txt", O_RDONLY)) == -1)
                err(1, "open");
        if ((fp = fdopen(fd, "r")) == NULL)
                err(1, "fdopen");
        if (close(fd) == -1)
                err(1, "close");
        if (setvbuf(fp, buf, _IOFBF, 1024) != 0)
                err(1, "setvbuf");
        if (fgets(buf, sizeof(buf), fp) == NULL)
                err(1, "fgets");

        return 0;
}
schwarze@isnote $ make setvbuf
cc -O2 -pipe    -o setvbuf setvbuf.c 
schwarze@isnote $ ./setvbuf           
setvbuf: fgets: Bad file descriptor

    Note how setvbuf() suceeds and only fgets() fails.

    This is *not* a POSIX violation.  POSIX only says:

      The setvbuf() function may fail if:
      [EBADF] The file descriptor underlying stream is not valid.

    Yet, this is an outright documentation bug in our manual page.

 4. That said, fiddling with even that detail is petty, there are
    still worse problems.  Consider this program:

schwarze@isnote $ cat setvbuf_invalid.c                                        
#include <err.h>
#include <stdio.h>

int
main(void)
{
        FILE    *fp;

        if ((fp = fopen("testfile.txt", "r")) == NULL)
                err(1, "fopen");
        if (setvbuf(fp, NULL, _IOFBF, -1) != 0)
                err(1, "setvbuf");

        return 0;
}
schwarze@isnote $ make setvbuf_invalid    
cc -O2 -pipe    -o setvbuf_invalid setvbuf_invalid.c 
schwarze@isnote $ ./setvbuf_invalid    
setvbuf_invalid: setvbuf: Undefined error: 0

    A negative buffer size is invalid and causes setvbuf() to
    fail, but fails to set errno.

    Again, that's not a POSIX violation, POSIX allows additional
    implementation-dependent failure scenarios, and POSIX does
    not require setting errno.

    But arguably, failure to set errno is a bug in our implementation,
    because setting errno would clearly be useful, and our manual
    even suggests that it might happen.

    In any case, our manual is imprecise, neither making it clear
    what a programmer trying to write POSIX-conforming, portable
    code should expect, nor making it clear what our implementation
    actually does.


To summarize, the code is buggy (and almost certainly not only in
the one aspect i demonstrated), our manual is a mess, POSIX
is a mess, too, and you are applying lipstick to a pig.

All this (maybe except POSIX) can be fixed, but that's a huge
project because error handling is very, very messy all across
the C library similar to what i described for this example.


Patches of this kind are not helpful, in particular since you are
clearly not (yet) understanding what you are doing.  Checking such
patches causes substantial work for developers and may result in
erractic and inconsistent improvements at best.

If someone has a year of working time to spare to tackle error
handling in libc, it should be done systematically, not in such a
piecemeal manner, and it needs to be done carefully.

Yours,
  Ingo


> Index: lib/libc/stdio/setvbuf.3
> ===================================================================
> RCS file: /cvs/src/lib/libc/stdio/setvbuf.3,v
> retrieving revision 1.4
> diff -u -p -r1.4 setvbuf.3
> --- lib/libc/stdio/setvbuf.3  26 Nov 2014 18:16:32 -0000      1.4
> +++ lib/libc/stdio/setvbuf.3  29 Jul 2017 01:27:55 -0000
> @@ -138,6 +138,10 @@ function will fail if:
>  The
>  .Fa stream
>  specified is not associated with a valid file descriptor.
> +.It Bq Er ENOMEM
> +Line or block buffering is requested,
> +.Fa buf
> +is NULL, and insufficient storage space is available.
>  .El
>  .Sh SEE ALSO
>  .Xr fclose 3 ,

Reply via email to