Message ID | CALoOobOzXze3Ncu8FHhJwjttQnssrXG=XwS1gd=N7Cfgk=vadw@mail.gmail.com |
---|---|
State | New |
Headers | show |
Series | libio: use stdout in puts and putchar, etc [BZ #24051]. | expand |
* Paul Pluzhnikov: > diff --git a/libio/tst-bz24051.c b/libio/tst-bz24051.c > new file mode 100644 > index 0000000000..52c7a64b5b > --- /dev/null > +++ b/libio/tst-bz24051.c > @@ -0,0 +1,79 @@ > +/* Prevent putchar -> _IO_putc inline expansion. */ > +#define __NO_INLINE__ #pragma GCC optimize ("O0") seems prudent here as well, to inhibit any printf uptimizations. > + rewind (stdout); > + TEST_VERIFY_EXIT (fgetws (buf, sizeof (buf), stdout) != NULL); > + TEST_VERIFY (wcscmp (buf, L"abcdef") == 0); You should use array_length (buf) here (and include <array_length.h>). fgetws does not expect a ybte byte count. Rest of the patch looks okay to me. (Siddhesh needs to ack this because of the freeze.) Thanks, Florian
On 03/01/19 6:38 PM, Florian Weimer wrote: > * Paul Pluzhnikov: > >> diff --git a/libio/tst-bz24051.c b/libio/tst-bz24051.c >> new file mode 100644 >> index 0000000000..52c7a64b5b >> --- /dev/null >> +++ b/libio/tst-bz24051.c >> @@ -0,0 +1,79 @@ > >> +/* Prevent putchar -> _IO_putc inline expansion. */ >> +#define __NO_INLINE__ > > #pragma GCC optimize ("O0") seems prudent here as well, to inhibit any > printf uptimizations. > >> + rewind (stdout); >> + TEST_VERIFY_EXIT (fgetws (buf, sizeof (buf), stdout) != NULL); >> + TEST_VERIFY (wcscmp (buf, L"abcdef") == 0); > > You should use array_length (buf) here (and include <array_length.h>). > fgetws does not expect a ybte byte count. > > Rest of the patch looks okay to me. (Siddhesh needs to ack this because > of the freeze.) Fine from the freeze perspective, but this needs a more descriptive commit description. Siddhesh
On Thu, Jan 3, 2019 at 5:17 AM Siddhesh Poyarekar <siddhesh@gotplt.org> wrote: > > On 03/01/19 6:38 PM, Florian Weimer wrote: > > You should use array_length (buf) here (and include <array_length.h>). > > fgetws does not expect a ybte byte count. Thanks for catching that. > Fine from the freeze perspective, but this needs a more descriptive > commit description. Revised patch attached. With respect to timing, I would prefer to postpone pushing this to master until after 2.29 is released, so that both stdout and stdin (which I'll fix next) are fixed together in 2.30. Alternatively, I can extend this patch to also cover stdin. Maybe that's actually better? Thanks!
* Paul Pluzhnikov: > On Thu, Jan 3, 2019 at 5:17 AM Siddhesh Poyarekar <siddhesh@gotplt.org> wrote: >> >> On 03/01/19 6:38 PM, Florian Weimer wrote: > >> > You should use array_length (buf) here (and include <array_length.h>). >> > fgetws does not expect a ybte byte count. > > Thanks for catching that. > >> Fine from the freeze perspective, but this needs a more descriptive >> commit description. > > Revised patch attached. > > With respect to timing, I would prefer to postpone pushing this to > master until after 2.29 is released, so that both stdout and stdin > (which I'll fix next) are fixed together in 2.30. I'm fine with incremental progress here. I've tried and it's also possible to get rid of those secondary variables entirely, I think, but this is definitely something for after the release (and your two patches went in; the patch will be much smaller after that). Thanks, Florian diff --git a/debug/gets_chk.c b/debug/gets_chk.c index 4b5e13d6a6..fc6557a7f7 100644 --- a/debug/gets_chk.c +++ b/debug/gets_chk.c @@ -37,8 +37,8 @@ __gets_chk (char *buf, size_t size) if (size == 0) __chk_fail (); - _IO_acquire_lock (_IO_stdin); - ch = _IO_getc_unlocked (_IO_stdin); + _IO_acquire_lock (stdin); + ch = _IO_getc_unlocked (stdin); if (ch == EOF) { retval = NULL; @@ -51,24 +51,24 @@ __gets_chk (char *buf, size_t size) /* This is very tricky since a file descriptor may be in the non-blocking mode. The error flag doesn't mean much in this case. We return an error only when there is a new error. */ - int old_error = _IO_stdin->_flags & _IO_ERR_SEEN; - _IO_stdin->_flags &= ~_IO_ERR_SEEN; + int old_error = stdin->_flags & _IO_ERR_SEEN; + stdin->_flags &= ~_IO_ERR_SEEN; buf[0] = (char) ch; - count = _IO_getline (_IO_stdin, buf + 1, size - 1, '\n', 0) + 1; - if (_IO_stdin->_flags & _IO_ERR_SEEN) + count = _IO_getline (stdin, buf + 1, size - 1, '\n', 0) + 1; + if (stdin->_flags & _IO_ERR_SEEN) { retval = NULL; goto unlock_return; } else - _IO_stdin->_flags |= old_error; + stdin->_flags |= old_error; } if (count >= size) __chk_fail (); buf[count] = 0; retval = buf; unlock_return: - _IO_release_lock (_IO_stdin); + _IO_release_lock (stdin); return retval; } diff --git a/libio/fileops.c b/libio/fileops.c index 43e33820e3..d2070a856e 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -501,13 +501,13 @@ _IO_new_file_underflow (FILE *fp) traditional Unix systems did this for stdout. stderr better not be line buffered. So we do just that here explicitly. --drepper */ - _IO_acquire_lock (_IO_stdout); + _IO_acquire_lock (stdout); - if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) + if ((stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) == (_IO_LINKED | _IO_LINE_BUF)) - _IO_OVERFLOW (_IO_stdout, EOF); + _IO_OVERFLOW (stdout, EOF); - _IO_release_lock (_IO_stdout); + _IO_release_lock (stdout); } _IO_switch_to_get_mode (fp); diff --git a/libio/getchar.c b/libio/getchar.c index 7e385305ff..90dea38ddb 100644 --- a/libio/getchar.c +++ b/libio/getchar.c @@ -33,11 +33,11 @@ int getchar (void) { int result; - if (!_IO_need_lock (_IO_stdin)) - return _IO_getc_unlocked (_IO_stdin); - _IO_acquire_lock (_IO_stdin); - result = _IO_getc_unlocked (_IO_stdin); - _IO_release_lock (_IO_stdin); + if (!_IO_need_lock (stdin)) + return _IO_getc_unlocked (stdin); + _IO_acquire_lock (stdin); + result = _IO_getc_unlocked (stdin); + _IO_release_lock (stdin); return result; } diff --git a/libio/getchar_u.c b/libio/getchar_u.c index 9af50be717..a933d9aad0 100644 --- a/libio/getchar_u.c +++ b/libio/getchar_u.c @@ -32,5 +32,5 @@ int getchar_unlocked (void) { - return _IO_getc_unlocked (_IO_stdin); + return _IO_getc_unlocked (stdin); } diff --git a/libio/getwchar.c b/libio/getwchar.c index baac916fdd..b6155fa276 100644 --- a/libio/getwchar.c +++ b/libio/getwchar.c @@ -33,8 +33,8 @@ wint_t getwchar (void) { wint_t result; - _IO_acquire_lock (_IO_stdin); - result = _IO_getwc_unlocked (_IO_stdin); - _IO_release_lock (_IO_stdin); + _IO_acquire_lock (stdin); + result = _IO_getwc_unlocked (stdin); + _IO_release_lock (stdin); return result; } diff --git a/libio/getwchar_u.c b/libio/getwchar_u.c index 1490bbe79f..23fa18b007 100644 --- a/libio/getwchar_u.c +++ b/libio/getwchar_u.c @@ -32,5 +32,5 @@ wint_t getwchar_unlocked (void) { - return _IO_getwc_unlocked (_IO_stdin); + return _IO_getwc_unlocked (stdin); } diff --git a/libio/iofclose.c b/libio/iofclose.c index 9b39a6cc4e..8a80dd0b78 100644 --- a/libio/iofclose.c +++ b/libio/iofclose.c @@ -71,12 +71,7 @@ _IO_new_fclose (FILE *fp) if (_IO_have_backup (fp)) _IO_free_backup_area (fp); } - if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr) - { - fp->_flags = 0; - free(fp); - } - + _IO_deallocate_file (fp); return status; } diff --git a/libio/iofwide.c b/libio/iofwide.c index 6676ad5e53..247cfde3d0 100644 --- a/libio/iofwide.c +++ b/libio/iofwide.c @@ -87,8 +87,7 @@ _IO_fwide (FILE *fp, int mode) mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1); #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) - if (__builtin_expect (&_IO_stdin_used == NULL, 0) - && (fp == _IO_stdin || fp == _IO_stdout || fp == _IO_stderr)) + if (__glibc_unlikely (&_IO_stdin_used == NULL) && _IO_legacy_file (fp)) /* This is for a stream in the glibc 2.0 format. */ return -1; #endif diff --git a/libio/iogets.c b/libio/iogets.c index 2525056781..6691e4c0fc 100644 --- a/libio/iogets.c +++ b/libio/iogets.c @@ -34,8 +34,8 @@ _IO_gets (char *buf) int ch; char *retval; - _IO_acquire_lock (_IO_stdin); - ch = _IO_getc_unlocked (_IO_stdin); + _IO_acquire_lock (stdin); + ch = _IO_getc_unlocked (stdin); if (ch == EOF) { retval = NULL; @@ -48,22 +48,22 @@ _IO_gets (char *buf) /* This is very tricky since a file descriptor may be in the non-blocking mode. The error flag doesn't mean much in this case. We return an error only when there is a new error. */ - int old_error = _IO_stdin->_flags & _IO_ERR_SEEN; - _IO_stdin->_flags &= ~_IO_ERR_SEEN; + int old_error = stdin->_flags & _IO_ERR_SEEN; + stdin->_flags &= ~_IO_ERR_SEEN; buf[0] = (char) ch; - count = _IO_getline (_IO_stdin, buf + 1, INT_MAX, '\n', 0) + 1; - if (_IO_stdin->_flags & _IO_ERR_SEEN) + count = _IO_getline (stdin, buf + 1, INT_MAX, '\n', 0) + 1; + if (stdin->_flags & _IO_ERR_SEEN) { retval = NULL; goto unlock_return; } else - _IO_stdin->_flags |= old_error; + stdin->_flags |= old_error; } buf[count] = 0; retval = buf; unlock_return: - _IO_release_lock (_IO_stdin); + _IO_release_lock (stdin); return retval; } diff --git a/libio/iolibio.h b/libio/iolibio.h index 2642d71e4f..9561833655 100644 --- a/libio/iolibio.h +++ b/libio/iolibio.h @@ -58,8 +58,6 @@ extern int _IO_vsscanf (const char *, const char *, __gnuc_va_list) __THROW; == _IO_pos_BAD ? EOF : 0) #define _IO_rewind(FILE) \ (void) _IO_seekoff_unlocked (FILE, 0, 0, _IOS_INPUT|_IOS_OUTPUT) -#define _IO_vprintf(FORMAT, ARGS) \ - _IO_vfprintf (_IO_stdout, FORMAT, ARGS) #define _IO_freopen(FILENAME, MODE, FP) \ (_IO_file_close_it (FP), \ _IO_file_fopen (FP, FILENAME, MODE, 1)) diff --git a/libio/ioputs.c b/libio/ioputs.c index 04ae323c6d..319e551de5 100644 --- a/libio/ioputs.c +++ b/libio/ioputs.c @@ -33,15 +33,15 @@ _IO_puts (const char *str) { int result = EOF; size_t len = strlen (str); - _IO_acquire_lock (_IO_stdout); + _IO_acquire_lock (stdout); - if ((_IO_vtable_offset (_IO_stdout) != 0 - || _IO_fwide (_IO_stdout, -1) == -1) - && _IO_sputn (_IO_stdout, str, len) == len - && _IO_putc_unlocked ('\n', _IO_stdout) != EOF) + if ((_IO_vtable_offset (stdout) != 0 + || _IO_fwide (stdout, -1) == -1) + && _IO_sputn (stdout, str, len) == len + && _IO_putc_unlocked ('\n', stdout) != EOF) result = MIN (INT_MAX, len + 1); - _IO_release_lock (_IO_stdout); + _IO_release_lock (stdout); return result; } diff --git a/libio/libio.h b/libio/libio.h index d21162aab0..872574cfb7 100644 --- a/libio/libio.h +++ b/libio/libio.h @@ -185,9 +185,6 @@ struct _IO_FILE_plus; extern struct _IO_FILE_plus _IO_2_1_stdin_; extern struct _IO_FILE_plus _IO_2_1_stdout_; extern struct _IO_FILE_plus _IO_2_1_stderr_; -extern FILE *_IO_stdin attribute_hidden; -extern FILE *_IO_stdout attribute_hidden; -extern FILE *_IO_stderr attribute_hidden; struct _IO_cookie_file; diff --git a/libio/libioP.h b/libio/libioP.h index 8c75f15167..1c434ec3a1 100644 --- a/libio/libioP.h +++ b/libio/libioP.h @@ -817,7 +817,35 @@ extern int _IO_vscanf (const char *, va_list) __THROW; # endif #endif -extern struct _IO_fake_stdiobuf _IO_stdin_buf, _IO_stdout_buf, _IO_stderr_buf; +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) +/* See oldstdfiles.c. These are the old stream variables. */ +extern struct _IO_FILE_plus _IO_stdin_; +extern struct _IO_FILE_plus _IO_stdout_; +extern struct _IO_FILE_plus _IO_stderr_; + +static inline bool +_IO_legacy_file (FILE *fp) +{ + return fp == (FILE *) &_IO_stdin_ || fp == (FILE *) &_IO_stdout_ + || fp == (FILE *) &_IO_stderr_; +} +#endif + +/* Deallocate a stream if it is heap-allocated. Preallocated + stdin/stdout/stderr streams are not deallocated. */ +static inline void +_IO_deallocate_file (FILE *fp) +{ + /* The current stream variables. */ + if (fp == (FILE *) &_IO_2_1_stdin_ || fp == (FILE *) &_IO_2_1_stdout_ + || fp == (FILE *) &_IO_2_1_stderr_) + return; +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) + if (_IO_legacy_file (fp)) + return; +#endif + free (fp); +} #ifdef IO_DEBUG # define CHECK_FILE(FILE, RET) do { \ diff --git a/libio/oldfileops.c b/libio/oldfileops.c index 4d6c5e3fe7..10f2205e8a 100644 --- a/libio/oldfileops.c +++ b/libio/oldfileops.c @@ -109,10 +109,7 @@ _IO_old_file_init_internal (struct _IO_FILE_plus *fp) - (int) sizeof (struct _IO_FILE_complete)); fp->file._fileno = -1; - if (__builtin_expect (&_IO_stdin_used != NULL, 1) - || (fp != (struct _IO_FILE_plus *) _IO_stdin - && fp != (struct _IO_FILE_plus *) _IO_stdout - && fp != (struct _IO_FILE_plus *) _IO_stderr)) + if (&_IO_stdin_used != NULL && !_IO_legacy_file ((FILE *) fp)) /* The object is dynamically allocated and large enough. Initialize the _mode element as well. */ ((struct _IO_FILE_complete *) fp)->_mode = -1; diff --git a/libio/oldiofclose.c b/libio/oldiofclose.c index e4cbf88566..be5044c3bd 100644 --- a/libio/oldiofclose.c +++ b/libio/oldiofclose.c @@ -58,12 +58,7 @@ _IO_old_fclose (FILE *fp) _IO_FINISH (fp); if (_IO_have_backup (fp)) _IO_free_backup_area (fp); - if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr) - { - fp->_flags = 0; - free(fp); - } - + _IO_deallocate_file (fp); return status; } diff --git a/libio/oldstdfiles.c b/libio/oldstdfiles.c index 524e260b7e..a8fa2a56dc 100644 --- a/libio/oldstdfiles.c +++ b/libio/oldstdfiles.c @@ -78,13 +78,12 @@ _IO_check_libio (void) if (&_IO_stdin_used == NULL) { /* We are using the old one. */ - _IO_stdin = stdin = (FILE *) &_IO_stdin_; - _IO_stdout = stdout = (FILE *) &_IO_stdout_; - _IO_stderr = stderr = (FILE *) &_IO_stderr_; + stdin = (FILE *) &_IO_stdin_; + stdout = (FILE *) &_IO_stdout_; + stderr = (FILE *) &_IO_stderr_; _IO_list_all = &_IO_stderr_; - _IO_stdin->_vtable_offset = _IO_stdout->_vtable_offset = - _IO_stderr->_vtable_offset = stdin->_vtable_offset = - stdout->_vtable_offset = stderr->_vtable_offset = + stdin->_vtable_offset = stdout->_vtable_offset + = stderr->_vtable_offset = ((int) sizeof (struct _IO_FILE) - (int) sizeof (struct _IO_FILE_complete)); } diff --git a/libio/putchar.c b/libio/putchar.c index 665f46685a..a3f4200cdb 100644 --- a/libio/putchar.c +++ b/libio/putchar.c @@ -24,9 +24,9 @@ int putchar (int c) { int result; - _IO_acquire_lock (_IO_stdout); - result = _IO_putc_unlocked (c, _IO_stdout); - _IO_release_lock (_IO_stdout); + _IO_acquire_lock (stdout); + result = _IO_putc_unlocked (c, stdout); + _IO_release_lock (stdout); return result; } diff --git a/libio/putchar_u.c b/libio/putchar_u.c index 37d03ad364..1eebf0fc8f 100644 --- a/libio/putchar_u.c +++ b/libio/putchar_u.c @@ -23,6 +23,6 @@ int putchar_unlocked (int c) { - CHECK_FILE (_IO_stdout, EOF); - return _IO_putc_unlocked (c, _IO_stdout); + CHECK_FILE (stdout, EOF); + return _IO_putc_unlocked (c, stdout); } diff --git a/libio/putwchar.c b/libio/putwchar.c index 8d6b6a4df0..1f5c4176f8 100644 --- a/libio/putwchar.c +++ b/libio/putwchar.c @@ -22,8 +22,8 @@ wint_t putwchar (wchar_t wc) { wint_t result; - _IO_acquire_lock (_IO_stdout); - result = _IO_putwc_unlocked (wc, _IO_stdout); - _IO_release_lock (_IO_stdout); + _IO_acquire_lock (stdout); + result = _IO_putwc_unlocked (wc, stdout); + _IO_release_lock (stdout); return result; } diff --git a/libio/putwchar_u.c b/libio/putwchar_u.c index cfb46fc253..d943220031 100644 --- a/libio/putwchar_u.c +++ b/libio/putwchar_u.c @@ -21,6 +21,6 @@ wint_t putwchar_unlocked (wchar_t wc) { - CHECK_FILE (_IO_stdout, WEOF); - return _IO_putwc_unlocked (wc, _IO_stdout); + CHECK_FILE (stdout, WEOF); + return _IO_putwc_unlocked (wc, stdout); } diff --git a/libio/vscanf.c b/libio/vscanf.c index 342dc3effc..526d486399 100644 --- a/libio/vscanf.c +++ b/libio/vscanf.c @@ -37,6 +37,6 @@ int _IO_vscanf (const char *format, va_list args) { - return __vfscanf_internal (_IO_stdin, format, args, 0); + return __vfscanf_internal (stdin, format, args, 0); } ldbl_weak_alias (_IO_vscanf, vscanf) diff --git a/libio/vwscanf.c b/libio/vwscanf.c index 696e4c5c99..7b1903c71d 100644 --- a/libio/vwscanf.c +++ b/libio/vwscanf.c @@ -35,6 +35,6 @@ int __vwscanf (const wchar_t *format, va_list args) { - return __vfwscanf_internal (_IO_stdin, format, args, 0); + return __vfwscanf_internal (stdin, format, args, 0); } ldbl_strong_alias (__vwscanf, vwscanf) diff --git a/libio/wfileops.c b/libio/wfileops.c index 78f20486e5..0367643703 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -208,13 +208,13 @@ _IO_wfile_underflow (FILE *fp) traditional Unix systems did this for stdout. stderr better not be line buffered. So we do just that here explicitly. --drepper */ - _IO_acquire_lock (_IO_stdout); + _IO_acquire_lock (stdout); - if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) + if ((stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) == (_IO_LINKED | _IO_LINE_BUF)) - _IO_OVERFLOW (_IO_stdout, EOF); + _IO_OVERFLOW (stdout, EOF); - _IO_release_lock (_IO_stdout); + _IO_release_lock (stdout); } _IO_switch_to_get_mode (fp);
diff --git a/ChangeLog b/ChangeLog index 75c80e6f02..70c6176f6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2019-01-02 Paul Pluzhnikov <ppluzhnikov@google.com> + + [BZ #24051] + * libio/ioputs.c (_IO_puts): Use stdout instead of _IO_stdout. + * libio/fileops.c (_IO_new_file_underflow): Likewise + * libio/wfileops.c (_IO_wfile_underflow): Likewise + * libio/putchar.c (putchar): Likewise. + * libio/putchar_u.c (putchar_unlocked): Likewise. + * libio/putwchar.c (putchar): Likewise. + * libio/putwchar_u.c (putwchar_unlocked): Likewise. + * libio/tst-bz24051.c: New test. + * libio/Makefile (tests): Add tst-bz24051 + 2019-01-02 Joseph Myers <joseph@codesourcery.com> * sysdeps/unix/sysv/linux/tst-mman-consts.py (main): Expect diff --git a/libio/Makefile b/libio/Makefile index 5bee83e55c..dbeba88fc0 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -65,7 +65,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \ tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \ tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \ tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \ - tst-sprintf-ub tst-sprintf-chk-ub + tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tests-internal = tst-vtables tst-vtables-interposed tst-readline diff --git a/libio/fileops.c b/libio/fileops.c index 43e33820e3..d2070a856e 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -501,13 +501,13 @@ _IO_new_file_underflow (FILE *fp) traditional Unix systems did this for stdout. stderr better not be line buffered. So we do just that here explicitly. --drepper */ - _IO_acquire_lock (_IO_stdout); + _IO_acquire_lock (stdout); - if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) + if ((stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) == (_IO_LINKED | _IO_LINE_BUF)) - _IO_OVERFLOW (_IO_stdout, EOF); + _IO_OVERFLOW (stdout, EOF); - _IO_release_lock (_IO_stdout); + _IO_release_lock (stdout); } _IO_switch_to_get_mode (fp); diff --git a/libio/ioputs.c b/libio/ioputs.c index 04ae323c6d..319e551de5 100644 --- a/libio/ioputs.c +++ b/libio/ioputs.c @@ -33,15 +33,15 @@ _IO_puts (const char *str) { int result = EOF; size_t len = strlen (str); - _IO_acquire_lock (_IO_stdout); + _IO_acquire_lock (stdout); - if ((_IO_vtable_offset (_IO_stdout) != 0 - || _IO_fwide (_IO_stdout, -1) == -1) - && _IO_sputn (_IO_stdout, str, len) == len - && _IO_putc_unlocked ('\n', _IO_stdout) != EOF) + if ((_IO_vtable_offset (stdout) != 0 + || _IO_fwide (stdout, -1) == -1) + && _IO_sputn (stdout, str, len) == len + && _IO_putc_unlocked ('\n', stdout) != EOF) result = MIN (INT_MAX, len + 1); - _IO_release_lock (_IO_stdout); + _IO_release_lock (stdout); return result; } diff --git a/libio/putchar.c b/libio/putchar.c index 665f46685a..a3f4200cdb 100644 --- a/libio/putchar.c +++ b/libio/putchar.c @@ -24,9 +24,9 @@ int putchar (int c) { int result; - _IO_acquire_lock (_IO_stdout); - result = _IO_putc_unlocked (c, _IO_stdout); - _IO_release_lock (_IO_stdout); + _IO_acquire_lock (stdout); + result = _IO_putc_unlocked (c, stdout); + _IO_release_lock (stdout); return result; } diff --git a/libio/putchar_u.c b/libio/putchar_u.c index 37d03ad364..1eebf0fc8f 100644 --- a/libio/putchar_u.c +++ b/libio/putchar_u.c @@ -23,6 +23,6 @@ int putchar_unlocked (int c) { - CHECK_FILE (_IO_stdout, EOF); - return _IO_putc_unlocked (c, _IO_stdout); + CHECK_FILE (stdout, EOF); + return _IO_putc_unlocked (c, stdout); } diff --git a/libio/putwchar.c b/libio/putwchar.c index 8d6b6a4df0..1f5c4176f8 100644 --- a/libio/putwchar.c +++ b/libio/putwchar.c @@ -22,8 +22,8 @@ wint_t putwchar (wchar_t wc) { wint_t result; - _IO_acquire_lock (_IO_stdout); - result = _IO_putwc_unlocked (wc, _IO_stdout); - _IO_release_lock (_IO_stdout); + _IO_acquire_lock (stdout); + result = _IO_putwc_unlocked (wc, stdout); + _IO_release_lock (stdout); return result; } diff --git a/libio/putwchar_u.c b/libio/putwchar_u.c index cfb46fc253..d943220031 100644 --- a/libio/putwchar_u.c +++ b/libio/putwchar_u.c @@ -21,6 +21,6 @@ wint_t putwchar_unlocked (wchar_t wc) { - CHECK_FILE (_IO_stdout, WEOF); - return _IO_putwc_unlocked (wc, _IO_stdout); + CHECK_FILE (stdout, WEOF); + return _IO_putwc_unlocked (wc, stdout); } diff --git a/libio/tst-bz24051.c b/libio/tst-bz24051.c new file mode 100644 index 0000000000..52c7a64b5b --- /dev/null +++ b/libio/tst-bz24051.c @@ -0,0 +1,79 @@ +/* Test that assigning to stdout redirects puts, putchar, etc (BZ#24051) + Copyright (C) 2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + + +/* Prevent putchar -> _IO_putc inline expansion. */ +#define __NO_INLINE__ + +#include <stdio.h> +#include <string.h> +#include <wchar.h> + +#include <support/check.h> +#include <support/temp_file.h> +#include <support/test-driver.h> + +#undef putchar +#undef putwchar + +static int +do_test_narrow (void) +{ + char buf[100]; + int fd = create_temp_file ("tst-bz24051", NULL); + stdout = fdopen (fd, "w+"); + TEST_VERIFY_EXIT (stdout != NULL); + + printf ("ab%s", "cd"); + putchar ('e'); + putchar_unlocked ('f'); + puts ("ghi"); + + rewind (stdout); + TEST_VERIFY_EXIT (fgets (buf, sizeof (buf), stdout) != NULL); + TEST_VERIFY (strcmp (buf, "abcdefghi\n") == 0); + + return 0; +} + +static int +do_test_wide (void) +{ + wchar_t buf[100]; + int fd = create_temp_file ("tst-bz24051w", NULL); + stdout = fdopen (fd, "w+"); + TEST_VERIFY_EXIT (stdout != NULL); + + wprintf (L"ab%ls", L"cd"); + putwchar (L'e'); + putwchar_unlocked (L'f'); + + rewind (stdout); + TEST_VERIFY_EXIT (fgetws (buf, sizeof (buf), stdout) != NULL); + TEST_VERIFY (wcscmp (buf, L"abcdef") == 0); + + return 0; +} + +static int +do_test (void) +{ + return do_test_narrow () + do_test_wide (); +} + +#include <support/test-driver.c> diff --git a/libio/wfileops.c b/libio/wfileops.c index 78f20486e5..0367643703 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -208,13 +208,13 @@ _IO_wfile_underflow (FILE *fp) traditional Unix systems did this for stdout. stderr better not be line buffered. So we do just that here explicitly. --drepper */ - _IO_acquire_lock (_IO_stdout); + _IO_acquire_lock (stdout); - if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) + if ((stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) == (_IO_LINKED | _IO_LINE_BUF)) - _IO_OVERFLOW (_IO_stdout, EOF); + _IO_OVERFLOW (stdout, EOF); - _IO_release_lock (_IO_stdout); + _IO_release_lock (stdout); } _IO_switch_to_get_mode (fp);