Hi Bruno,

Bruno Haible <[email protected]> writes:

>> The issue here, and likely for the other cases, is that Musl's fclose
>> returns without indicating an error.
>> ...
>> I'm not sure what the proper fix would be here.
>
> The first step should be to create a reproducing test program that does
> only a few system calls.
>
> The second step then should be to decide whether musl violates POSIX,
> or whether it merely violates GNU expectations.
>
> Then, it sounds like a Gnulib override of fclose() is in order.
>
> Or maybe Gnulib already overrides fclose() on musl, and there is a
> bug in this override?

Sorry, I meant to respond to this sooner. I think the following program
should demonstrate the issue:

    $ cat main.c
    #include <stdio.h>
    int
    main (void)
    {
      int v1 = printf ("%s %s\n", "hello", " 1");
      int v2 = fflush (stdout);
      int v3 = fclose (stdout);
      fprintf (stderr, "v1: %d\n", v1);
      fprintf (stderr, "v2: %d\n", v2);
      fprintf (stderr, "v3: %d\n", v3);
      return 0;
    }

Here is the result when standard output is connected to a terminal:

    $ ldd --version | head -n 1  && gcc main.c && ./a.out
    ldd (GNU libc) 2.42
    hello  1
    v1: 9
    v2: 0
    v3: 0

    $ ldd --version 2>&1 | head -n 2 && gcc main.c && ./a.out
    musl libc (i386)
    Version 1.2.5
    hello  1
    v1: 9
    v2: 0
    v3: 0

Here is the result when standard output is redirected to /dev/full:

    $ ldd --version | head -n 1  && gcc main.c && ./a.out > /dev/full
    ldd (GNU libc) 2.42
    v1: 9
    v2: -1
    v3: 0
    
    $ ldd --version 2>&1 | head -n 2 && gcc main.c && ./a.out > /dev/full
    musl libc (i386)
    Version 1.2.5
    v1: -1
    v2: 0
    v3: 0

This confused me for a bit until I enabled buffering before the printf
call on musl:

    $ sed -i '5i\  setvbuf (stdout, NULL, _IOFBF, 0);' main.c

Afterwards, we can run it and see the same result as glibc:

    $ ldd --version 2>&1 | head -n 2 && gcc main.c && ./a.out > /dev/full
    musl libc (i386)
    Version 1.2.5
    v1: 9
    v2: -1
    v3: 0

POSIX says the following [1]:

    When opened, stderr shall not be fully buffered; stdin and stdout
    shall be fully buffered if and only if the file descriptor
    associated with the stream is determined not to be associated with
    an interactive device.

Which seems to not be the case for musl. We can confirm that is the case
by adding another call to printf:

    $ sed -i '6i\  printf ("%s %s\\n", "hello", " 2");' main.c
    $ gcc main.c
    $ strace -e trace='/write' ./a.out > /dev/full
    writev(1, [{iov_base="hello  1", iov_len=8}, {iov_base="\n", iov_len=1}], 
2) = -1 ENOSPC (No space left on device)
    writev(1, [{iov_base="hello  2\n", iov_len=9}, {iov_base=NULL, iov_len=0}], 
2) = -1 ENOSPC (No space left on device)
    writev(2, [{iov_base="v1: -1\n", iov_len=7}, {iov_base=NULL, iov_len=0}], 
2v1: -1
    ) = 7
    writev(2, [{iov_base="v2: -1\n", iov_len=7}, {iov_base=NULL, iov_len=0}], 
2v2: -1
    ) = 7
    writev(2, [{iov_base="v3: 0\n", iov_len=6}, {iov_base=NULL, iov_len=0}], 
2v3: 0
    ) = 6
    +++ exited with 0 +++

Whereas the same program on glibc does the following:

    $ strace -e trace='/write' ./a.out > /dev/full
    write(1, "hello  1\nhello  2\n", 18)    = -1 ENOSPC (No space left on 
device)
    write(2, "v1: 9\n", 6v1: 9
    )                  = 6
    write(2, "v2: -1\n", 7v2: -1
    )                 = 7
    write(2, "v3: 0\n", 6v3: 0
    )                  = 6
    +++ exited with 0 +++

Collin

[1] https://pubs.opengroup.org/onlinepubs/9799919799/functions/stdin.html

Reply via email to