libio: Use stdin consistently for input functions [BZ #24153]

The internal _IO_stdin_ variable is not updated when the application
assigns to stdin, which is a GNU extension.
This commit is contained in:
Florian Weimer 2019-02-03 09:37:30 +01:00
parent c70824b9a4
commit ee9941f94e
11 changed files with 157 additions and 29 deletions

View File

@ -1,3 +1,17 @@
2019-02-03 Florian Weimer <fweimer@redhat.com>
[BZ #24153]
* debug/gets_chk.c (__gets_chk): Use stdin instead of _IO_stdin.
* libio/getchar.c (getchar): Likewise.
* libio/getchar_u.c (getchar_unlocked): Likewise.
* libio/getwchar.c (getwchar): Likewise.
* libio/getwchar_u.c (getwchar_unlocked): Likewise.
* libio/iogets.c (_IO_gets): Likewise.
* libio/vscanf.c (_IO_vscanf): Likewise.
* libio/vwscanf.c (__vwscanf): Likewise.
* libio/tst-bz24153.c: New file.
* libio/Makefile (tests): Add it.
2019-02-02 Florian Weimer <fweimer@redhat.com>
[BZ #14829]

View File

@ -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;
}

View File

@ -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-bz24051
tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tst-bz24153
tests-internal = tst-vtables tst-vtables-interposed tst-readline

View File

@ -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;
}

View File

@ -32,5 +32,5 @@
int
getchar_unlocked (void)
{
return _IO_getc_unlocked (_IO_stdin);
return _IO_getc_unlocked (stdin);
}

View File

@ -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;
}

View File

@ -32,5 +32,5 @@
wint_t
getwchar_unlocked (void)
{
return _IO_getwc_unlocked (_IO_stdin);
return _IO_getwc_unlocked (stdin);
}

View File

@ -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;
}

114
libio/tst-bz24153.c Normal file
View File

@ -0,0 +1,114 @@
/* Test that assigning to stdin redirects input (bug 24153).
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 getchar -> getc inline expansion. */
#define __NO_INLINE__
#pragma GCC optimize ("O0")
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/temp_file.h>
#include <support/xstdio.h>
#include <support/xunistd.h>
#include <wchar.h>
static int
call_vscanf (const char *format, ...)
{
va_list ap;
va_start (ap, format);
int ret = vscanf (format, ap);
va_end (ap);
return ret;
}
static int
call_vwscanf (const wchar_t *format, ...)
{
va_list ap;
va_start (ap, format);
int ret = vwscanf (format, ap);
va_end (ap);
return ret;
}
static void
narrow (const char *path)
{
FILE *old_stdin = stdin;
stdin = xfopen (path, "r");
TEST_COMPARE (getchar (), 'a');
TEST_COMPARE (getchar_unlocked (), 'b');
char ch = 1;
TEST_COMPARE (scanf ("%c", &ch), 1);
TEST_COMPARE (ch, 'c');
TEST_COMPARE (call_vscanf ("%c", &ch), 1);
TEST_COMPARE (ch, 'd');
char buf[8];
memset (buf, 'X', sizeof (buf));
/* Legacy interface. */
extern char *gets (char *);
TEST_VERIFY (gets (buf) == buf);
TEST_COMPARE_BLOB (buf, sizeof (buf), "ef\0XXXXX", sizeof (buf));
fclose (stdin);
stdin = old_stdin;
}
static void
wide (const char *path)
{
FILE *old_stdin = stdin;
stdin = xfopen (path, "r");
TEST_COMPARE (getwchar (), L'a');
TEST_COMPARE (getwchar_unlocked (), L'b');
wchar_t ch = 1;
TEST_COMPARE (wscanf (L"%c", &ch), 1);
TEST_COMPARE (ch, L'c');
TEST_COMPARE (call_vwscanf (L"%c", &ch), 1);
TEST_COMPARE (ch, L'd');
fclose (stdin);
stdin = old_stdin;
}
static int
do_test (void)
{
char *path;
{
int fd = create_temp_file ("tst-bz24153-", &path);
TEST_VERIFY_EXIT (fd >= 0);
xwrite (fd, "abcdef", strlen ("abcdef"));
xclose (fd);
}
narrow (path);
wide (path);
free (path);
return 0;
}
#include <support/test-driver.c>

View File

@ -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)

View File

@ -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)