This is the mail archive of the glibc-bugs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug stdio/20677] New: perror() changes orientation of stream when it is redirected


https://sourceware.org/bugzilla/show_bug.cgi?id=20677

            Bug ID: 20677
           Summary: perror() changes orientation of stream when it is
                    redirected
           Product: glibc
           Version: 2.24
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: stdio
          Assignee: unassigned at sourceware dot org
          Reporter: igor.liferenko at gmail dot com
  Target Milestone: ---

Posix insists that the orientation of stderr not be changed by perror, even if
stderr is not yet oriented.

Following is the quote from "XSH 2.5 Standard I/O Streams" [1]:

The perror(), psiginfo(), and psignal() functions shall behave as described
above for the byte output functions if the stream is already byte-oriented, and
shall behave as described above for the wide-character output functions if the
stream is already wide-oriented. If the stream has no orientation, they shall
behave as described for the byte output functions except that they shall not
change the orientation of the stream.
And glibc attempts to implement Posix semantics. Unfortunately, it doesn't
quite get it right.

Here is also quote from "perror()" reference page [2]:

The perror() function shall not change the orientation of the standard error
stream.

And glibc attempts to implement Posix semantics. Unfortunately, it doesn't
quite get it right.

Following are the tests when stderr is wide-oriented, multibyte-oriented and
not oriented, prior to calling perror(). Tests 1) and 2) are OK. The issue is
in test 3).

1) stderr is wide-oriented:

#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
  fwide(stderr, 1);
  errno = EINVAL;
  perror("");
  int x = fwide(stderr, 0);
  printf("fwide: %d\n",x);
  return 0;
}

$ ./a.out
Invalid argument
fwide: 1
$ ./a.out 2>/dev/null
fwide: 1

2) stderr is multibyte-oriented:

#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
  fwide(stderr, -1);
  errno = EINVAL;
  perror("");
  int x = fwide(stderr, 0);
  printf("fwide: %d\n",x);
  return 0;
}

$ ./a.out
Invalid argument
fwide: -1
$ ./a.out 2>/dev/null
fwide: -1

3) stderr is not oriented:

#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
  printf("initial fwide: %d\n", fwide(stderr, 0));
  errno = EINVAL;
  perror("");
  int x = fwide(stderr, 0);
  printf("fwide: %d\n", x);
  return 0;
}

$ ./a.out
initial fwide: 0
Invalid argument
fwide: 0
$ ./a.out 2>/dev/null
initial fwide: 0
fwide: -1

Of course, it is impossible to write to a stream without setting its
orientation. So in an attempt to comply with this curious requirement, glibc
attempts to make a new stream based on the same fd as stderr by using dup().

fileno() extracts the fd from a standard C library stream. dup() takes an fd,
duplicates it, and returns the number of the copy. And fdopen() creates a
standard C library stream from an fd. In short, that doesn't reopen stderr;
rather, it creates (or attempts to create) a copy of stderr which can be
written to without affecting the orientation of stderr.

Unfortunately, it doesn't work reliably because of the mode:

    fp = fdopen(fd, "w+");

That attempts to open a stream which allows both reading and writing. And it
will work with the original stderr, which is just a copy of the console fd,
originally opened for both reading and writing. But when you bind stderr to
some other device with a redirect:

$ ./a.out 2>/dev/null

you are passing the executable an fd opened only for output. And  fdopen won't
let you get away with that:

The application shall ensure that the mode of the stream as expressed by the
mode argument is allowed by the file access mode of the open file description
to which fildes refers.

The glibc implementation of fdopen actually checks, and returns NULL with errno
set to EINVAL if you specify a mode which requires access rights not available
to the fd.

I don't know why the glibc code uses "w+" as a mode argument, since it has no
intention of reading from stderr.

Actually, you could just write directly to the new fd using write instead of
fwrite, so I don't think fdopen is actually necessary at all.

[1]
http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05

[2] http://pubs.opengroup.org/onlinepubs/9699919799/functions/perror.html

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]