Bug#980764: libc6-dev: wrong return value for fputs when STDOUT_FILENO was closed()

2021-02-07 Thread Florian Weimer
* Morel Bérenger:

>> * Bérenger:  
> ...
>> Why do you think this is a bug?  
>
> POSIX 10031-2017 standard says:

POSIX requires that if you manipulate the underlying file descriptor
of a stream, you first need to call fseek when using the stream again.
Your example code does not do that, so it's not following POSIX
requirements for these interfaces.

But there's another reason why POSIX requirements are met by the glibc
implemetnation.

> In the error section, we can read that it can return the same errors
> (in errno) as fputc, which itself says, as for errors:
>
>> [EBADF] The file descriptor underlying stream is not a valid file
>> descriptor open for writing.  

The error is conditional:

| The fputc() function shall fail if either the stream is unbuffered
| or the stream's buffer needs to be flushed, and: […]

As I explained, the stream is buffered because it is not connected to
a terminal.



Bug#980764: libc6-dev: wrong return value for fputs when STDOUT_FILENO was closed()

2021-02-02 Thread Morel Bérenger
Le Fri, 22 Jan 2021 23:45:45 +0100,
Florian Weimer  a écrit :

Hello.
Sorry for the delay, I completely forgot about this issue.

> * Bérenger:  
...
> Why do you think this is a bug?  

POSIX 10031-2017 standard says:

> RETURN VALUE  
Upon successful completion, the dprintf( ), fprintf( ), and printf( )
functions shall return the number of bytes transmitted.
Upon successful completion, the sprintf( ) function shall return the
number of bytes written to s, excluding the terminating null byte.
>  
Upon successful completion, the snprintf( ) function shall return the
number of bytes that would be written to s had n been sufficiently
large excluding the terminating null byte.
>  
If the value of n is zero on a call to snprintf( ), nothing shall be
written, the number of bytes that would have been written had n been
sufficiently large excluding the terminating null shall be returned,
and s may be a null pointer.

I changed my code so that it stores printf's result. It does not
contains 0, which is the real number of bytes transmitted (none were),
but the number of bytes it should have transmitted.

In the error section, we can read that it can return the same errors
(in errno) as fputc, which itself says, as for errors:

> [EBADF] The file descriptor underlying stream is not a valid file
> descriptor open for writing.  

This is clearly the case here. The underlying file descriptor *is*
closed. Thus, fputc (thus fprintf) functions should return -1 and have
errno set at EBADF.

I do not have access to older versions of POSIX nor to C standard.

According to wikipedia, "Debian 10 (Buster) was released on 6 July
2019;" which is at least 1 year after the POSIX standard I put a
reference to was out.

I must confess I didn't checked the POSIX standard on this question
before writting this answer, because the manpage clearly states that
"Upon successful return, these functions return the number of
characters printed" which is not the case here.

I do note that the manpage does not mentions errors returned nor being
conform to POSIX-2017: it says:

> CONFORMING TO  
   fprintf(), printf(), sprintf(), vprintf(), vfprintf(),
vsprintf(): POSIX.1-2001, POSIX.1-2008, C89, C99.
>  
   snprintf(), vsnprintf(): POSIX.1-2001, POSIX.1-2008, C99.

With those informations, I really think this is a glibc bug (not
present in muslc since it do returns -1 and sets errno at -9, which is
the defined value of EBADF).


PS: sorry for putting you in "To" field, Florian Weimer, did a "reply",
instead of "reply to all"



Bug#980764: libc6-dev: wrong return value for fputs when STDOUT_FILENO was closed()

2021-01-22 Thread Florian Weimer
* Bérenger:

> When running following code:
>
> ```C
> #include 
> #include 
> #include 
>
> int main()
> {
>   close( STDIN_FILENO );
>   close( STDOUT_FILENO );
>   int fd = dup( STDERR_FILENO );
>   close( STDERR_FILENO );
>   if( -1 == fprintf( stdout, "%d\n", fd ) )
>   {
>   return -1;
>   }
>
>   char s[] = "should fail\n";
>   if( -1 == write( STDOUT_FILENO, s, sizeof( s ) ) )
>   {
>   return -2;
>   }
>   return EXIT_SUCCESS;
> }
> ```
>
> built with glibc, the program returns 254. When built with muslc, it
> returns the expected value of 255.
>
> I believe glibc's behavior here is wrong. From what I could get by using
> strace, it seems that the 1st printf's write() call is ran _after_ the
> 2nd one, even when adding a call to fflush( stdout ) right after the
> printf.

The reason for the glibc behavior is that stdout ends up as a buffered
stream because it is not a terminal.  Why do you think this is a bug?



Bug#980764: libc6-dev: wrong return value for fputs when STDOUT_FILENO was closed()

2021-01-21 Thread Bérenger
Package: libc6-dev
Version: 2.28-10
Severity: normal

When running following code:

```C
#include 
#include 
#include 

int main()
{
close( STDIN_FILENO );
close( STDOUT_FILENO );
int fd = dup( STDERR_FILENO );
close( STDERR_FILENO );
if( -1 == fprintf( stdout, "%d\n", fd ) )
{
return -1;
}

char s[] = "should fail\n";
if( -1 == write( STDOUT_FILENO, s, sizeof( s ) ) )
{
return -2;
}
return EXIT_SUCCESS;
}
```

built with glibc, the program returns 254. When built with muslc, it
returns the expected value of 255.

I believe glibc's behavior here is wrong. From what I could get by using
strace, it seems that the 1st printf's write() call is ran _after_ the
2nd one, even when adding a call to fflush( stdout ) right after the
printf.

A way to make the code behaving as one would expect is to add a fprintf
call before closing descriptor. It then behaves as expected with both
libCs.

-- System Information:
Debian Release: 10.7
  APT prefers stable
  APT policy: (990, 'stable'), (500, 'stable-updates')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 4.19.0-13-amd64 (SMP w/4 CPU cores)
Kernel taint flags: TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE
Locale: LANG=fr_FR.UTF-8, LC_CTYPE=fr_FR.UTF-8 (charmap=UTF-8), 
LANGUAGE=fr_FR.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: runit (via /run/runit.stopit)

Versions of packages libc6-dev depends on:
ii  libc-dev-bin2.28-10
ii  libc6   2.28-10
ii  linux-libc-dev  4.19.160-2

libc6-dev recommends no packages.

Versions of packages libc6-dev suggests:
pn  glibc-doc 
ii  manpages-dev  4.16-2

-- no debconf information