This is the mail archive of the
glibc-bugs@sourceware.org
mailing list for the glibc project.
[Bug stdio/20677] New: perror() changes orientation of stream when it is redirected
- From: "igor.liferenko at gmail dot com" <sourceware-bugzilla at sourceware dot org>
- To: glibc-bugs at sourceware dot org
- Date: Mon, 10 Oct 2016 05:41:38 +0000
- Subject: [Bug stdio/20677] New: perror() changes orientation of stream when it is redirected
- Auto-submitted: auto-generated
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.