diff mbox

[v2,1/2] libio: Multiple fixes for open_{w}memstram (BZ#18241 and BZ#20181)

Message ID 1470688986-8798-1-git-send-email-adhemerval.zanella@linaro.org
State Accepted
Commit 645f97ced4d4b35deda3f8bde0927f898b163f5d
Headers show

Commit Message

Adhemerval Zanella Aug. 8, 2016, 8:43 p.m. UTC
Changes from previous version:

 * Set EINVAL on _IO_{w}str_seekoff and remove INVALPOS
 * Rewrite how to print error messages in tests to avoid put __FILE__ in
   format string.
 * Indentation and style fixes.

--

This patches fixes multiples issues on open_{w}memstream reported on both
BZ#18241 and BZ#20181:

  - failed fseek does not set errno.
  - negative offset in fseek fails even when resulting position is
    a valid one.
  - a flush after write if the current write position is not at the
    end of the stream currupt data.

The main fix is on seek operation for memstream (_IO_{w}str_seekoff), where
both _IO_read_ptr and _IO_read_end pointer are updated if a write operation
has occured (similar to default file operations).  Also, to calculate the
offset on both read and write pointers, a temporary value is instead of
updating the argument supplied value.  Negative offset are valid if resulting
internal pointer is within the range of _IO_{read,write}_base and
_IO_{read,write}_end.

Also POSIX states that a null or wide null shall be appended to the current
buffer iff a write moves the position to a value larger than the current
lenght.  Current implementation appends a null or wide null regardless
of this condition.  This patch fixes it by removing the 'else' condition
on _IO_{w}mem_sync.

Checked on x86_64.

	[BZ #18241]
	[BZ #20181]
	* libio/Makefile (test): Add tst-memstream3 and tst-wmemstream3.
	* libio/memstream.c (_IO_mem_sync): Only append a null byte if
	write position is at the end the buffer.
	* libio/wmemstream.c (_IO_wmem_sync): Likewise.
	* libio/strops.c (_IO_str_switch_to_get_mode): New function.
	(_IO_str_seekoff): Set correct offset from negative displacement and
	set EINVAL for invalid ones.
	* libio/wstrops.c (enlarge_userbuf): Use correct function to calculate
	buffer length.
	(_IO_wstr_switch_to_get_mode): New function.
	(_IO_wstr_seekoff): Set correct offset from negative displacement and
	set EINVAL for invalid ones.
	* libio/tst-memstream3.c: New file.
	* libio/tst-wmemstream3.c: Likewise.
	* manual/examples/memstrm.c: Remove warning when priting size_t.
---
 ChangeLog                 |  20 ++++++
 libio/Makefile            |   4 +-
 libio/memstream.c         |   2 -
 libio/strops.c            |  81 +++++++++++++++--------
 libio/tst-memstream3.c    | 165 ++++++++++++++++++++++++++++++++++++++++++++++
 libio/tst-wmemstream3.c   |  44 +++++++++++++
 libio/wmemstream.c        |   2 -
 libio/wstrops.c           |  89 +++++++++++++++++--------
 manual/examples/memstrm.c |   4 +-
 9 files changed, 348 insertions(+), 63 deletions(-)
 create mode 100644 libio/tst-memstream3.c
 create mode 100644 libio/tst-wmemstream3.c

-- 
2.7.4

Comments

Adhemerval Zanella Aug. 25, 2016, 7:53 p.m. UTC | #1
Ping.

On 08/08/2016 17:43, Adhemerval Zanella wrote:
> Changes from previous version:

> 

>  * Set EINVAL on _IO_{w}str_seekoff and remove INVALPOS

>  * Rewrite how to print error messages in tests to avoid put __FILE__ in

>    format string.

>  * Indentation and style fixes.

> 

> --

> 

> This patches fixes multiples issues on open_{w}memstream reported on both

> BZ#18241 and BZ#20181:

> 

>   - failed fseek does not set errno.

>   - negative offset in fseek fails even when resulting position is

>     a valid one.

>   - a flush after write if the current write position is not at the

>     end of the stream currupt data.

> 

> The main fix is on seek operation for memstream (_IO_{w}str_seekoff), where

> both _IO_read_ptr and _IO_read_end pointer are updated if a write operation

> has occured (similar to default file operations).  Also, to calculate the

> offset on both read and write pointers, a temporary value is instead of

> updating the argument supplied value.  Negative offset are valid if resulting

> internal pointer is within the range of _IO_{read,write}_base and

> _IO_{read,write}_end.

> 

> Also POSIX states that a null or wide null shall be appended to the current

> buffer iff a write moves the position to a value larger than the current

> lenght.  Current implementation appends a null or wide null regardless

> of this condition.  This patch fixes it by removing the 'else' condition

> on _IO_{w}mem_sync.

> 

> Checked on x86_64.

> 

> 	[BZ #18241]

> 	[BZ #20181]

> 	* libio/Makefile (test): Add tst-memstream3 and tst-wmemstream3.

> 	* libio/memstream.c (_IO_mem_sync): Only append a null byte if

> 	write position is at the end the buffer.

> 	* libio/wmemstream.c (_IO_wmem_sync): Likewise.

> 	* libio/strops.c (_IO_str_switch_to_get_mode): New function.

> 	(_IO_str_seekoff): Set correct offset from negative displacement and

> 	set EINVAL for invalid ones.

> 	* libio/wstrops.c (enlarge_userbuf): Use correct function to calculate

> 	buffer length.

> 	(_IO_wstr_switch_to_get_mode): New function.

> 	(_IO_wstr_seekoff): Set correct offset from negative displacement and

> 	set EINVAL for invalid ones.

> 	* libio/tst-memstream3.c: New file.

> 	* libio/tst-wmemstream3.c: Likewise.

> 	* manual/examples/memstrm.c: Remove warning when priting size_t.

> ---

>  ChangeLog                 |  20 ++++++

>  libio/Makefile            |   4 +-

>  libio/memstream.c         |   2 -

>  libio/strops.c            |  81 +++++++++++++++--------

>  libio/tst-memstream3.c    | 165 ++++++++++++++++++++++++++++++++++++++++++++++

>  libio/tst-wmemstream3.c   |  44 +++++++++++++

>  libio/wmemstream.c        |   2 -

>  libio/wstrops.c           |  89 +++++++++++++++++--------

>  manual/examples/memstrm.c |   4 +-

>  9 files changed, 348 insertions(+), 63 deletions(-)

>  create mode 100644 libio/tst-memstream3.c

>  create mode 100644 libio/tst-wmemstream3.c

> 

> diff --git a/ChangeLog b/ChangeLog

> index 205da06..28d9012 100644

> --- a/ChangeLog

> +++ b/ChangeLog

> @@ -1,3 +1,23 @@

> +2016-08-05  Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> +

> +	[BZ #18241]

> +	[BZ #20181]

> +	* libio/Makefile (test): Add tst-memstream3 and tst-wmemstream3.

> +	* libio/memstream.c (_IO_mem_sync): Only append a null byte if

> +	write position is at the end the buffer.

> +	* libio/wmemstream.c (_IO_wmem_sync): Likewise.

> +	* libio/strops.c (_IO_str_switch_to_get_mode): New function.

> +	(_IO_str_seekoff): Set correct offset from negative displacement and

> +	set EINVAL for invalid ones.

> +	* libio/wstrops.c (enlarge_userbuf): Use correct function to calculate

> +	buffer length.

> +	(_IO_wstr_switch_to_get_mode): New function.

> +	(_IO_wstr_seekoff): Set correct offset from negative displacement and

> +	set EINVAL for invalid ones.

> +	* libio/tst-memstream3.c: New file.

> +	* libio/tst-wmemstream3.c: Likewise.

> +	* manual/examples/memstrm.c: Remove warning when priting size_t.

> +

>  2016-08-05  Torvald Riegel  <triegel@redhat.com>

>  

>  	* include/atomic.h (atomic_exchange_relaxed): New.

> diff --git a/libio/Makefile b/libio/Makefile

> index 12589f2..0c7751c 100644

> --- a/libio/Makefile

> +++ b/libio/Makefile

> @@ -56,8 +56,8 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \

>  	tst-mmap-eofsync tst-mmap-fflushsync bug-mmap-fflush \

>  	tst-mmap2-eofsync tst-mmap-offend bug-fopena+ bug-wfflush \

>  	bug-ungetc2 bug-ftell bug-ungetc3 bug-ungetc4 tst-fopenloc2 \

> -	tst-memstream1 tst-memstream2 \

> -	tst-wmemstream1 tst-wmemstream2 \

> +	tst-memstream1 tst-memstream2 tst-memstream3 \

> +	tst-wmemstream1 tst-wmemstream2 tst-wmemstream3 \

>  	bug-memstream1 bug-wmemstream1 \

>  	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \

>  	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \

> diff --git a/libio/memstream.c b/libio/memstream.c

> index e20b9c2..f1e8d58 100644

> --- a/libio/memstream.c

> +++ b/libio/memstream.c

> @@ -112,8 +112,6 @@ _IO_mem_sync (_IO_FILE *fp)

>        _IO_str_overflow (fp, '\0');

>        --fp->_IO_write_ptr;

>      }

> -  else

> -    *fp->_IO_write_ptr = '\0';

>  

>    *mp->bufloc = fp->_IO_write_base;

>    *mp->sizeloc = fp->_IO_write_ptr - fp->_IO_write_base;

> diff --git a/libio/strops.c b/libio/strops.c

> index 2ba3704..1bb8a77 100644

> --- a/libio/strops.c

> +++ b/libio/strops.c

> @@ -230,6 +230,21 @@ enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)

>    return 0;

>  }

>  

> +static void

> +_IO_str_switch_to_get_mode (_IO_FILE *fp)

> +{

> +  if (_IO_in_backup (fp))

> +    fp->_IO_read_base = fp->_IO_backup_base;

> +  else

> +    {

> +      fp->_IO_read_base = fp->_IO_buf_base;

> +      if (fp->_IO_write_ptr > fp->_IO_read_end)

> +        fp->_IO_read_end = fp->_IO_write_ptr;

> +    }

> +  fp->_IO_read_ptr = fp->_IO_read_end = fp->_IO_write_ptr;

> +

> +  fp->_flags &= ~_IO_CURRENTLY_PUTTING;

> +}

>  

>  _IO_off64_t

>  _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

> @@ -239,14 +254,14 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>    if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))

>      mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);

>  

> +  bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base

> +		     || _IO_in_put_mode (fp));

> +  if (was_writing)

> +    _IO_str_switch_to_get_mode (fp);

> +

>    if (mode == 0)

>      {

> -      /* Don't move any pointers. But there is no clear indication what

> -	 mode FP is in. Let's guess. */

> -      if (fp->_IO_file_flags & _IO_NO_WRITES)

> -        new_pos = fp->_IO_read_ptr - fp->_IO_read_base;

> -      else

> -        new_pos = fp->_IO_write_ptr - fp->_IO_write_base;

> +      new_pos = fp->_IO_read_ptr - fp->_IO_read_base;

>      }

>    else

>      {

> @@ -256,48 +271,62 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>        /* Move the get pointer, if requested. */

>        if (mode & _IOS_INPUT)

>  	{

> +	  _IO_ssize_t base;

>  	  switch (dir)

>  	    {

> -	    case _IO_seek_end:

> -	      offset += cur_size;

> +	    case _IO_seek_set:

> +	      base = 0;

>  	      break;

>  	    case _IO_seek_cur:

> -	      offset += fp->_IO_read_ptr - fp->_IO_read_base;

> +	      base = fp->_IO_read_ptr - fp->_IO_read_base;

>  	      break;

> -	    default: /* case _IO_seek_set: */

> +	    default: /* case _IO_seek_end: */

> +	      base = cur_size;

>  	      break;

>  	    }

> -	  if (offset < 0)

> -	    return EOF;

> -	  if ((_IO_ssize_t) offset > cur_size

> -	      && enlarge_userbuf (fp, offset, 1) != 0)

> +	  _IO_ssize_t maxval = SSIZE_MAX - base;

> +	  if (offset < -base || offset > maxval)

> +	    {

> +	      __set_errno (EINVAL);

> +	      return EOF;

> +	    }

> +	  base += offset;

> +	  if (base > cur_size

> +	      && enlarge_userbuf (fp, base, 1) != 0)

>  	    return EOF;

> -	  fp->_IO_read_ptr = fp->_IO_read_base + offset;

> +	  fp->_IO_read_ptr = fp->_IO_read_base + base;

>  	  fp->_IO_read_end = fp->_IO_read_base + cur_size;

> -	  new_pos = offset;

> +	  new_pos = base;

>  	}

>  

>        /* Move the put pointer, if requested. */

>        if (mode & _IOS_OUTPUT)

>  	{

> +	  _IO_ssize_t base;

>  	  switch (dir)

>  	    {

> -	    case _IO_seek_end:

> -	      offset += cur_size;

> +	    case _IO_seek_set:

> +	      base = 0;

>  	      break;

>  	    case _IO_seek_cur:

> -	      offset += fp->_IO_write_ptr - fp->_IO_write_base;

> +	      base = fp->_IO_write_ptr - fp->_IO_write_base;

>  	      break;

> -	    default: /* case _IO_seek_set: */

> +	    default: /* case _IO_seek_end: */

> +	      base = cur_size;

>  	      break;

>  	    }

> -	  if (offset < 0)

> -	    return EOF;

> -	  if ((_IO_ssize_t) offset > cur_size

> -	      && enlarge_userbuf (fp, offset, 0) != 0)

> +	  _IO_ssize_t maxval = SSIZE_MAX - base;

> +	  if (offset < -base || offset > maxval)

> +	    {

> +	      __set_errno (EINVAL);

> +	      return EOF;

> +	    }

> +	  base += offset;

> +	  if (base > cur_size

> +	      && enlarge_userbuf (fp, base, 0) != 0)

>  	    return EOF;

> -	  fp->_IO_write_ptr = fp->_IO_write_base + offset;

> -	  new_pos = offset;

> +	  fp->_IO_write_ptr = fp->_IO_write_base + base;

> +	  new_pos = base;

>  	}

>      }

>    return new_pos;

> diff --git a/libio/tst-memstream3.c b/libio/tst-memstream3.c

> new file mode 100644

> index 0000000..34b04e5

> --- /dev/null

> +++ b/libio/tst-memstream3.c

> @@ -0,0 +1,165 @@

> +/* Test for open_memstream implementation.

> +   Copyright (C) 2016 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/>.  */

> +

> +#include <mcheck.h>

> +#include <stdio.h>

> +#include <stdarg.h>

> +#include <errno.h>

> +

> +

> +#ifndef CHAR_T

> +# define CHAR_T char

> +# define W(o) o

> +# define OPEN_MEMSTREAM open_memstream

> +# define PRINTF printf

> +# define FWRITE fwrite

> +# define FPUTC fputc

> +# define STRCMP strcmp

> +#endif

> +

> +#define S(s) S1 (s)

> +#define S1(s) #s

> +

> +static void

> +mcheck_abort (enum mcheck_status ev)

> +{

> +  printf ("mecheck failed with status %d\n", (int) ev);

> +  exit (1);

> +}

> +

> +static void

> +error_printf (int line, const char *fmt, ...)

> +{

> +  va_list ap;

> +

> +  printf ("error: %s:%i: ", __FILE__, line);

> +  va_start (ap, fmt);

> +  vprintf (fmt, ap);

> +  va_end (ap);

> +}

> +

> +#define ERROR_RET1(...) \

> +  { error_printf(__LINE__, __VA_ARGS__); return 1; }

> +

> +static int

> +do_test_bz18241 (void)

> +{

> +  CHAR_T *buf;

> +  size_t size;

> +

> +  FILE *fp = OPEN_MEMSTREAM (&buf, &size);

> +  if (fp == NULL)

> +    ERROR_RET1 ("%s failed\n", S(OPEN_MEMSTREAM));

> +

> +  if (FPUTC (W('a'), fp) != W('a'))

> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FPUTC), errno);

> +  if (fflush (fp) != 0)

> +    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);

> +  if (fseek (fp, -2, SEEK_SET) != -1)

> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

> +  if (errno != EINVAL)

> +    ERROR_RET1 ("errno != EINVAL\n");

> +  if (ftell (fp) != 1)

> +    ERROR_RET1 ("ftell failed (errno = %d)\n", errno);

> +  if (ferror (fp) != 0)

> +    ERROR_RET1 ("ferror != 0\n");

> +

> +  if (fseek (fp, -1, SEEK_CUR) == -1)

> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

> +  if (ftell (fp) != 0)

> +    ERROR_RET1 ("ftell failed (errno = %d)\n", errno);

> +  if (ferror (fp) != 0)

> +    ERROR_RET1 ("ferror != 0\n");

> +  if (FPUTC (W('b'), fp) != W('b'))

> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FPUTC), errno);

> +  if (fflush (fp) != 0)

> +    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);

> +

> +  if (fclose (fp) != 0)

> +    ERROR_RET1 ("fclose failed (errno = %d\n", errno);

> +

> +  if (STRCMP (buf, W("b")) != 0)

> +    ERROR_RET1 ("%s failed\n", S(STRCMP));

> +

> +  free (buf);

> +

> +  return 0;

> +}

> +

> +static int

> +do_test_bz20181 (void)

> +{

> +  CHAR_T *buf;

> +  size_t size;

> +  size_t ret;

> +

> +  FILE *fp = OPEN_MEMSTREAM (&buf, &size);

> +  if (fp == NULL)

> +    ERROR_RET1 ("%s failed\n", S(OPEN_MEMSTREAM));

> +

> +  if ((ret = FWRITE (W("abc"), 1, 3, fp)) != 3)

> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);

> +

> +  if (fseek (fp, 0, SEEK_SET) != 0)

> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

> +

> +  if (FWRITE (W("z"), 1, 1, fp) != 1)

> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);

> +

> +  if (fflush (fp) != 0)

> +    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);

> +

> +  /* Avoid truncating the buffer on close.  */

> +  if (fseek (fp, 3, SEEK_SET) != 0)

> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

> +

> +  if (fclose (fp) != 0)

> +    ERROR_RET1 ("fclose failed (errno = %d\n", errno);

> +

> +  if (size != 3)

> +    ERROR_RET1 ("size != 3\n");

> +

> +  if (buf[0] != W('z')

> +      || buf[1] != W('b')

> +      || buf[2] != W('c'))

> +    {

> +      PRINTF (W("error: buf {%c,%c,%c} != {z,b,c}\n"),

> +	      buf[0], buf[1], buf[2]);

> +      return 1;

> +    }

> +    

> +  free (buf);

> +

> +  return 0;

> +}

> +

> +static int

> +do_test (void)

> +{

> +  int ret = 0;

> +

> +  mcheck_pedantic (mcheck_abort);

> +

> +  ret += do_test_bz18241 ();

> +  ret += do_test_bz20181 ();

> +

> +  return ret;

> +}

> +

> +#define TEST_FUNCTION do_test ()

> +#include "../test-skeleton.c"

> diff --git a/libio/tst-wmemstream3.c b/libio/tst-wmemstream3.c

> new file mode 100644

> index 0000000..190283a

> --- /dev/null

> +++ b/libio/tst-wmemstream3.c

> @@ -0,0 +1,44 @@

> +/* Test for open_memstream implementation.

> +   Copyright (C) 2016 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/>.  */

> +

> +#include <wchar.h>

> +

> +/* Straighforward implementation so tst-memstream3 could use check

> +   fwrite on open_memstream.  */

> +static size_t

> +fwwrite (const void *ptr, size_t size, size_t nmemb, FILE *arq)

> +{

> +  const wchar_t *wcs = (const wchar_t*) (ptr);

> +  for (size_t s = 0; s < size; s++)

> +    {

> +      for (size_t n = 0; n < nmemb; n++)

> +        if (fputwc (wcs[n], arq) == WEOF)

> +          return n; 

> +    }

> +  return size * nmemb; 

> +}

> +

> +#define CHAR_T wchar_t

> +#define W(o) L##o

> +#define OPEN_MEMSTREAM open_wmemstream

> +#define PRINTF wprintf

> +#define FWRITE fwwrite

> +#define FPUTC  fputwc

> +#define STRCMP wcscmp

> +

> +#include "tst-memstream3.c"

> diff --git a/libio/wmemstream.c b/libio/wmemstream.c

> index bf2a50b..fd01be0 100644

> --- a/libio/wmemstream.c

> +++ b/libio/wmemstream.c

> @@ -112,8 +112,6 @@ _IO_wmem_sync (_IO_FILE *fp)

>        _IO_wstr_overflow (fp, '\0');

>        --fp->_wide_data->_IO_write_ptr;

>      }

> -  else

> -    *fp->_wide_data->_IO_write_ptr = '\0';

>  

>    *mp->bufloc = fp->_wide_data->_IO_write_base;

>    *mp->sizeloc = (fp->_wide_data->_IO_write_ptr

> diff --git a/libio/wstrops.c b/libio/wstrops.c

> index 09fa543..0b2bec3 100644

> --- a/libio/wstrops.c

> +++ b/libio/wstrops.c

> @@ -169,7 +169,7 @@ _IO_wstr_count (_IO_FILE *fp)

>  static int

>  enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)

>  {

> -  if ((_IO_ssize_t) offset <= _IO_blen (fp))

> +  if ((_IO_ssize_t) offset <= _IO_wblen (fp))

>      return 0;

>  

>    struct _IO_wide_data *wd = fp->_wide_data;

> @@ -235,6 +235,22 @@ enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)

>    return 0;

>  }

>  

> +static void

> +_IO_wstr_switch_to_get_mode (_IO_FILE *fp)

> +{

> +  if (_IO_in_backup (fp))

> +    fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_backup_base;

> +  else

> +    {

> +      fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_buf_base;

> +      if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)

> +        fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;

> +    }

> +  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;

> +  fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;

> +

> +  fp->_flags &= ~_IO_CURRENTLY_PUTTING;

> +}

>  

>  _IO_off64_t

>  _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

> @@ -244,15 +260,16 @@ _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>    if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))

>      mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);

>  

> +  bool was_writing = (fp->_wide_data->_IO_write_ptr >

> +			fp->_wide_data->_IO_write_base

> +		     || _IO_in_put_mode (fp));

> +  if (was_writing)

> +    _IO_wstr_switch_to_get_mode (fp);

> +

>    if (mode == 0)

>      {

> -      /* Don't move any pointers. But there is no clear indication what

> -	 mode FP is in. Let's guess. */

> -      if (fp->_IO_file_flags & _IO_NO_WRITES)

> -        new_pos = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_base;

> -      else

> -        new_pos = (fp->_wide_data->_IO_write_ptr

> -		   - fp->_wide_data->_IO_write_base);

> +      new_pos = (fp->_wide_data->_IO_write_ptr

> +		 - fp->_wide_data->_IO_write_base);

>      }

>    else

>      {

> @@ -262,25 +279,32 @@ _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>        /* Move the get pointer, if requested. */

>        if (mode & _IOS_INPUT)

>  	{

> +	  _IO_ssize_t base;

>  	  switch (dir)

>  	    {

> -	    case _IO_seek_end:

> -	      offset += cur_size;

> +	    case _IO_seek_set:

> +	      base = 0;

>  	      break;

>  	    case _IO_seek_cur:

> -	      offset += (fp->_wide_data->_IO_read_ptr

> -			 - fp->_wide_data->_IO_read_base);

> +	      base = (fp->_wide_data->_IO_read_ptr

> +		     - fp->_wide_data->_IO_read_base);

>  	      break;

> -	    default: /* case _IO_seek_set: */

> +	    default: /* case _IO_seek_end: */

> +	      base = cur_size;

>  	      break;

>  	    }

> -	  if (offset < 0)

> -	    return EOF;

> -	  if ((_IO_ssize_t) offset > cur_size

> -	      && enlarge_userbuf (fp, offset, 1) != 0)

> +	  _IO_ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;

> +	  if (offset < -base || offset > maxval)

> +	    {

> +	      __set_errno (EINVAL);

> +	      return EOF;

> +	    }

> +	  base += offset;

> +	  if (base > cur_size

> +	      && enlarge_userbuf (fp, base, 1) != 0)

>  	    return EOF;

>  	  fp->_wide_data->_IO_read_ptr = (fp->_wide_data->_IO_read_base

> -					  + offset);

> +					  + base);

>  	  fp->_wide_data->_IO_read_end = (fp->_wide_data->_IO_read_base

>  					  + cur_size);

>  	  new_pos = offset;

> @@ -289,26 +313,33 @@ _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>        /* Move the put pointer, if requested. */

>        if (mode & _IOS_OUTPUT)

>  	{

> +	  _IO_ssize_t base;

>  	  switch (dir)

>  	    {

> -	    case _IO_seek_end:

> -	      offset += cur_size;

> +	    case _IO_seek_set:

> +	      base = 0;

>  	      break;

>  	    case _IO_seek_cur:

> -	      offset += (fp->_wide_data->_IO_write_ptr

> -			 - fp->_wide_data->_IO_write_base);

> +	      base = (fp->_wide_data->_IO_write_ptr

> +		     - fp->_wide_data->_IO_write_base);

>  	      break;

> -	    default: /* case _IO_seek_set: */

> +	    default: /* case _IO_seek_end: */

> +	      base = cur_size;

>  	      break;

>  	    }

> -	  if (offset < 0)

> -	    return EOF;

> -	  if ((_IO_ssize_t) offset > cur_size

> -	      && enlarge_userbuf (fp, offset, 0) != 0)

> +	  _IO_ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;

> +	  if (offset < -base || offset > maxval)

> +	    {

> +	      __set_errno (EINVAL);

> +	      return EOF;

> +	    }

> +	  base += offset;

> +	  if (base > cur_size

> +	      && enlarge_userbuf (fp, base, 0) != 0)

>  	    return EOF;

>  	  fp->_wide_data->_IO_write_ptr = (fp->_wide_data->_IO_write_base

> -					   + offset);

> -	  new_pos = offset;

> +					   + base);

> +	  new_pos = base;

>  	}

>      }

>    return new_pos;

> diff --git a/manual/examples/memstrm.c b/manual/examples/memstrm.c

> index 0d443b1..5701ba1 100644

> --- a/manual/examples/memstrm.c

> +++ b/manual/examples/memstrm.c

> @@ -27,10 +27,10 @@ main (void)

>    stream = open_memstream (&bp, &size);

>    fprintf (stream, "hello");

>    fflush (stream);

> -  printf ("buf = `%s', size = %d\n", bp, size);

> +  printf ("buf = `%s', size = %zu\n", bp, size);

>    fprintf (stream, ", world");

>    fclose (stream);

> -  printf ("buf = `%s', size = %d\n", bp, size);

> +  printf ("buf = `%s', size = %zu\n", bp, size);

>  

>    return 0;

>  }

>
Adhemerval Zanella Sept. 30, 2016, 12:06 a.m. UTC | #2
Ping.

On 25/08/2016 12:53, Adhemerval Zanella wrote:
> Ping.

> 

> On 08/08/2016 17:43, Adhemerval Zanella wrote:

>> Changes from previous version:

>>

>>  * Set EINVAL on _IO_{w}str_seekoff and remove INVALPOS

>>  * Rewrite how to print error messages in tests to avoid put __FILE__ in

>>    format string.

>>  * Indentation and style fixes.

>>

>> --

>>

>> This patches fixes multiples issues on open_{w}memstream reported on both

>> BZ#18241 and BZ#20181:

>>

>>   - failed fseek does not set errno.

>>   - negative offset in fseek fails even when resulting position is

>>     a valid one.

>>   - a flush after write if the current write position is not at the

>>     end of the stream currupt data.

>>

>> The main fix is on seek operation for memstream (_IO_{w}str_seekoff), where

>> both _IO_read_ptr and _IO_read_end pointer are updated if a write operation

>> has occured (similar to default file operations).  Also, to calculate the

>> offset on both read and write pointers, a temporary value is instead of

>> updating the argument supplied value.  Negative offset are valid if resulting

>> internal pointer is within the range of _IO_{read,write}_base and

>> _IO_{read,write}_end.

>>

>> Also POSIX states that a null or wide null shall be appended to the current

>> buffer iff a write moves the position to a value larger than the current

>> lenght.  Current implementation appends a null or wide null regardless

>> of this condition.  This patch fixes it by removing the 'else' condition

>> on _IO_{w}mem_sync.

>>

>> Checked on x86_64.

>>

>> 	[BZ #18241]

>> 	[BZ #20181]

>> 	* libio/Makefile (test): Add tst-memstream3 and tst-wmemstream3.

>> 	* libio/memstream.c (_IO_mem_sync): Only append a null byte if

>> 	write position is at the end the buffer.

>> 	* libio/wmemstream.c (_IO_wmem_sync): Likewise.

>> 	* libio/strops.c (_IO_str_switch_to_get_mode): New function.

>> 	(_IO_str_seekoff): Set correct offset from negative displacement and

>> 	set EINVAL for invalid ones.

>> 	* libio/wstrops.c (enlarge_userbuf): Use correct function to calculate

>> 	buffer length.

>> 	(_IO_wstr_switch_to_get_mode): New function.

>> 	(_IO_wstr_seekoff): Set correct offset from negative displacement and

>> 	set EINVAL for invalid ones.

>> 	* libio/tst-memstream3.c: New file.

>> 	* libio/tst-wmemstream3.c: Likewise.

>> 	* manual/examples/memstrm.c: Remove warning when priting size_t.

>> ---

>>  ChangeLog                 |  20 ++++++

>>  libio/Makefile            |   4 +-

>>  libio/memstream.c         |   2 -

>>  libio/strops.c            |  81 +++++++++++++++--------

>>  libio/tst-memstream3.c    | 165 ++++++++++++++++++++++++++++++++++++++++++++++

>>  libio/tst-wmemstream3.c   |  44 +++++++++++++

>>  libio/wmemstream.c        |   2 -

>>  libio/wstrops.c           |  89 +++++++++++++++++--------

>>  manual/examples/memstrm.c |   4 +-

>>  9 files changed, 348 insertions(+), 63 deletions(-)

>>  create mode 100644 libio/tst-memstream3.c

>>  create mode 100644 libio/tst-wmemstream3.c

>>

>> diff --git a/ChangeLog b/ChangeLog

>> index 205da06..28d9012 100644

>> --- a/ChangeLog

>> +++ b/ChangeLog

>> @@ -1,3 +1,23 @@

>> +2016-08-05  Adhemerval Zanella  <adhemerval.zanella@linaro.org>

>> +

>> +	[BZ #18241]

>> +	[BZ #20181]

>> +	* libio/Makefile (test): Add tst-memstream3 and tst-wmemstream3.

>> +	* libio/memstream.c (_IO_mem_sync): Only append a null byte if

>> +	write position is at the end the buffer.

>> +	* libio/wmemstream.c (_IO_wmem_sync): Likewise.

>> +	* libio/strops.c (_IO_str_switch_to_get_mode): New function.

>> +	(_IO_str_seekoff): Set correct offset from negative displacement and

>> +	set EINVAL for invalid ones.

>> +	* libio/wstrops.c (enlarge_userbuf): Use correct function to calculate

>> +	buffer length.

>> +	(_IO_wstr_switch_to_get_mode): New function.

>> +	(_IO_wstr_seekoff): Set correct offset from negative displacement and

>> +	set EINVAL for invalid ones.

>> +	* libio/tst-memstream3.c: New file.

>> +	* libio/tst-wmemstream3.c: Likewise.

>> +	* manual/examples/memstrm.c: Remove warning when priting size_t.

>> +

>>  2016-08-05  Torvald Riegel  <triegel@redhat.com>

>>  

>>  	* include/atomic.h (atomic_exchange_relaxed): New.

>> diff --git a/libio/Makefile b/libio/Makefile

>> index 12589f2..0c7751c 100644

>> --- a/libio/Makefile

>> +++ b/libio/Makefile

>> @@ -56,8 +56,8 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \

>>  	tst-mmap-eofsync tst-mmap-fflushsync bug-mmap-fflush \

>>  	tst-mmap2-eofsync tst-mmap-offend bug-fopena+ bug-wfflush \

>>  	bug-ungetc2 bug-ftell bug-ungetc3 bug-ungetc4 tst-fopenloc2 \

>> -	tst-memstream1 tst-memstream2 \

>> -	tst-wmemstream1 tst-wmemstream2 \

>> +	tst-memstream1 tst-memstream2 tst-memstream3 \

>> +	tst-wmemstream1 tst-wmemstream2 tst-wmemstream3 \

>>  	bug-memstream1 bug-wmemstream1 \

>>  	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \

>>  	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \

>> diff --git a/libio/memstream.c b/libio/memstream.c

>> index e20b9c2..f1e8d58 100644

>> --- a/libio/memstream.c

>> +++ b/libio/memstream.c

>> @@ -112,8 +112,6 @@ _IO_mem_sync (_IO_FILE *fp)

>>        _IO_str_overflow (fp, '\0');

>>        --fp->_IO_write_ptr;

>>      }

>> -  else

>> -    *fp->_IO_write_ptr = '\0';

>>  

>>    *mp->bufloc = fp->_IO_write_base;

>>    *mp->sizeloc = fp->_IO_write_ptr - fp->_IO_write_base;

>> diff --git a/libio/strops.c b/libio/strops.c

>> index 2ba3704..1bb8a77 100644

>> --- a/libio/strops.c

>> +++ b/libio/strops.c

>> @@ -230,6 +230,21 @@ enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)

>>    return 0;

>>  }

>>  

>> +static void

>> +_IO_str_switch_to_get_mode (_IO_FILE *fp)

>> +{

>> +  if (_IO_in_backup (fp))

>> +    fp->_IO_read_base = fp->_IO_backup_base;

>> +  else

>> +    {

>> +      fp->_IO_read_base = fp->_IO_buf_base;

>> +      if (fp->_IO_write_ptr > fp->_IO_read_end)

>> +        fp->_IO_read_end = fp->_IO_write_ptr;

>> +    }

>> +  fp->_IO_read_ptr = fp->_IO_read_end = fp->_IO_write_ptr;

>> +

>> +  fp->_flags &= ~_IO_CURRENTLY_PUTTING;

>> +}

>>  

>>  _IO_off64_t

>>  _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>> @@ -239,14 +254,14 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>    if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))

>>      mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);

>>  

>> +  bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base

>> +		     || _IO_in_put_mode (fp));

>> +  if (was_writing)

>> +    _IO_str_switch_to_get_mode (fp);

>> +

>>    if (mode == 0)

>>      {

>> -      /* Don't move any pointers. But there is no clear indication what

>> -	 mode FP is in. Let's guess. */

>> -      if (fp->_IO_file_flags & _IO_NO_WRITES)

>> -        new_pos = fp->_IO_read_ptr - fp->_IO_read_base;

>> -      else

>> -        new_pos = fp->_IO_write_ptr - fp->_IO_write_base;

>> +      new_pos = fp->_IO_read_ptr - fp->_IO_read_base;

>>      }

>>    else

>>      {

>> @@ -256,48 +271,62 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>        /* Move the get pointer, if requested. */

>>        if (mode & _IOS_INPUT)

>>  	{

>> +	  _IO_ssize_t base;

>>  	  switch (dir)

>>  	    {

>> -	    case _IO_seek_end:

>> -	      offset += cur_size;

>> +	    case _IO_seek_set:

>> +	      base = 0;

>>  	      break;

>>  	    case _IO_seek_cur:

>> -	      offset += fp->_IO_read_ptr - fp->_IO_read_base;

>> +	      base = fp->_IO_read_ptr - fp->_IO_read_base;

>>  	      break;

>> -	    default: /* case _IO_seek_set: */

>> +	    default: /* case _IO_seek_end: */

>> +	      base = cur_size;

>>  	      break;

>>  	    }

>> -	  if (offset < 0)

>> -	    return EOF;

>> -	  if ((_IO_ssize_t) offset > cur_size

>> -	      && enlarge_userbuf (fp, offset, 1) != 0)

>> +	  _IO_ssize_t maxval = SSIZE_MAX - base;

>> +	  if (offset < -base || offset > maxval)

>> +	    {

>> +	      __set_errno (EINVAL);

>> +	      return EOF;

>> +	    }

>> +	  base += offset;

>> +	  if (base > cur_size

>> +	      && enlarge_userbuf (fp, base, 1) != 0)

>>  	    return EOF;

>> -	  fp->_IO_read_ptr = fp->_IO_read_base + offset;

>> +	  fp->_IO_read_ptr = fp->_IO_read_base + base;

>>  	  fp->_IO_read_end = fp->_IO_read_base + cur_size;

>> -	  new_pos = offset;

>> +	  new_pos = base;

>>  	}

>>  

>>        /* Move the put pointer, if requested. */

>>        if (mode & _IOS_OUTPUT)

>>  	{

>> +	  _IO_ssize_t base;

>>  	  switch (dir)

>>  	    {

>> -	    case _IO_seek_end:

>> -	      offset += cur_size;

>> +	    case _IO_seek_set:

>> +	      base = 0;

>>  	      break;

>>  	    case _IO_seek_cur:

>> -	      offset += fp->_IO_write_ptr - fp->_IO_write_base;

>> +	      base = fp->_IO_write_ptr - fp->_IO_write_base;

>>  	      break;

>> -	    default: /* case _IO_seek_set: */

>> +	    default: /* case _IO_seek_end: */

>> +	      base = cur_size;

>>  	      break;

>>  	    }

>> -	  if (offset < 0)

>> -	    return EOF;

>> -	  if ((_IO_ssize_t) offset > cur_size

>> -	      && enlarge_userbuf (fp, offset, 0) != 0)

>> +	  _IO_ssize_t maxval = SSIZE_MAX - base;

>> +	  if (offset < -base || offset > maxval)

>> +	    {

>> +	      __set_errno (EINVAL);

>> +	      return EOF;

>> +	    }

>> +	  base += offset;

>> +	  if (base > cur_size

>> +	      && enlarge_userbuf (fp, base, 0) != 0)

>>  	    return EOF;

>> -	  fp->_IO_write_ptr = fp->_IO_write_base + offset;

>> -	  new_pos = offset;

>> +	  fp->_IO_write_ptr = fp->_IO_write_base + base;

>> +	  new_pos = base;

>>  	}

>>      }

>>    return new_pos;

>> diff --git a/libio/tst-memstream3.c b/libio/tst-memstream3.c

>> new file mode 100644

>> index 0000000..34b04e5

>> --- /dev/null

>> +++ b/libio/tst-memstream3.c

>> @@ -0,0 +1,165 @@

>> +/* Test for open_memstream implementation.

>> +   Copyright (C) 2016 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/>.  */

>> +

>> +#include <mcheck.h>

>> +#include <stdio.h>

>> +#include <stdarg.h>

>> +#include <errno.h>

>> +

>> +

>> +#ifndef CHAR_T

>> +# define CHAR_T char

>> +# define W(o) o

>> +# define OPEN_MEMSTREAM open_memstream

>> +# define PRINTF printf

>> +# define FWRITE fwrite

>> +# define FPUTC fputc

>> +# define STRCMP strcmp

>> +#endif

>> +

>> +#define S(s) S1 (s)

>> +#define S1(s) #s

>> +

>> +static void

>> +mcheck_abort (enum mcheck_status ev)

>> +{

>> +  printf ("mecheck failed with status %d\n", (int) ev);

>> +  exit (1);

>> +}

>> +

>> +static void

>> +error_printf (int line, const char *fmt, ...)

>> +{

>> +  va_list ap;

>> +

>> +  printf ("error: %s:%i: ", __FILE__, line);

>> +  va_start (ap, fmt);

>> +  vprintf (fmt, ap);

>> +  va_end (ap);

>> +}

>> +

>> +#define ERROR_RET1(...) \

>> +  { error_printf(__LINE__, __VA_ARGS__); return 1; }

>> +

>> +static int

>> +do_test_bz18241 (void)

>> +{

>> +  CHAR_T *buf;

>> +  size_t size;

>> +

>> +  FILE *fp = OPEN_MEMSTREAM (&buf, &size);

>> +  if (fp == NULL)

>> +    ERROR_RET1 ("%s failed\n", S(OPEN_MEMSTREAM));

>> +

>> +  if (FPUTC (W('a'), fp) != W('a'))

>> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FPUTC), errno);

>> +  if (fflush (fp) != 0)

>> +    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);

>> +  if (fseek (fp, -2, SEEK_SET) != -1)

>> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

>> +  if (errno != EINVAL)

>> +    ERROR_RET1 ("errno != EINVAL\n");

>> +  if (ftell (fp) != 1)

>> +    ERROR_RET1 ("ftell failed (errno = %d)\n", errno);

>> +  if (ferror (fp) != 0)

>> +    ERROR_RET1 ("ferror != 0\n");

>> +

>> +  if (fseek (fp, -1, SEEK_CUR) == -1)

>> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

>> +  if (ftell (fp) != 0)

>> +    ERROR_RET1 ("ftell failed (errno = %d)\n", errno);

>> +  if (ferror (fp) != 0)

>> +    ERROR_RET1 ("ferror != 0\n");

>> +  if (FPUTC (W('b'), fp) != W('b'))

>> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FPUTC), errno);

>> +  if (fflush (fp) != 0)

>> +    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);

>> +

>> +  if (fclose (fp) != 0)

>> +    ERROR_RET1 ("fclose failed (errno = %d\n", errno);

>> +

>> +  if (STRCMP (buf, W("b")) != 0)

>> +    ERROR_RET1 ("%s failed\n", S(STRCMP));

>> +

>> +  free (buf);

>> +

>> +  return 0;

>> +}

>> +

>> +static int

>> +do_test_bz20181 (void)

>> +{

>> +  CHAR_T *buf;

>> +  size_t size;

>> +  size_t ret;

>> +

>> +  FILE *fp = OPEN_MEMSTREAM (&buf, &size);

>> +  if (fp == NULL)

>> +    ERROR_RET1 ("%s failed\n", S(OPEN_MEMSTREAM));

>> +

>> +  if ((ret = FWRITE (W("abc"), 1, 3, fp)) != 3)

>> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);

>> +

>> +  if (fseek (fp, 0, SEEK_SET) != 0)

>> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

>> +

>> +  if (FWRITE (W("z"), 1, 1, fp) != 1)

>> +    ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);

>> +

>> +  if (fflush (fp) != 0)

>> +    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);

>> +

>> +  /* Avoid truncating the buffer on close.  */

>> +  if (fseek (fp, 3, SEEK_SET) != 0)

>> +    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);

>> +

>> +  if (fclose (fp) != 0)

>> +    ERROR_RET1 ("fclose failed (errno = %d\n", errno);

>> +

>> +  if (size != 3)

>> +    ERROR_RET1 ("size != 3\n");

>> +

>> +  if (buf[0] != W('z')

>> +      || buf[1] != W('b')

>> +      || buf[2] != W('c'))

>> +    {

>> +      PRINTF (W("error: buf {%c,%c,%c} != {z,b,c}\n"),

>> +	      buf[0], buf[1], buf[2]);

>> +      return 1;

>> +    }

>> +    

>> +  free (buf);

>> +

>> +  return 0;

>> +}

>> +

>> +static int

>> +do_test (void)

>> +{

>> +  int ret = 0;

>> +

>> +  mcheck_pedantic (mcheck_abort);

>> +

>> +  ret += do_test_bz18241 ();

>> +  ret += do_test_bz20181 ();

>> +

>> +  return ret;

>> +}

>> +

>> +#define TEST_FUNCTION do_test ()

>> +#include "../test-skeleton.c"

>> diff --git a/libio/tst-wmemstream3.c b/libio/tst-wmemstream3.c

>> new file mode 100644

>> index 0000000..190283a

>> --- /dev/null

>> +++ b/libio/tst-wmemstream3.c

>> @@ -0,0 +1,44 @@

>> +/* Test for open_memstream implementation.

>> +   Copyright (C) 2016 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/>.  */

>> +

>> +#include <wchar.h>

>> +

>> +/* Straighforward implementation so tst-memstream3 could use check

>> +   fwrite on open_memstream.  */

>> +static size_t

>> +fwwrite (const void *ptr, size_t size, size_t nmemb, FILE *arq)

>> +{

>> +  const wchar_t *wcs = (const wchar_t*) (ptr);

>> +  for (size_t s = 0; s < size; s++)

>> +    {

>> +      for (size_t n = 0; n < nmemb; n++)

>> +        if (fputwc (wcs[n], arq) == WEOF)

>> +          return n; 

>> +    }

>> +  return size * nmemb; 

>> +}

>> +

>> +#define CHAR_T wchar_t

>> +#define W(o) L##o

>> +#define OPEN_MEMSTREAM open_wmemstream

>> +#define PRINTF wprintf

>> +#define FWRITE fwwrite

>> +#define FPUTC  fputwc

>> +#define STRCMP wcscmp

>> +

>> +#include "tst-memstream3.c"

>> diff --git a/libio/wmemstream.c b/libio/wmemstream.c

>> index bf2a50b..fd01be0 100644

>> --- a/libio/wmemstream.c

>> +++ b/libio/wmemstream.c

>> @@ -112,8 +112,6 @@ _IO_wmem_sync (_IO_FILE *fp)

>>        _IO_wstr_overflow (fp, '\0');

>>        --fp->_wide_data->_IO_write_ptr;

>>      }

>> -  else

>> -    *fp->_wide_data->_IO_write_ptr = '\0';

>>  

>>    *mp->bufloc = fp->_wide_data->_IO_write_base;

>>    *mp->sizeloc = (fp->_wide_data->_IO_write_ptr

>> diff --git a/libio/wstrops.c b/libio/wstrops.c

>> index 09fa543..0b2bec3 100644

>> --- a/libio/wstrops.c

>> +++ b/libio/wstrops.c

>> @@ -169,7 +169,7 @@ _IO_wstr_count (_IO_FILE *fp)

>>  static int

>>  enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)

>>  {

>> -  if ((_IO_ssize_t) offset <= _IO_blen (fp))

>> +  if ((_IO_ssize_t) offset <= _IO_wblen (fp))

>>      return 0;

>>  

>>    struct _IO_wide_data *wd = fp->_wide_data;

>> @@ -235,6 +235,22 @@ enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)

>>    return 0;

>>  }

>>  

>> +static void

>> +_IO_wstr_switch_to_get_mode (_IO_FILE *fp)

>> +{

>> +  if (_IO_in_backup (fp))

>> +    fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_backup_base;

>> +  else

>> +    {

>> +      fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_buf_base;

>> +      if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)

>> +        fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;

>> +    }

>> +  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;

>> +  fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;

>> +

>> +  fp->_flags &= ~_IO_CURRENTLY_PUTTING;

>> +}

>>  

>>  _IO_off64_t

>>  _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>> @@ -244,15 +260,16 @@ _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>    if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))

>>      mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);

>>  

>> +  bool was_writing = (fp->_wide_data->_IO_write_ptr >

>> +			fp->_wide_data->_IO_write_base

>> +		     || _IO_in_put_mode (fp));

>> +  if (was_writing)

>> +    _IO_wstr_switch_to_get_mode (fp);

>> +

>>    if (mode == 0)

>>      {

>> -      /* Don't move any pointers. But there is no clear indication what

>> -	 mode FP is in. Let's guess. */

>> -      if (fp->_IO_file_flags & _IO_NO_WRITES)

>> -        new_pos = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_base;

>> -      else

>> -        new_pos = (fp->_wide_data->_IO_write_ptr

>> -		   - fp->_wide_data->_IO_write_base);

>> +      new_pos = (fp->_wide_data->_IO_write_ptr

>> +		 - fp->_wide_data->_IO_write_base);

>>      }

>>    else

>>      {

>> @@ -262,25 +279,32 @@ _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>        /* Move the get pointer, if requested. */

>>        if (mode & _IOS_INPUT)

>>  	{

>> +	  _IO_ssize_t base;

>>  	  switch (dir)

>>  	    {

>> -	    case _IO_seek_end:

>> -	      offset += cur_size;

>> +	    case _IO_seek_set:

>> +	      base = 0;

>>  	      break;

>>  	    case _IO_seek_cur:

>> -	      offset += (fp->_wide_data->_IO_read_ptr

>> -			 - fp->_wide_data->_IO_read_base);

>> +	      base = (fp->_wide_data->_IO_read_ptr

>> +		     - fp->_wide_data->_IO_read_base);

>>  	      break;

>> -	    default: /* case _IO_seek_set: */

>> +	    default: /* case _IO_seek_end: */

>> +	      base = cur_size;

>>  	      break;

>>  	    }

>> -	  if (offset < 0)

>> -	    return EOF;

>> -	  if ((_IO_ssize_t) offset > cur_size

>> -	      && enlarge_userbuf (fp, offset, 1) != 0)

>> +	  _IO_ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;

>> +	  if (offset < -base || offset > maxval)

>> +	    {

>> +	      __set_errno (EINVAL);

>> +	      return EOF;

>> +	    }

>> +	  base += offset;

>> +	  if (base > cur_size

>> +	      && enlarge_userbuf (fp, base, 1) != 0)

>>  	    return EOF;

>>  	  fp->_wide_data->_IO_read_ptr = (fp->_wide_data->_IO_read_base

>> -					  + offset);

>> +					  + base);

>>  	  fp->_wide_data->_IO_read_end = (fp->_wide_data->_IO_read_base

>>  					  + cur_size);

>>  	  new_pos = offset;

>> @@ -289,26 +313,33 @@ _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>        /* Move the put pointer, if requested. */

>>        if (mode & _IOS_OUTPUT)

>>  	{

>> +	  _IO_ssize_t base;

>>  	  switch (dir)

>>  	    {

>> -	    case _IO_seek_end:

>> -	      offset += cur_size;

>> +	    case _IO_seek_set:

>> +	      base = 0;

>>  	      break;

>>  	    case _IO_seek_cur:

>> -	      offset += (fp->_wide_data->_IO_write_ptr

>> -			 - fp->_wide_data->_IO_write_base);

>> +	      base = (fp->_wide_data->_IO_write_ptr

>> +		     - fp->_wide_data->_IO_write_base);

>>  	      break;

>> -	    default: /* case _IO_seek_set: */

>> +	    default: /* case _IO_seek_end: */

>> +	      base = cur_size;

>>  	      break;

>>  	    }

>> -	  if (offset < 0)

>> -	    return EOF;

>> -	  if ((_IO_ssize_t) offset > cur_size

>> -	      && enlarge_userbuf (fp, offset, 0) != 0)

>> +	  _IO_ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;

>> +	  if (offset < -base || offset > maxval)

>> +	    {

>> +	      __set_errno (EINVAL);

>> +	      return EOF;

>> +	    }

>> +	  base += offset;

>> +	  if (base > cur_size

>> +	      && enlarge_userbuf (fp, base, 0) != 0)

>>  	    return EOF;

>>  	  fp->_wide_data->_IO_write_ptr = (fp->_wide_data->_IO_write_base

>> -					   + offset);

>> -	  new_pos = offset;

>> +					   + base);

>> +	  new_pos = base;

>>  	}

>>      }

>>    return new_pos;

>> diff --git a/manual/examples/memstrm.c b/manual/examples/memstrm.c

>> index 0d443b1..5701ba1 100644

>> --- a/manual/examples/memstrm.c

>> +++ b/manual/examples/memstrm.c

>> @@ -27,10 +27,10 @@ main (void)

>>    stream = open_memstream (&bp, &size);

>>    fprintf (stream, "hello");

>>    fflush (stream);

>> -  printf ("buf = `%s', size = %d\n", bp, size);

>> +  printf ("buf = `%s', size = %zu\n", bp, size);

>>    fprintf (stream, ", world");

>>    fclose (stream);

>> -  printf ("buf = `%s', size = %d\n", bp, size);

>> +  printf ("buf = `%s', size = %zu\n", bp, size);

>>  

>>    return 0;

>>  }

>>
Adhemerval Zanella Oct. 2, 2016, 1:28 p.m. UTC | #3
Unfortunately, I did not see it on the environments I have tested
(basically ubuntu 16).  I pushed a obvious fix for this as edbdf87,
thanks for reporting it.

On 01/10/2016 06:17, Andreas Schwab wrote:
> tst-memstream3.c:32:17: error: implicit declaration of function 'strcmp' [-Werror=implicit-function-declaration]

>  # define STRCMP strcmp

>                  ^

> tst-memstream3.c:96:7: note: in expansion of macro 'STRCMP'

>    if (STRCMP (buf, W("b")) != 0)

>        ^~~~~~

> 

> Andreas.

>
Dmitry V. Levin April 19, 2017, 12:35 p.m. UTC | #4
On Wed, Apr 19, 2017 at 11:17:21AM +0200, Florian Weimer wrote:
[...]
> I'm leaning towards a clean break: Stop installing <libio.h>.


You must be joking.  This would definitely break libzio.


-- 
ldv
Zack Weinberg April 19, 2017, 12:52 p.m. UTC | #5
On Wed, Apr 19, 2017 at 5:17 AM, Florian Weimer <fweimer@redhat.com> wrote:
> I'm leaning towards a clean break: Stop installing <libio.h>.  Remove all

> symbols related to external vtable support (i.e., an ABI break, so that

> affected programs fail in a clean manner).  Do this now, and revisit it for

> glibc 2.27 if someone actually has an application that breaks due to this.


I had been thinking about proposing a patch to stop installing
libio.h, myself.  It definitely is getting in the way of forward
progress.

However, stdio in general could use a great deal of revision, and we
don't want to break compatibility several releases in a row.  Maybe it
makes more sense to start in on a "revise stdio" project on a branch,
but not merge any breaking changes until it's completely ready to go?
Meanwhile, on trunk, we could make stdio.h stop including libio.h and
add a deprecation warning to libio.h -- that should be enough to flush
out applications that still need it, and find out what exactly they
need.

(I wish I had time to help with a stdio overhaul, but realistically I don't.)

zw
Zack Weinberg April 19, 2017, 12:52 p.m. UTC | #6
On Wed, Apr 19, 2017 at 8:35 AM, Dmitry V. Levin <ldv@altlinux.org> wrote:
> On Wed, Apr 19, 2017 at 11:17:21AM +0200, Florian Weimer wrote:

> [...]

>> I'm leaning towards a clean break: Stop installing <libio.h>.

>

> You must be joking.  This would definitely break libzio.


Never heard of it. What applications use it?

zw
Florian Weimer April 19, 2017, 1:01 p.m. UTC | #7
On 04/19/2017 02:35 PM, Dmitry V. Levin wrote:
> On Wed, Apr 19, 2017 at 11:17:21AM +0200, Florian Weimer wrote:

> [...]

>> I'm leaning towards a clean break: Stop installing <libio.h>.

> 

> You must be joking.  This would definitely break libzio.


I'm not.

I just tried, and libzio 1.04 compiles fine with !HAVE_LIBIO_H.  It 
doesn't define its own vtables, either, nor does it internal _IO_* symbols.

Florian
Dmitry V. Levin April 19, 2017, 1:03 p.m. UTC | #8
On Wed, Apr 19, 2017 at 08:52:55AM -0400, Zack Weinberg wrote:
> On Wed, Apr 19, 2017 at 8:35 AM, Dmitry V. Levin <ldv@altlinux.org> wrote:

> > On Wed, Apr 19, 2017 at 11:17:21AM +0200, Florian Weimer wrote:

> > [...]

> >> I'm leaning towards a clean break: Stop installing <libio.h>.

> >

> > You must be joking.  This would definitely break libzio.

> 

> Never heard of it. What applications use it?


A slightly patched version (git://git.altlinux.org/gears/l/libzio.git)
is part of our build environment:

$ rpm -e --test libzio
error: Failed dependencies:
	libzio.so.0()(64bit) >= set:feLS is needed by (installed) info-install-6.0-alt3.x86_64
	libzio.so.0()(64bit) >= set:feLS is needed by (installed) patchutils-0.3.4-alt1.x86_64


-- 
ldv
Dmitry V. Levin April 19, 2017, 1:17 p.m. UTC | #9
On Wed, Apr 19, 2017 at 03:01:25PM +0200, Florian Weimer wrote:
> On 04/19/2017 02:35 PM, Dmitry V. Levin wrote:

> > On Wed, Apr 19, 2017 at 11:17:21AM +0200, Florian Weimer wrote:

> > [...]

> >> I'm leaning towards a clean break: Stop installing <libio.h>.

> > 

> > You must be joking.  This would definitely break libzio.

> 

> I'm not.

> 

> I just tried, and libzio 1.04 compiles fine with !HAVE_LIBIO_H.  It 

> doesn't define its own vtables, either, nor does it internal _IO_* symbols.


libzio uses cookie_io_functions_t provided by libio.h;
in case of !HAVE_LIBIO_H it falls back to _IO_cookie_io_functions_t
also defined by libio.h via stdio.h;

If stdio.h continues to provide fopencookie and _IO_cookie_io_functions_t,
this should be enough for libzio.


-- 
ldv
Florian Weimer April 19, 2017, 1:23 p.m. UTC | #10
On 04/19/2017 03:17 PM, Dmitry V. Levin wrote:
> On Wed, Apr 19, 2017 at 03:01:25PM +0200, Florian Weimer wrote:

>> On 04/19/2017 02:35 PM, Dmitry V. Levin wrote:

>>> On Wed, Apr 19, 2017 at 11:17:21AM +0200, Florian Weimer wrote:

>>> [...]

>>>> I'm leaning towards a clean break: Stop installing <libio.h>.

>>>

>>> You must be joking.  This would definitely break libzio.

>>

>> I'm not.

>>

>> I just tried, and libzio 1.04 compiles fine with !HAVE_LIBIO_H.  It

>> doesn't define its own vtables, either, nor does it internal _IO_* symbols.

> 

> libzio uses cookie_io_functions_t provided by libio.h;

> in case of !HAVE_LIBIO_H it falls back to _IO_cookie_io_functions_t

> also defined by libio.h via stdio.h;

> 

> If stdio.h continues to provide fopencookie and _IO_cookie_io_functions_t,

> this should be enough for libzio.


The documented type of the fopencookie argument is 
cookie_io_functions_t, and we will continue to provide that from 
<stdio.h> (if _GNU_SOURCE is defined).

Thanks,
Florian
Adhemerval Zanella April 19, 2017, 2:48 p.m. UTC | #11
On 19/04/2017 06:17, Florian Weimer wrote:
> On 08/08/2016 10:43 PM, Adhemerval Zanella wrote:

>> @@ -239,14 +254,14 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>     if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))

>>       mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);

>>   +  bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base

>> +             || _IO_in_put_mode (fp));

>> +  if (was_writing)

>> +    _IO_str_switch_to_get_mode (fp);

> 

> This patch breaks backwards compatibility with applications which call _IO_str_seekoff directly.  This is an exported function and it was originally intended as a building block for building custom streams, so we cannot change what it does just to fit glibc's internal needs, based on how the function is called from within glibc.

> 

> But if we apply this standard of backwards compatibility, we cannot make *any* changes to libio (including important security hardening) without copying most of the code first.  We have no tests which check the extended API behavior, and the interface is very much under-documented, too.

> 

> What should we do here?


Right, so should we revert the patch, reopen both bugs and rework all libio 
in this case (which might span on multiple releases)? I know that we should
aim for compatibility where applicable, but I think blindly aim for it even
for bug/out of conformance cases adds more maintainer burden that actually
fixes real cases usage. 

For this specific case, the code is clearly buggy when ran a different libc
for a non-specific gnu extension (open_memstream). Should we still provide 
buggy compat interfaces in this case (as we are still aiming to provide)?

> 

> I'm leaning towards a clean break: Stop installing <libio.h>.  Remove all symbols related to external vtable support (i.e., an ABI break, so that affected programs fail in a clean manner).  Do this now, and revisit it for glibc 2.27 if someone actually has an application that breaks due to this.

> 

> For the old inlined putc_unlocked implementation which directly poked at struct _IO_FILE, I suggest we write tests and keep compatibility.

> 

> Comments?

> 

> Thanks,

> Florian
Florian Weimer April 19, 2017, 3:02 p.m. UTC | #12
On 04/19/2017 04:48 PM, Adhemerval Zanella wrote:
> 

> 

> On 19/04/2017 06:17, Florian Weimer wrote:

>> On 08/08/2016 10:43 PM, Adhemerval Zanella wrote:

>>> @@ -239,14 +254,14 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>>      if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))

>>>        mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);

>>>    +  bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base

>>> +             || _IO_in_put_mode (fp));

>>> +  if (was_writing)

>>> +    _IO_str_switch_to_get_mode (fp);

>>

>> This patch breaks backwards compatibility with applications which call _IO_str_seekoff directly.  This is an exported function and it was originally intended as a building block for building custom streams, so we cannot change what it does just to fit glibc's internal needs, based on how the function is called from within glibc.

>>

>> But if we apply this standard of backwards compatibility, we cannot make *any* changes to libio (including important security hardening) without copying most of the code first.  We have no tests which check the extended API behavior, and the interface is very much under-documented, too.

>>

>> What should we do here?

> 

> Right, so should we revert the patch, reopen both bugs and rework all libio

> in this case (which might span on multiple releases)? I know that we should

> aim for compatibility where applicable, but I think blindly aim for it even

> for bug/out of conformance cases adds more maintainer burden that actually

> fixes real cases usage.

> 

> For this specific case, the code is clearly buggy when ran a different libc

> for a non-specific gnu extension (open_memstream). Should we still provide

> buggy compat interfaces in this case (as we are still aiming to provide)?


Sorry, I wasn't clear.  The buggy interface is open_memstream.  Fixing 
that is completely fine, no compatibility symbol is required.  But 
_IO_str_seekoff is a completely different, allegedly public interface, 
and the existing callers most expect some concrete behavior from it.

A hypothetical example of the same scenario: posix_fallocate used to 
have a bug that it did not work on O_APPEND descriptors because pwrite64 
ignored the offset for them, as required by POSIX.  We could have fixed 
that by changing pwrite64 not to ignore the write offset, but that's of 
course bogus because the specified semantics for pwrite64 require 
different behavior.

What I'm trying to say is that similar, but undocumented requirements 
might well exist for the _IO_str_seekoff function.  Hence my original 
comment that we'd need to make a copy first, fix the copy, and adjust 
internal callers to use the copy, leaving the original implementation alone.

Things get worse once we start changing struct definitions.  Then we 
basically have to duplicate everything that depends on those structs.

I think that's not a good use of our time because it is very unlikely 
that there are any applications left which use these interfaces. 
Instead, I suggest that we make explicit that the internal libio 
interfaces are unsupported (because they are unsupportable), and remove 
them from the ABI, so that people who have those old binaries get clear 
dynamic linker failures instead of corrupted data.

Thanks,
Florian
Adhemerval Zanella April 19, 2017, 3:17 p.m. UTC | #13
On 19/04/2017 12:02, Florian Weimer wrote:
> On 04/19/2017 04:48 PM, Adhemerval Zanella wrote:

>>

>>

>> On 19/04/2017 06:17, Florian Weimer wrote:

>>> On 08/08/2016 10:43 PM, Adhemerval Zanella wrote:

>>>> @@ -239,14 +254,14 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)

>>>>      if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))

>>>>        mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);

>>>>    +  bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base

>>>> +             || _IO_in_put_mode (fp));

>>>> +  if (was_writing)

>>>> +    _IO_str_switch_to_get_mode (fp);

>>>

>>> This patch breaks backwards compatibility with applications which call _IO_str_seekoff directly.  This is an exported function and it was originally intended as a building block for building custom streams, so we cannot change what it does just to fit glibc's internal needs, based on how the function is called from within glibc.

>>>

>>> But if we apply this standard of backwards compatibility, we cannot make *any* changes to libio (including important security hardening) without copying most of the code first.  We have no tests which check the extended API behavior, and the interface is very much under-documented, too.

>>>

>>> What should we do here?

>>

>> Right, so should we revert the patch, reopen both bugs and rework all libio

>> in this case (which might span on multiple releases)? I know that we should

>> aim for compatibility where applicable, but I think blindly aim for it even

>> for bug/out of conformance cases adds more maintainer burden that actually

>> fixes real cases usage.

>>

>> For this specific case, the code is clearly buggy when ran a different libc

>> for a non-specific gnu extension (open_memstream). Should we still provide

>> buggy compat interfaces in this case (as we are still aiming to provide)?

> 

> Sorry, I wasn't clear.  The buggy interface is open_memstream.  Fixing that is completely fine, no compatibility symbol is required.  But _IO_str_seekoff is a completely different, allegedly public interface, and the existing callers most expect some concrete behavior from it.

> 

> A hypothetical example of the same scenario: posix_fallocate used to have a bug that it did not work on O_APPEND descriptors because pwrite64 ignored the offset for them, as required by POSIX.  We could have fixed that by changing pwrite64 not to ignore the write offset, but that's of course bogus because the specified semantics for pwrite64 require different behavior.

> 

> What I'm trying to say is that similar, but undocumented requirements might well exist for the _IO_str_seekoff function.  Hence my original comment that we'd need to make a copy first, fix the copy, and adjust internal callers to use the copy, leaving the original implementation alone.

> 

> Things get worse once we start changing struct definitions.  Then we basically have to duplicate everything that depends on those structs.

> 

> I think that's not a good use of our time because it is very unlikely that there are any applications left which use these interfaces. Instead, I suggest that we make explicit that the internal libio interfaces are unsupported (because they are unsupportable), and remove them from the ABI, so that people who have those old binaries get clear dynamic linker failures instead of corrupted data.


This seems a more reasonable approach, I realised it was about _IO_str_seekoff
exposure right after send the email (sorry about that noise).
Zack Weinberg April 20, 2017, 3:44 p.m. UTC | #14
On Wed, Apr 19, 2017 at 5:17 AM, Florian Weimer <fweimer@redhat.com> wrote:
>

> I'm leaning towards a clean break: Stop installing <libio.h>.  Remove all

> symbols related to external vtable support (i.e., an ABI break, so that

> affected programs fail in a clean manner).  Do this now, and revisit it for

> glibc 2.27 if someone actually has an application that breaks due to this.


If you pull branch zack/no-libio-h you will get a libc that does not
install libio.h.  I didn't convert any of the currently-exposed _IO_
symbols into compat symbols, and I strongly suspect that I broke
support for the old (GLIBC_2.0) FILE object, but this should be good
enough to do archive rebuilds against.

For convenience, the changes are attached.  However, the branch is
built on top of a bunch of my other header cleanup work (_ISOMAC
testsuite and several bits/types/ conversions) so do not expect the
attached patch to apply to trunk.

zwFrom c5176fb74279fde580bc31a385852645f910b5fa Mon Sep 17 00:00:00 2001
From: Zack Weinberg <zackw@panix.com>
Date: Thu, 20 Apr 2017 11:21:30 -0400
Subject: [PATCH] Don't install libio.h or _G_config.h.

This is an experimental patch which removes libio.h (and _G_config.h)
from the set of application-exposed headers.  After this change, the
public stdio.h does not define any symbols whose names begin with _G_
nor _IO_, except that when optimizing, the guts of struct _IO_FILE and
three of the flag constants are visible (see bits/stdio.h and
bits/types/FILE_internals.h).  There is a small amount of code
duplication in bits/stdio.h, of macro bodies from libio.h that are no
longer available.  A number of internal .c files that were manually
doing PLT bypass for flockfile/funlockfile can now rely on
include/stdio.h to do it for them.

It passes the testsuite on x86_64-linux, but it needs a great deal of
additional testing; in particular I'm almost certain I broke the
support for old-format (GLIBC_2.0) struct _IO_FILE, which is
configured out on this target.  Testing this properly would require
someone to get their hands on _really_ old binaries, compiled against
glibc 2.0, possibly statically-linked-but-using-NSS.  Unfortunately,
libc.so cannot be expected to be binary identical.

However, this should be ready to feed into archive rebuilds to find
out what applications break.

Substantial clean-ups to the libio implementation are possible if this
sticks, but I haven't done 'em; this is intended to be minimal.

	* libio/Makefile: Don't install libio.h or _G_config.h.  Do install
	bits/types/FILE_internals.h, bits/types/cookie_io_functions_t.h,
	and bits/types/__fpos_t.h.

	* libio/stdio.h: Don't include libio.h.  Get __gnuc_va_list
	directly from stdarg.h, __fpos_t and __fpos64_t from
	bits/types/__fpos_t.h, and the cookie types from
	bits/types/cookie_io_functions_t.h.  Change all uses of
	_G_va_list, _G_fpos_t, _G_fpos64_t, _IO_FILE,
	_IO_cookie_io_functions_t, and _IO_ssize_t to __gnuc_va_list,
	__fpos_t, __fpos64_t, FILE, cookie_io_functions_t, and __ssize_t
	respectively.
	Do not define getc nor putc as macros.
	Define BUFSIZ as literal 8192.

	* libio/bits/types/FILE_internals.h: New header. Provide complete
	definition of struct _IO_FILE (the complete version) here.
	Duplicate definitions of _IO_EOF_SEEN, _IO_ERR_SEEN, and _IO_USER_LOCK
	here, with value assertions if they are already defined.
	* libio/bits/types/__fpos_t.h: New header. Define __fpos_t and
	__fpos64_t here.
	* libio/bits/types/cookie_io_functions_t.h: New header.  Define
	cookie_read_function_t, cookie_write_function_t,
	cookie_seek_function_t, cookie_close_function_t, and
	cookie_io_functions_t here.

	* libio/libio.h: Include features.h first thing, then error out if
	either _LIBC or __USE_GNU is not defined, or if _ISOMAC is
	defined.  Inline all of _G_config.h except _G_HAVE_MREMAP here.
	Get definitions of __mbstate_t, __fpos_t, __fpos64_t, struct
	_IO_FILE, and the cookie-related types from the relevant
	bits/types headers.  Get definition of NULL from stddef.h.
	Make all #ifdef _LIBC and #if __GNUC__ >= (2,3) blocks
	unconditional.  Remove all #if 0 and #ifdef __cplusplus blocks.
	Change all uses of _G_va_list, _G_fpos_t, and _G_fpos64_t to
	__gnuc_va_list, __fpos_t, __fpos64_t respectively.  Provide
	definitions of _STDIO_USES_IOSTREAM, __HAVE_COLUMN,
	_IO_file_flags, __io_read_fn, __io_write_fn, __io_seek_fn,
	__io_close_fn, _IO_cookie_io_functions_t for the sake of the
	implementation.  When _IO_USE_OLD_IO_FILE is defined, define
	struct _IO_FILE_old.
	* libio/libioP.h: When _IO_USE_OLD_IO_FILE is defined, define
	struct _IO_FILE_old_plus.
	* libio/oldfileops.c: Change all uses of _IO_FILE to _IO_FILE_old,
	_IO_FILE_plus to _IO_FILE_old_plus, _IO_FILE_complete to _IO_FILE,
	and _IO_FILE_complete_plus to _IO_FILE_plus.
	* sysdeps/generic/_G_config.h, sysdeps/unix/sysv/linux/_G_config.h:
	Only provide definition or non-definition of _G_HAVE_MREMAP.
	* sysdeps/ieee754/ldbl-opt/nldbl-compat.h:
	Change all uses of _G_va_list to __gnuc_va_list.

	* libio/bits/stdio.h: Add multiple-inclusion guard. Include
	bits/types/FILE_internals.h. Declare __uflow and __overflow here.
	Remove redundant __USE_EXTERN_INLINES ifdef.  Change all uses of
	_G_va_list to __gnuc_va_list and _IO_ssize_t to __ssize_t.
	(getchar): Use getc, not _IO_getc.
	(__getc_unlocked, __putc_unlocked): New inlines, duplicating the
	bodies of _IO_getc_unlocked and	_IO_putc_unlocked.
	(fgetc_unlocked, getc_unlocked, getchar_unlocked, fread_unlocked):
	Use __getc_unlocked.
	(fputc_unlocked, putc_unlocked, putchar_unlocked, fwrite_unlocked):
	Use __putc_unlocked.
	(feof_unlocked): Duplicate the body of _IO_feof_unlocked here.
	(ferror_unlocked): Duplicate the body of _IO_ferror_unlocked here.
	* libio/bits/stdio2.h: Change all uses of _G_va_list to __gnuc_va_list.
	(fread_unlocked): Use __getc_unlocked.
	* libio/bits/types/FILE.h, libio/bits/types/__FILE.h: Explain in
	comments why the name _IO_FILE is used.

	* include/stdio.h: Change all uses of _G_va_list to __gnuc_va_list,
	_IO_ssize_t to __size_t, _IO_FILE to FILE, and _IO_fpos_t to __fpos_t.
	When IS_IN (libc), redirect flockfile and funlockfile to
	__flockfile and __funlockfile respectively.
	When _IO_MTSAFE_IO and not _ISOMAC, include stdio-lock.h before
	stdio.h proper.
	* include/stdio_ext.h: Include bits/types/FILE_internals.h for the
	sake of the inline definition of __fsetlocking.
	* include/libio.h: Adjust #ifdef nest to activate multiple-include
	optimization.
	* include/bits/types/FILE_internals.h, include/bits/types/__fpos_t.h
	* include/bits/types/cookie_io_functions_t.h: New trivial wrappers.
	* include/bits/stdio.h:	New wrapper; mark __uflow and __overflow
	as hidden for intra-libc callers.

	* csu/init.c: Include libio.h, not _G_config.h.

	* grp/fgetgrent_r.c, grp/putgrent.c, gshadow/fgetsgent_r.c
	* gshadow/putsgent.c, misc/getpass.c, misc/getttyent.c
	* misc/mntent_r.c, posix/getopt.c, pwd/fgetpwent_r.c
	* shadow/fgetspent_r.c, shadow/putspent.c:
	Don't include libio/iolibio.h.  Don't redefine flockfile or
	funlockfile.  Don't use _IO_flockfile or _IO_funlockfile.

	* libio/__fbufsize.c, libio/__flbf.c, libio/__fpending.c
	* libio/__freadable.c, libio/__freading.c, libio/__fwritable.c
	* libio/__fwriting.c, malloc/malloc.c: Include libio.h.
	* misc/err.c: Include libio.h. Don't redefine flockfile or funlockfile.

	* stdio-common/tstgetln.c: Include sys/types.h. Don't redefine ssize_t.
	* conform/data/stdio.h-data: va_list may be defined as __gnuc_va_list,
	not _G_va_list.
	* benchtests/strcoll-inputs/filelist#en_US.UTF-8: Remove _G_config.h.
---
 benchtests/strcoll-inputs/filelist#en_US.UTF-8 |   2 -
 conform/data/stdio.h-data                      |   2 +-
 csu/init.c                                     |   2 +-
 grp/fgetgrent_r.c                              |   4 -
 grp/putgrent.c                                 |   3 -
 gshadow/fgetsgent_r.c                          |   8 +-
 gshadow/putsgent.c                             |   4 +-
 include/bits/stdio.h                           |   9 +
 include/bits/types/FILE_internals.h            |   1 +
 include/bits/types/__fpos_t.h                  |   1 +
 include/bits/types/cookie_io_functions_t.h     |   1 +
 include/libio.h                                |  18 +-
 include/stdio.h                                |  51 ++--
 include/stdio_ext.h                            |   1 +
 libio/Makefile                                 |   7 +-
 libio/__fbufsize.c                             |   1 +
 libio/__flbf.c                                 |   1 +
 libio/__fpending.c                             |   1 +
 libio/__freadable.c                            |   1 +
 libio/__freading.c                             |   1 +
 libio/__fwritable.c                            |   1 +
 libio/__fwriting.c                             |   1 +
 libio/bits/stdio.h                             |  71 ++++--
 libio/bits/stdio2.h                            |  31 +--
 libio/bits/types/FILE.h                        |   7 +-
 libio/bits/types/FILE_internals.h              | 110 ++++++++
 libio/bits/types/__FILE.h                      |   5 +
 libio/bits/types/__fpos_t.h                    |  19 ++
 libio/bits/types/cookie_io_functions_t.h       |  61 +++++
 libio/libio.h                                  | 333 ++++++++++---------------
 libio/libioP.h                                 |   7 +-
 libio/oldfileops.c                             |  54 ++--
 libio/stdio.h                                  |  82 +++---
 malloc/malloc.c                                |   1 +
 misc/err.c                                     |   4 +-
 misc/getpass.c                                 |   2 -
 misc/getttyent.c                               |   3 -
 misc/mntent_r.c                                |   3 -
 posix/getopt.c                                 |  14 +-
 pwd/fgetpwent_r.c                              |   3 -
 shadow/fgetspent_r.c                           |   2 -
 shadow/putspent.c                              |   2 -
 stdio-common/tstgetln.c                        |   3 +-
 sysdeps/generic/_G_config.h                    |  54 +---
 sysdeps/ieee754/ldbl-opt/nldbl-compat.h        |  13 +-
 sysdeps/unix/sysv/linux/_G_config.h            |  53 +---
 46 files changed, 547 insertions(+), 511 deletions(-)
 create mode 100644 include/bits/stdio.h
 create mode 100644 include/bits/types/FILE_internals.h
 create mode 100644 include/bits/types/__fpos_t.h
 create mode 100644 include/bits/types/cookie_io_functions_t.h
 create mode 100644 libio/bits/types/FILE_internals.h
 create mode 100644 libio/bits/types/__fpos_t.h
 create mode 100644 libio/bits/types/cookie_io_functions_t.h

diff --git a/benchtests/strcoll-inputs/filelist#en_US.UTF-8 b/benchtests/strcoll-inputs/filelist#en_US.UTF-8
index eb23b47484..63d22feb0a 100644
--- a/benchtests/strcoll-inputs/filelist#en_US.UTF-8
+++ b/benchtests/strcoll-inputs/filelist#en_US.UTF-8
@@ -1370,7 +1370,6 @@ a.out.h
 semget.c
 posix_fallocate.c
 getpid.c
-_G_config.h
 getsockopt.S
 pthread_setaffinity.c
 setipv4sourcefilter.c
@@ -3407,7 +3406,6 @@ a.out.h
 libBrokenLocale.abilist
 machine-gmon.h
 _itoa.h
-_G_config.h
 local-setxid.h
 dl-osinfo.h
 dl-dtprocnum.h
diff --git a/conform/data/stdio.h-data b/conform/data/stdio.h-data
index 168128eac9..5424d7d7f6 100644
--- a/conform/data/stdio.h-data
+++ b/conform/data/stdio.h-data
@@ -41,7 +41,7 @@ type fpos_t
 #if !defined ISO && !defined ISO99 && !defined ISO11 && !defined POSIX
 type va_list
 #else
-#define va_list _G_va_list
+#define va_list __gnuc_va_list
 #endif
 type size_t
 #if defined XOPEN2K8 || defined POSIX2008
diff --git a/csu/init.c b/csu/init.c
index bb68386c9b..6da0a9cd47 100644
--- a/csu/init.c
+++ b/csu/init.c
@@ -18,7 +18,7 @@
 
 #if defined __GNUC__ && __GNUC__ >= 2
 
-#include <_G_config.h>
+#include <libio.h>
 
 /* This records which stdio is linked against in the application. */
 const int _IO_stdin_used = _G_IO_IO_FILE_VERSION;
diff --git a/grp/fgetgrent_r.c b/grp/fgetgrent_r.c
index 5a4107ba9c..7bcbdfe539 100644
--- a/grp/fgetgrent_r.c
+++ b/grp/fgetgrent_r.c
@@ -20,10 +20,6 @@
 #include <grp.h>
 #include <stdio.h>
 
-#include <libio/iolibio.h>
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
-
 /* Define a line parsing function using the common code
    used in the nss_files module.  */
 
diff --git a/grp/putgrent.c b/grp/putgrent.c
index 5a12c70557..cd3588ded3 100644
--- a/grp/putgrent.c
+++ b/grp/putgrent.c
@@ -21,9 +21,6 @@
 #include <string.h>
 #include <grp.h>
 
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
-
 #define _S(x)	x ? x : ""
 
 /* Write an entry to the given stream.
diff --git a/gshadow/fgetsgent_r.c b/gshadow/fgetsgent_r.c
index f1d0650333..95c21e960c 100644
--- a/gshadow/fgetsgent_r.c
+++ b/gshadow/fgetsgent_r.c
@@ -38,21 +38,21 @@ __fgetsgent_r (FILE *stream, struct sgrp *resbuf, char *buffer, size_t buflen,
 {
   char *p;
 
-  _IO_flockfile (stream);
+  flockfile (stream);
   do
     {
       buffer[buflen - 1] = '\xff';
       p = fgets_unlocked (buffer, buflen, stream);
       if (p == NULL && feof_unlocked (stream))
 	{
-	  _IO_funlockfile (stream);
+	  funlockfile (stream);
 	  *result = NULL;
 	  __set_errno (ENOENT);
 	  return errno;
 	}
       if (p == NULL || buffer[buflen - 1] != '\xff')
 	{
-	  _IO_funlockfile (stream);
+	  funlockfile (stream);
 	  *result = NULL;
 	  __set_errno (ERANGE);
 	  return errno;
@@ -67,7 +67,7 @@ __fgetsgent_r (FILE *stream, struct sgrp *resbuf, char *buffer, size_t buflen,
 	     ! parse_line (buffer, (void *) resbuf, (void *) buffer, buflen,
 			   &errno));
 
-  _IO_funlockfile (stream);
+  funlockfile (stream);
 
   *result = resbuf;
   return 0;
diff --git a/gshadow/putsgent.c b/gshadow/putsgent.c
index 4e219c478a..70063d74c2 100644
--- a/gshadow/putsgent.c
+++ b/gshadow/putsgent.c
@@ -40,7 +40,7 @@ putsgent (const struct sgrp *g, FILE *stream)
       return -1;
     }
 
-  _IO_flockfile (stream);
+  flockfile (stream);
 
   if (fprintf (stream, "%s:%s:", g->sg_namp, _S (g->sg_passwd)) < 0)
     ++errors;
@@ -75,7 +75,7 @@ putsgent (const struct sgrp *g, FILE *stream)
   if (putc_unlocked ('\n', stream) == EOF)
     ++errors;
 
-  _IO_funlockfile (stream);
+  funlockfile (stream);
 
   return errors ? -1 : 0;
 }
diff --git a/include/bits/stdio.h b/include/bits/stdio.h
new file mode 100644
index 0000000000..4aebc9edf2
--- /dev/null
+++ b/include/bits/stdio.h
@@ -0,0 +1,9 @@
+#ifndef _BITS_STDIO_H
+# include <libio/bits/stdio.h>
+# ifndef _ISOMAC
+
+libc_hidden_proto (__uflow)
+libc_hidden_proto (__overflow)
+
+# endif
+#endif
diff --git a/include/bits/types/FILE_internals.h b/include/bits/types/FILE_internals.h
new file mode 100644
index 0000000000..a653c8101c
--- /dev/null
+++ b/include/bits/types/FILE_internals.h
@@ -0,0 +1 @@
+#include <libio/bits/types/FILE_internals.h>
diff --git a/include/bits/types/__fpos_t.h b/include/bits/types/__fpos_t.h
new file mode 100644
index 0000000000..2dcdc98d75
--- /dev/null
+++ b/include/bits/types/__fpos_t.h
@@ -0,0 +1 @@
+#include <libio/bits/types/__fpos_t.h>
diff --git a/include/bits/types/cookie_io_functions_t.h b/include/bits/types/cookie_io_functions_t.h
new file mode 100644
index 0000000000..87f7930c6e
--- /dev/null
+++ b/include/bits/types/cookie_io_functions_t.h
@@ -0,0 +1 @@
+#include <libio/bits/types/cookie_io_functions_t.h>
diff --git a/include/libio.h b/include/libio.h
index d2fa796758..54ed704bdd 100644
--- a/include/libio.h
+++ b/include/libio.h
@@ -1,11 +1,9 @@
-#if !defined _ISOMAC && defined _IO_MTSAFE_IO
-# include <stdio-lock.h>
-#endif
-#include <libio/libio.h>
-
-#ifndef _ISOMAC
-#ifndef _LIBC_LIBIO_H
-#define _LIBC_LIBIO_H
+#ifndef _IO_STDIO_H
+# if !defined _ISOMAC && defined _IO_MTSAFE_IO
+#  include <stdio-lock.h>
+# endif
+# include <libio/libio.h>
+# ifndef _ISOMAC
 
 libc_hidden_proto (__overflow)
 libc_hidden_proto (__underflow)
@@ -41,5 +39,5 @@ libc_hidden_proto (_IO_vfscanf)
 # endif
 #endif /* _IO_MTSAFE_IO */
 
-#endif
-#endif
+# endif /* !_ISOMAC */
+#endif /* libio.h */
diff --git a/include/stdio.h b/include/stdio.h
index f68f633bd6..824e970435 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -1,4 +1,7 @@
 #ifndef _STDIO_H
+# if !defined _ISOMAC && defined _IO_MTSAFE_IO
+#  include <stdio-lock.h>
+# endif
 # include <libio/stdio.h>
 # ifndef _ISOMAC
 
@@ -10,44 +13,44 @@ extern int __snprintf (char *__restrict __s, size_t __maxlen,
      __attribute__ ((__format__ (__printf__, 3, 4)));
 libc_hidden_proto (__snprintf)
 extern int __vsnprintf (char *__restrict __s, size_t __maxlen,
-			const char *__restrict __format, _G_va_list __arg)
+			const char *__restrict __format, __gnuc_va_list __arg)
      __attribute__ ((__format__ (__printf__, 3, 0)));
 extern int __vfscanf (FILE *__restrict __s,
 		      const char *__restrict __format,
-		      _G_va_list __arg)
+		      __gnuc_va_list __arg)
      __attribute__ ((__format__ (__scanf__, 2, 0)));
 libc_hidden_proto (__vfscanf)
 extern int __vscanf (const char *__restrict __format,
-		     _G_va_list __arg)
+		     __gnuc_va_list __arg)
      __attribute__ ((__format__ (__scanf__, 1, 0)));
-extern _IO_ssize_t __getline (char **__lineptr, size_t *__n,
+extern __ssize_t __getline (char **__lineptr, size_t *__n,
 			      FILE *__stream);
 extern int __vsscanf (const char *__restrict __s,
 		      const char *__restrict __format,
-		      _G_va_list __arg)
+		      __gnuc_va_list __arg)
      __attribute__ ((__format__ (__scanf__, 2, 0)));
 
 extern int __sprintf_chk (char *, int, size_t, const char *, ...) __THROW;
 extern int __snprintf_chk (char *, size_t, int, size_t, const char *, ...)
      __THROW;
 extern int __vsprintf_chk (char *, int, size_t, const char *,
-			   _G_va_list) __THROW;
+			   __gnuc_va_list) __THROW;
 extern int __vsnprintf_chk (char *, size_t, int, size_t, const char *,
-			    _G_va_list) __THROW;
+			    __gnuc_va_list) __THROW;
 extern int __printf_chk (int, const char *, ...);
 extern int __fprintf_chk (FILE *, int, const char *, ...);
-extern int __vprintf_chk (int, const char *, _G_va_list);
-extern int __vfprintf_chk (FILE *, int, const char *, _G_va_list);
+extern int __vprintf_chk (int, const char *, __gnuc_va_list);
+extern int __vfprintf_chk (FILE *, int, const char *, __gnuc_va_list);
 extern char *__fgets_unlocked_chk (char *buf, size_t size, int n, FILE *fp);
 extern char *__fgets_chk (char *buf, size_t size, int n, FILE *fp);
 extern int __asprintf_chk (char **, int, const char *, ...) __THROW;
-extern int __vasprintf_chk (char **, int, const char *, _G_va_list) __THROW;
+extern int __vasprintf_chk (char **, int, const char *, __gnuc_va_list) __THROW;
 extern int __dprintf_chk (int, int, const char *, ...);
-extern int __vdprintf_chk (int, int, const char *, _G_va_list);
+extern int __vdprintf_chk (int, int, const char *, __gnuc_va_list);
 extern int __obstack_printf_chk (struct obstack *, int, const char *, ...)
      __THROW;
 extern int __obstack_vprintf_chk (struct obstack *, int, const char *,
-				  _G_va_list) __THROW;
+				  __gnuc_va_list) __THROW;
 
 extern int __isoc99_fscanf (FILE *__restrict __stream,
 			    const char *__restrict __format, ...) __wur;
@@ -56,12 +59,12 @@ extern int __isoc99_sscanf (const char *__restrict __s,
 			    const char *__restrict __format, ...) __THROW;
 extern int __isoc99_vfscanf (FILE *__restrict __s,
 			     const char *__restrict __format,
-			     _G_va_list __arg) __wur;
+			     __gnuc_va_list __arg) __wur;
 extern int __isoc99_vscanf (const char *__restrict __format,
-			    _G_va_list __arg) __wur;
+			    __gnuc_va_list __arg) __wur;
 extern int __isoc99_vsscanf (const char *__restrict __s,
 			     const char *__restrict __format,
-			     _G_va_list __arg) __THROW;
+			     __gnuc_va_list __arg) __THROW;
 libc_hidden_proto (__isoc99_vsscanf)
 libc_hidden_proto (__isoc99_vfscanf)
 
@@ -96,9 +99,15 @@ libc_hidden_proto (__fortify_fail)
 
 /* Acquire ownership of STREAM.  */
 extern void __flockfile (FILE *__stream);
+#  if IS_IN (libc)
+#   define flockfile(stream) __flockfile(stream)
+#  endif
 
 /* Relinquish the ownership granted for STREAM.  */
 extern void __funlockfile (FILE *__stream);
+#  if IS_IN (libc)
+#   define funlockfile(stream) __funlockfile(stream)
+#  endif
 
 /* Try to acquire ownership of STREAM but do not block if it is not
    possible.  */
@@ -117,18 +126,18 @@ extern int _sys_nerr_internal attribute_hidden;
 
 libc_hidden_proto (__asprintf)
 #  if IS_IN (libc)
-extern _IO_FILE *_IO_new_fopen (const char*, const char*);
+extern FILE *_IO_new_fopen (const char*, const char*);
 #   define fopen(fname, mode) _IO_new_fopen (fname, mode)
-extern _IO_FILE *_IO_new_fdopen (int, const char*);
+extern FILE *_IO_new_fdopen (int, const char*);
 #   define fdopen(fd, mode) _IO_new_fdopen (fd, mode)
-extern int _IO_new_fclose (_IO_FILE*);
+extern int _IO_new_fclose (FILE*);
 #   define fclose(fp) _IO_new_fclose (fp)
-extern int _IO_fputs (const char*, _IO_FILE*);
+extern int _IO_fputs (const char*, FILE*);
 libc_hidden_proto (_IO_fputs)
 #   define fputs(str, fp) _IO_fputs (str, fp)
-extern int _IO_new_fsetpos (_IO_FILE *, const _IO_fpos_t *);
+extern int _IO_new_fsetpos (FILE *, const __fpos_t *);
 #   define fsetpos(fp, posp) _IO_new_fsetpos (fp, posp)
-extern int _IO_new_fgetpos (_IO_FILE *, _IO_fpos_t *);
+extern int _IO_new_fgetpos (FILE *, __fpos_t *);
 #   define fgetpos(fp, posp) _IO_new_fgetpos (fp, posp)
 #  endif
 
diff --git a/include/stdio_ext.h b/include/stdio_ext.h
index 29c6e68cdb..7f8835211e 100644
--- a/include/stdio_ext.h
+++ b/include/stdio_ext.h
@@ -2,6 +2,7 @@
 #include <stdio-common/stdio_ext.h>
 
 # ifndef _ISOMAC
+# include <libio/bits/types/FILE_internals.h>
 
 libc_hidden_proto (__fsetlocking)
 
diff --git a/libio/Makefile b/libio/Makefile
index a002a3365c..63191f386c 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -22,9 +22,10 @@ subdir	:= libio
 
 include ../Makeconfig
 
-headers	:= stdio.h libio.h _G_config.h bits/stdio.h \
-	   bits/sys_errlist.h bits/stdio2.h bits/stdio-ldbl.h bits/libio-ldbl.h \
-	   bits/types/FILE.h bits/types/__FILE.h
+headers := stdio.h bits/stdio.h bits/stdio2.h bits/sys_errlist.h	      \
+	   bits/stdio-ldbl.h bits/libio-ldbl.h				      \
+	   bits/types/FILE.h bits/types/__FILE.h bits/types/FILE_internals.h  \
+	   bits/types/cookie_io_functions_t.h bits/types/__fpos_t.h
 
 routines	:=							      \
 	filedoalloc iofclose iofdopen iofflush iofgetpos iofgets iofopen      \
diff --git a/libio/__fbufsize.c b/libio/__fbufsize.c
index 74d2ebbe81..8c00ca7658 100644
--- a/libio/__fbufsize.c
+++ b/libio/__fbufsize.c
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdio_ext.h>
+#include <libio.h>
 
 size_t
 __fbufsize (FILE *fp)
diff --git a/libio/__flbf.c b/libio/__flbf.c
index 929175df70..dfa68aa2e8 100644
--- a/libio/__flbf.c
+++ b/libio/__flbf.c
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdio_ext.h>
+#include <libio.h>
 
 int
 __flbf (FILE *fp)
diff --git a/libio/__fpending.c b/libio/__fpending.c
index e957839985..008cf08fb4 100644
--- a/libio/__fpending.c
+++ b/libio/__fpending.c
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdio_ext.h>
+#include <libio.h>
 
 size_t
 __fpending (FILE *fp)
diff --git a/libio/__freadable.c b/libio/__freadable.c
index 3bde42a729..d5535e902b 100644
--- a/libio/__freadable.c
+++ b/libio/__freadable.c
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdio_ext.h>
+#include <libio.h>
 
 int
 __freadable (FILE *fp)
diff --git a/libio/__freading.c b/libio/__freading.c
index f16f42679f..32e26f8a6c 100644
--- a/libio/__freading.c
+++ b/libio/__freading.c
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdio_ext.h>
+#include <libio.h>
 
 int
 __freading (FILE *fp)
diff --git a/libio/__fwritable.c b/libio/__fwritable.c
index 1584aec68f..328e0b6b5b 100644
--- a/libio/__fwritable.c
+++ b/libio/__fwritable.c
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdio_ext.h>
+#include <libio.h>
 
 int
 __fwritable (FILE *fp)
diff --git a/libio/__fwriting.c b/libio/__fwriting.c
index 1769d2107e..2ec8fba1cf 100644
--- a/libio/__fwriting.c
+++ b/libio/__fwriting.c
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdio_ext.h>
+#include <libio.h>
 
 int
 __fwriting (FILE *fp)
diff --git a/libio/bits/stdio.h b/libio/bits/stdio.h
index 21ad2fb20c..ad77b04a4f 100644
--- a/libio/bits/stdio.h
+++ b/libio/bits/stdio.h
@@ -16,24 +16,30 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#ifndef _BITS_STDIO_H
+#define _BITS_STDIO_H 1
+
 #ifndef _STDIO_H
 # error "Never include <bits/stdio.h> directly; use <stdio.h> instead."
 #endif
 
+#include <bits/types/FILE_internals.h>
+
+extern int __uflow (FILE *);
+extern int __overflow (FILE *, int);
+
 #ifndef __extern_inline
 # define __STDIO_INLINE inline
 #else
 # define __STDIO_INLINE __extern_inline
 #endif
 
-
-#ifdef __USE_EXTERN_INLINES
 /* For -D_FORTIFY_SOURCE{,=2} bits/stdio2.h will define a different
    inline.  */
 # if !(__USE_FORTIFY_LEVEL > 0 && defined __fortify_function)
 /* Write formatted output to stdout from argument list ARG.  */
 __STDIO_INLINE int
-vprintf (const char *__restrict __fmt, _G_va_list __arg)
+vprintf (const char *__restrict __fmt, __gnuc_va_list __arg)
 {
   return vfprintf (stdout, __fmt, __arg);
 }
@@ -43,33 +49,46 @@ vprintf (const char *__restrict __fmt, _G_va_list __arg)
 __STDIO_INLINE int
 getchar (void)
 {
-  return _IO_getc (stdin);
+  return getc (stdin);
 }
 
+#if defined __USE_MISC || defined __USE_POSIX
+__STDIO_INLINE int
+__getc_unlocked (FILE *__fp)
+{
+  char __c;
+
+  if (__glibc_unlikely (__fp->_IO_read_ptr >= __fp->_IO_read_end))
+    return __uflow (__fp);
+
+  __c = *__fp->_IO_read_ptr++;
+  return (unsigned char) __c;
+}
+#endif
+
 
 # ifdef __USE_MISC
 /* Faster version when locking is not necessary.  */
 __STDIO_INLINE int
 fgetc_unlocked (FILE *__fp)
 {
-  return _IO_getc_unlocked (__fp);
+  return __getc_unlocked (__fp);
 }
 # endif /* misc */
 
-
 # ifdef __USE_POSIX
 /* This is defined in POSIX.1:1996.  */
 __STDIO_INLINE int
 getc_unlocked (FILE *__fp)
 {
-  return _IO_getc_unlocked (__fp);
+  return __getc_unlocked (__fp);
 }
 
 /* This is defined in POSIX.1:1996.  */
 __STDIO_INLINE int
 getchar_unlocked (void)
 {
-  return _IO_getc_unlocked (stdin);
+  return __getc_unlocked (stdin);
 }
 # endif	/* POSIX */
 
@@ -78,16 +97,28 @@ getchar_unlocked (void)
 __STDIO_INLINE int
 putchar (int __c)
 {
-  return _IO_putc (__c, stdout);
+  return putc (__c, stdout);
 }
 
+#if defined __USE_MISC || defined __USE_POSIX
+__STDIO_INLINE int
+__putc_unlocked (int __c, FILE *__stream)
+{
+  unsigned char __cc = __c;
+  if (__glibc_unlikely (__stream->_IO_write_ptr >= __stream->_IO_write_end))
+    return __overflow (__stream, __cc);
+
+  *__stream->_IO_write_ptr++ = __cc;
+  return __cc;
+}
+#endif
 
 # ifdef __USE_MISC
 /* Faster version when locking is not necessary.  */
 __STDIO_INLINE int
 fputc_unlocked (int __c, FILE *__stream)
 {
-  return _IO_putc_unlocked (__c, __stream);
+  return __putc_unlocked (__c, __stream);
 }
 # endif /* misc */
 
@@ -97,21 +128,21 @@ fputc_unlocked (int __c, FILE *__stream)
 __STDIO_INLINE int
 putc_unlocked (int __c, FILE *__stream)
 {
-  return _IO_putc_unlocked (__c, __stream);
+  return __putc_unlocked (__c, __stream);
 }
 
 /* This is defined in POSIX.1:1996.  */
 __STDIO_INLINE int
 putchar_unlocked (int __c)
 {
-  return _IO_putc_unlocked (__c, stdout);
+  return __putc_unlocked (__c, stdout);
 }
 # endif	/* POSIX */
 
 
 # ifdef	__USE_GNU
 /* Like `getdelim', but reads up to a newline.  */
-__STDIO_INLINE _IO_ssize_t
+__STDIO_INLINE __ssize_t
 getline (char **__lineptr, size_t *__n, FILE *__stream)
 {
   return __getdelim (__lineptr, __n, '\n', __stream);
@@ -124,20 +155,17 @@ getline (char **__lineptr, size_t *__n, FILE *__stream)
 __STDIO_INLINE int
 __NTH (feof_unlocked (FILE *__stream))
 {
-  return _IO_feof_unlocked (__stream);
+  return (__stream->_flags & _IO_EOF_SEEN) != 0;
 }
 
 /* Faster versions when locking is not required.  */
 __STDIO_INLINE int
 __NTH (ferror_unlocked (FILE *__stream))
 {
-  return _IO_ferror_unlocked (__stream);
+  return (__stream->_flags & _IO_ERR_SEEN) != 0;
 }
 # endif /* misc */
 
-#endif /* Use extern inlines.  */
-
-
 #if defined __USE_MISC && defined __GNUC__ && defined __OPTIMIZE__ \
     && !defined __cplusplus
 /* Perform some simple optimizations.  */
@@ -151,7 +179,7 @@ __NTH (ferror_unlocked (FILE *__stream))
 		       for (__cnt = (size_t) (size) * (size_t) (n);	      \
 			    __cnt > 0; --__cnt)				      \
 			 {						      \
-			   int __c = _IO_getc_unlocked (__stream);	      \
+			   int __c = __getc_unlocked (__stream);	      \
 			   if (__c == EOF)				      \
 			     break;					      \
 			   *__ptr++ = __c;				      \
@@ -174,7 +202,7 @@ __NTH (ferror_unlocked (FILE *__stream))
 		       size_t __cnt;					      \
 		       for (__cnt = (size_t) (size) * (size_t) (n);	      \
 			    __cnt > 0; --__cnt)				      \
-			 if (_IO_putc_unlocked (*__ptr++, __stream) == EOF)   \
+			 if (__putc_unlocked (*__ptr++, __stream) == EOF)     \
 			   break;					      \
 		       ((size_t) (size) * (size_t) (n) - __cnt)		      \
 			/ (size_t) (size); })				      \
@@ -186,5 +214,6 @@ __NTH (ferror_unlocked (FILE *__stream))
 		     : fwrite_unlocked (ptr, size, n, stream))))
 #endif
 
-/* Define helper macro.  */
 #undef __STDIO_INLINE
+
+#endif /* bits/stdio.h */
diff --git a/libio/bits/stdio2.h b/libio/bits/stdio2.h
index e9f9d6952b..2d5cb5c03d 100644
--- a/libio/bits/stdio2.h
+++ b/libio/bits/stdio2.h
@@ -24,7 +24,7 @@ extern int __sprintf_chk (char *__restrict __s, int __flag, size_t __slen,
 			  const char *__restrict __format, ...) __THROW;
 extern int __vsprintf_chk (char *__restrict __s, int __flag, size_t __slen,
 			   const char *__restrict __format,
-			   _G_va_list __ap) __THROW;
+			   __gnuc_va_list __ap) __THROW;
 
 #ifdef __va_arg_pack
 __fortify_function int
@@ -41,7 +41,7 @@ __NTH (sprintf (char *__restrict __s, const char *__restrict __fmt, ...))
 
 __fortify_function int
 __NTH (vsprintf (char *__restrict __s, const char *__restrict __fmt,
-		 _G_va_list __ap))
+		 __gnuc_va_list __ap))
 {
   return __builtin___vsprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
 				   __bos (__s), __fmt, __ap);
@@ -54,7 +54,7 @@ extern int __snprintf_chk (char *__restrict __s, size_t __n, int __flag,
 			   ...) __THROW;
 extern int __vsnprintf_chk (char *__restrict __s, size_t __n, int __flag,
 			    size_t __slen, const char *__restrict __format,
-			    _G_va_list __ap) __THROW;
+			    __gnuc_va_list __ap) __THROW;
 
 # ifdef __va_arg_pack
 __fortify_function int
@@ -72,7 +72,7 @@ __NTH (snprintf (char *__restrict __s, size_t __n,
 
 __fortify_function int
 __NTH (vsnprintf (char *__restrict __s, size_t __n,
-		  const char *__restrict __fmt, _G_va_list __ap))
+		  const char *__restrict __fmt, __gnuc_va_list __ap))
 {
   return __builtin___vsnprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
 				    __bos (__s), __fmt, __ap);
@@ -86,9 +86,10 @@ extern int __fprintf_chk (FILE *__restrict __stream, int __flag,
 			  const char *__restrict __format, ...);
 extern int __printf_chk (int __flag, const char *__restrict __format, ...);
 extern int __vfprintf_chk (FILE *__restrict __stream, int __flag,
-			   const char *__restrict __format, _G_va_list __ap);
+			   const char *__restrict __format,
+			   __gnuc_va_list __ap);
 extern int __vprintf_chk (int __flag, const char *__restrict __format,
-			  _G_va_list __ap);
+			  __gnuc_va_list __ap);
 
 # ifdef __va_arg_pack
 __fortify_function int
@@ -111,7 +112,7 @@ printf (const char *__restrict __fmt, ...)
 # endif
 
 __fortify_function int
-vprintf (const char *__restrict __fmt, _G_va_list __ap)
+vprintf (const char *__restrict __fmt, __gnuc_va_list __ap)
 {
 #ifdef __USE_EXTERN_INLINES
   return __vfprintf_chk (stdout, __USE_FORTIFY_LEVEL - 1, __fmt, __ap);
@@ -122,7 +123,7 @@ vprintf (const char *__restrict __fmt, _G_va_list __ap)
 
 __fortify_function int
 vfprintf (FILE *__restrict __stream,
-	  const char *__restrict __fmt, _G_va_list __ap)
+	  const char *__restrict __fmt, __gnuc_va_list __ap)
 {
   return __vfprintf_chk (__stream, __USE_FORTIFY_LEVEL - 1, __fmt, __ap);
 }
@@ -131,7 +132,7 @@ vfprintf (FILE *__restrict __stream,
 extern int __dprintf_chk (int __fd, int __flag, const char *__restrict __fmt,
 			  ...) __attribute__ ((__format__ (__printf__, 3, 4)));
 extern int __vdprintf_chk (int __fd, int __flag,
-			   const char *__restrict __fmt, _G_va_list __arg)
+			   const char *__restrict __fmt, __gnuc_va_list __arg)
      __attribute__ ((__format__ (__printf__, 3, 0)));
 
 #  ifdef __va_arg_pack
@@ -147,7 +148,7 @@ dprintf (int __fd, const char *__restrict __fmt, ...)
 #  endif
 
 __fortify_function int
-vdprintf (int __fd, const char *__restrict __fmt, _G_va_list __ap)
+vdprintf (int __fd, const char *__restrict __fmt, __gnuc_va_list __ap)
 {
   return __vdprintf_chk (__fd, __USE_FORTIFY_LEVEL - 1, __fmt, __ap);
 }
@@ -159,7 +160,7 @@ extern int __asprintf_chk (char **__restrict __ptr, int __flag,
 			   const char *__restrict __fmt, ...)
      __THROW __attribute__ ((__format__ (__printf__, 3, 4))) __wur;
 extern int __vasprintf_chk (char **__restrict __ptr, int __flag,
-			    const char *__restrict __fmt, _G_va_list __arg)
+			    const char *__restrict __fmt, __gnuc_va_list __arg)
      __THROW __attribute__ ((__format__ (__printf__, 3, 0))) __wur;
 extern int __obstack_printf_chk (struct obstack *__restrict __obstack,
 				 int __flag, const char *__restrict __format,
@@ -168,7 +169,7 @@ extern int __obstack_printf_chk (struct obstack *__restrict __obstack,
 extern int __obstack_vprintf_chk (struct obstack *__restrict __obstack,
 				  int __flag,
 				  const char *__restrict __format,
-				  _G_va_list __args)
+				  __gnuc_va_list __args)
      __THROW __attribute__ ((__format__ (__printf__, 3, 0)));
 
 #  ifdef __va_arg_pack
@@ -205,14 +206,14 @@ __NTH (obstack_printf (struct obstack *__restrict __obstack,
 
 __fortify_function int
 __NTH (vasprintf (char **__restrict __ptr, const char *__restrict __fmt,
-		  _G_va_list __ap))
+		  __gnuc_va_list __ap))
 {
   return __vasprintf_chk (__ptr, __USE_FORTIFY_LEVEL - 1, __fmt, __ap);
 }
 
 __fortify_function int
 __NTH (obstack_vprintf (struct obstack *__restrict __obstack,
-			const char *__restrict __fmt, _G_va_list __ap))
+			const char *__restrict __fmt, __gnuc_va_list __ap))
 {
   return __obstack_vprintf_chk (__obstack, __USE_FORTIFY_LEVEL - 1, __fmt,
 				__ap);
@@ -368,7 +369,7 @@ fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n,
 
       for (; __cnt > 0; --__cnt)
 	{
-	  int __c = _IO_getc_unlocked (__stream);
+	  int __c = __getc_unlocked (__stream);
 	  if (__c == EOF)
 	    break;
 	  *__cptr++ = __c;
diff --git a/libio/bits/types/FILE.h b/libio/bits/types/FILE.h
index f268263209..de02a9d75b 100644
--- a/libio/bits/types/FILE.h
+++ b/libio/bits/types/FILE.h
@@ -1,9 +1,12 @@
 #ifndef __FILE_defined
 #define __FILE_defined 1
 
-struct _IO_FILE;
+/* Note: the struct tag is _IO_FILE rather than __FILE for historical
+   reasons.  It potentially appears in C++ mangled names and therefore
+   cannot be changed.  This file must be kept in sync with __FILE.h and
+   FILE_internals.h.  */
 
-/* The opaque type of streams.  This is the definition used elsewhere.  */
+struct _IO_FILE;
 typedef struct _IO_FILE FILE;
 
 #endif
diff --git a/libio/bits/types/FILE_internals.h b/libio/bits/types/FILE_internals.h
new file mode 100644
index 0000000000..5f88b4c6a1
--- /dev/null
+++ b/libio/bits/types/FILE_internals.h
@@ -0,0 +1,110 @@
+/* Internal structure of a FILE object.
+   Copyright (C) 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Per Bothner <bothner@cygnus.com>.
+
+   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/>.  */
+
+
+#ifndef _FILE_internals_defined
+#define _FILE_internals_defined 1
+
+/* This file exposes just enough of the internal structure of a FILE
+   object to permit the optimizations in bits/stdio.h.
+
+   Note: the _IO_ prefixes on struct tags and field names are for
+   historical reasons.  The GNU C Library no longer supports the
+   "libio" extension to stdio.
+
+   This file must be kept in sync with __FILE.h, FILE.h, and internal
+   headers.  */
+
+#include <bits/types.h>
+
+/* During the build of glibc itself, _IO_lock_t will already have been
+   defined by internal headers.  */
+#ifndef _IO_lock_t_defined
+typedef void _IO_lock_t;
+#define _IO_lock_t_defined 1
+#endif
+
+struct _IO_marker;
+struct _IO_codecvt;
+struct _IO_wide_data;
+
+struct _IO_FILE
+{
+  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
+  /* The following pointers correspond to the C++ streambuf protocol. */
+  char* _IO_read_ptr;	/* Current read pointer */
+  char* _IO_read_end;	/* End of get area. */
+  char* _IO_read_base;	/* Start of putback+get area. */
+  char* _IO_write_base;	/* Start of put area. */
+  char* _IO_write_ptr;	/* Current put pointer. */
+  char* _IO_write_end;	/* End of put area. */
+  char* _IO_buf_base;	/* Start of reserve area. */
+  char* _IO_buf_end;	/* End of reserve area. */
+  /* The following fields are used to support backing up and undo. */
+  char *_IO_save_base; /* Pointer to start of non-current get area. */
+  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
+  char *_IO_save_end; /* Pointer to end of non-current get area. */
+
+  struct _IO_marker *_markers;
+
+  struct _IO_FILE *_chain;
+
+  int _fileno;
+  int _flags2;
+  __off_t _old_offset; /* This used to be _offset but it's too small.  */
+
+  /* 1+column number of pbase(); 0 is unknown. */
+  unsigned short _cur_column;
+  signed char _vtable_offset;
+  char _shortbuf[1];
+  _IO_lock_t *_lock;
+
+  /* Fields below this point are not present in the "old" FILE structure.  */
+  __off64_t _offset;
+  struct _IO_codecvt *_codecvt;
+  struct _IO_wide_data *_wide_data;
+  struct _IO_FILE *_freeres_list;
+  void *_freeres_buf;
+  size_t __pad5;
+  int _mode;
+
+  /* Make sure we don't get into trouble again.  */
+  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
+};
+
+/* Many more flags are defined internally.  */
+#ifndef _IO_EOF_SEEN
+# define _IO_EOF_SEEN 0x10
+#elif _IO_EOF_SEEN != 0x10
+# error "FILE_internals.h out of sync with libio.h (_IO_EOF_SEEN)"
+#endif
+
+#ifndef _IO_ERR_SEEN
+# define _IO_ERR_SEEN 0x20
+#elif _IO_ERR_SEEN != 0x20
+# error "FILE_internals.h out of sync with libio.h (_IO_ERR_SEEN)"
+#endif
+
+#ifndef _IO_USER_LOCK
+# define _IO_USER_LOCK 0x8000
+#elif _IO_USER_LOCK != 0x8000
+# error "FILE_internals.h out of sync with libio.h (_IO_USER_LOCK)"
+#endif
+
+#endif
diff --git a/libio/bits/types/__FILE.h b/libio/bits/types/__FILE.h
index 06dd79bc83..5e8a9096f0 100644
--- a/libio/bits/types/__FILE.h
+++ b/libio/bits/types/__FILE.h
@@ -1,6 +1,11 @@
 #ifndef ____FILE_defined
 #define ____FILE_defined 1
 
+/* Note: the struct tag is _IO_FILE rather than __FILE for historical
+   reasons.  It potentially appears in C++ mangled names and therefore
+   cannot be changed.  This file must be kept in sync with FILE.h and
+   FILE_internals.h.  */
+
 struct _IO_FILE;
 typedef struct _IO_FILE __FILE;
 
diff --git a/libio/bits/types/__fpos_t.h b/libio/bits/types/__fpos_t.h
new file mode 100644
index 0000000000..032a8f12ba
--- /dev/null
+++ b/libio/bits/types/__fpos_t.h
@@ -0,0 +1,19 @@
+#ifndef __fpos_t_defined
+#define __fpos_t_defined 1
+
+#include <bits/types.h>
+#include <bits/types/__mbstate_t.h>
+
+typedef struct
+{
+  __off_t __pos;
+  __mbstate_t __state;
+} __fpos_t;
+
+typedef struct
+{
+  __off64_t __pos;
+  __mbstate_t __state;
+} __fpos64_t;
+
+#endif
diff --git a/libio/bits/types/cookie_io_functions_t.h b/libio/bits/types/cookie_io_functions_t.h
new file mode 100644
index 0000000000..c29a46e019
--- /dev/null
+++ b/libio/bits/types/cookie_io_functions_t.h
@@ -0,0 +1,61 @@
+/* Types for fopencookie.
+   Copyright (C) 1991-2017 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/>.  */
+
+#ifndef __cookie_io_functions_t_defined
+#define __cookie_io_functions_t_defined 1
+
+#include <bits/types.h>
+
+#define __need_size_t
+#include <stddef.h>
+
+/* Read NBYTES bytes from COOKIE into a buffer pointed to by BUF.
+   Return number of bytes read.  */
+typedef __ssize_t cookie_read_function_t (void *__cookie, char *__buf,
+					  size_t __nbytes);
+
+/* Write N bytes pointed to by BUF to COOKIE.  Write all N bytes
+   unless there is an error.  Return number of bytes written.  If
+   there is an error, return 0 and do not write anything.  If the file
+   has been opened for append (__mode.__append set), then set the file
+   pointer to the end of the file and then do the write; if not, just
+   write at the current file pointer.  */
+typedef __ssize_t cookie_write_function_t (void *__cookie, const char *__buf,
+					   size_t __n);
+
+/* Move COOKIE's file position to *POS bytes from the
+   beginning of the file (if W is SEEK_SET),
+   the current position (if W is SEEK_CUR),
+   or the end of the file (if W is SEEK_END).
+   Set *POS to the new file position.
+   Returns zero if successful, nonzero if not.  */
+typedef int cookie_seek_function_t (void *__cookie, __off64_t *__pos, int __w);
+
+/* Close COOKIE.  */
+typedef int cookie_close_function_t (void *__cookie);
+
+/* The structure with the cookie function pointers.  */
+typedef struct
+{
+  cookie_read_function_t *read;		/* Read bytes.  */
+  cookie_write_function_t *write;	/* Write bytes.  */
+  cookie_seek_function_t *seek;		/* Seek/tell file position.  */
+  cookie_close_function_t *close;	/* Close file.  */
+} cookie_io_functions_t;
+
+#endif /* cookie_io_functions_t.h */
diff --git a/libio/libio.h b/libio/libio.h
index 518ffd8e44..d7f9d41cbc 100644
--- a/libio/libio.h
+++ b/libio/libio.h
@@ -28,10 +28,54 @@
 #ifndef _IO_STDIO_H
 #define _IO_STDIO_H
 
+#include <features.h>
+
+#if !defined _LIBC || !defined __USE_GNU || defined _ISOMAC
+# error "libio.h is private to the stdio implementation"
+#endif
+
+/* Former configuration parameters for standalone libio.  Most of
+   these are fixed for the GNU C library.  */
+
+#include <bits/types.h>
+
+#define __need_size_t
+#define __need_wchar_t
+#define __need_NULL
+#include <stddef.h>
+
+#define __need___va_list
+#include <stdarg.h>
+
+#include <bits/types/__mbstate_t.h>
+#include <bits/types/__fpos_t.h>
+#include <bits/types/wint_t.h>
+
+#include <gconv.h>
+typedef union
+{
+  struct __gconv_info __cd;
+  struct
+  {
+    struct __gconv_info __cd;
+    struct __gconv_step_data __data;
+  } __combined;
+} _G_iconv_t;
+
+#define _G_HAVE_MMAP 1
+#define _G_IO_IO_FILE_VERSION 0x20001
+
+/* This is defined by <bits/stat.h> if `st_blksize' exists.  */
+#define _G_HAVE_ST_BLKSIZE defined (_STATBUF_ST_BLKSIZE)
+
+/* This must match the definition of BUFSIZ in stdio.h.  */
+#define _G_BUFSIZ 8192
+
+/* Allow ports to override the above if absolutely necessary.   */
 #include <_G_config.h>
-/* ALL of these should be defined in _G_config.h */
-#define _IO_fpos_t _G_fpos_t
-#define _IO_fpos64_t _G_fpos64_t
+
+#define _IO_fpos_t __fpos_t
+#define _IO_fpos64_t __fpos64_t
 #define _IO_size_t size_t
 #define _IO_ssize_t __ssize_t
 #define _IO_off_t __off_t
@@ -41,38 +85,17 @@
 #define _IO_iconv_t _G_iconv_t
 #define _IO_HAVE_ST_BLKSIZE _G_HAVE_ST_BLKSIZE
 #define _IO_BUFSIZ _G_BUFSIZ
-#define _IO_va_list _G_va_list
+#define _IO_va_list __gnuc_va_list
 #define _IO_wint_t wint_t
 
-/* This define avoids name pollution if we're using GNU stdarg.h */
-#define __need___va_list
-#include <stdarg.h>
-#ifdef __GNUC_VA_LIST
-# undef _IO_va_list
-# define _IO_va_list __gnuc_va_list
-#endif /* __GNUC_VA_LIST */
-
-#ifndef __P
-# include <sys/cdefs.h>
-#endif /*!__P*/
-
+/* Backward compatibility */
+#define _STDIO_USES_IOSTREAM 1
 #define _IO_UNIFIED_JUMPTABLES 1
+#define __HAVE_COLUMN 1
 
 #ifndef EOF
 # define EOF (-1)
 #endif
-#ifndef NULL
-# if defined __GNUG__ && \
-    (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
-#  define NULL (__null)
-# else
-#  if !defined(__cplusplus)
-#   define NULL ((void*)0)
-#  else
-#   define NULL (0)
-#  endif
-# endif
-#endif
 
 #define _IOS_INPUT	1
 #define _IOS_OUTPUT	2
@@ -111,15 +134,11 @@
 
 #define _IO_FLAGS2_MMAP 1
 #define _IO_FLAGS2_NOTCANCEL 2
-#ifdef _LIBC
-# define _IO_FLAGS2_FORTIFY 4
-#endif
+#define _IO_FLAGS2_FORTIFY 4
 #define _IO_FLAGS2_USER_WBUF 8
-#ifdef _LIBC
-# define _IO_FLAGS2_SCANF_STD 16
-# define _IO_FLAGS2_NOCLOSE 32
-# define _IO_FLAGS2_CLOEXEC 64
-#endif
+#define _IO_FLAGS2_SCANF_STD 16
+#define _IO_FLAGS2_NOCLOSE 32
+#define _IO_FLAGS2_CLOEXEC 64
 
 /* These are "formatting flags" matching the iostream fmtflags enum values. */
 #define _IO_SKIPWS 01
@@ -141,34 +160,67 @@
 #define _IO_BOOLALPHA 0200000
 
 
-struct _IO_jump_t;  struct _IO_FILE;
+struct _IO_jump_t;
+struct _IO_FILE;
+struct _IO_FILE_plus;
+typedef struct _IO_FILE _IO_FILE;
 
-/* During the build of glibc itself, _IO_lock_t will already have been
-   defined by internal headers.  */
-#ifndef _IO_lock_t_defined
-typedef void _IO_lock_t;
+#include <bits/types/__FILE.h>
+#include <bits/types/FILE.h>
+#include <bits/types/FILE_internals.h>
+#define _IO_file_flags _flags /* Compatibility. */
+
+#ifdef _IO_USE_OLD_IO_FILE
+/* This structure is a proper prefix of the _IO_FILE structure defined
+   in FILE_internals.h.  */
+struct _IO_FILE_old
+{
+  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
+
+  /* The following pointers correspond to the C++ streambuf protocol. */
+  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
+  char* _IO_read_ptr;	/* Current read pointer */
+  char* _IO_read_end;	/* End of get area. */
+  char* _IO_read_base;	/* Start of putback+get area. */
+  char* _IO_write_base;	/* Start of put area. */
+  char* _IO_write_ptr;	/* Current put pointer. */
+  char* _IO_write_end;	/* End of put area. */
+  char* _IO_buf_base;	/* Start of reserve area. */
+  char* _IO_buf_end;	/* End of reserve area. */
+  /* The following fields are used to support backing up and undo. */
+  char *_IO_save_base; /* Pointer to start of non-current get area. */
+  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
+  char *_IO_save_end; /* Pointer to end of non-current get area. */
+
+  struct _IO_marker *_markers;
+
+  struct _IO_FILE *_chain;
+
+  int _fileno;
+  int _flags2;
+  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
+
+  /* 1+column number of pbase(); 0 is unknown. */
+  unsigned short _cur_column;
+  signed char _vtable_offset;
+  char _shortbuf[1];
+
+  /*  char* _save_gptr;  char* _save_egptr; */
+
+  _IO_lock_t *_lock;
+};
 #endif
 
-
 /* A streammarker remembers a position in a buffer. */
 
-struct _IO_marker {
+struct _IO_marker
+{
   struct _IO_marker *_next;
   struct _IO_FILE *_sbuf;
   /* If _pos >= 0
  it points to _buf->Gbase()+_pos. FIXME comment */
   /* if _pos < 0, it points to _buf->eBptr()+_pos. FIXME comment */
   int _pos;
-#if 0
-    void set_streampos(streampos sp) { _spos = sp; }
-    void set_offset(int offset) { _pos = offset; _spos = (streampos)(-2); }
-  public:
-    streammarker(streambuf *sb);
-    ~streammarker();
-    int saving() { return  _spos == -2; }
-    int delta(streammarker&);
-    int delta();
-#endif
 };
 
 /* This is the structure from the libstdc++ codecvt class.  */
@@ -180,7 +232,6 @@ enum __codecvt_result
   __codecvt_noconv
 };
 
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
 /* The order of the elements in the following struct must match the order
    of the virtual functions in the libstdc++ codecvt class.  */
 struct _IO_codecvt
@@ -235,166 +286,40 @@ struct _IO_wide_data
 
   const struct _IO_jump_t *_wide_vtable;
 };
-#endif
 
-struct _IO_FILE {
-  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
-#define _IO_file_flags _flags
-
-  /* The following pointers correspond to the C++ streambuf protocol. */
-  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
-  char* _IO_read_ptr;	/* Current read pointer */
-  char* _IO_read_end;	/* End of get area. */
-  char* _IO_read_base;	/* Start of putback+get area. */
-  char* _IO_write_base;	/* Start of put area. */
-  char* _IO_write_ptr;	/* Current put pointer. */
-  char* _IO_write_end;	/* End of put area. */
-  char* _IO_buf_base;	/* Start of reserve area. */
-  char* _IO_buf_end;	/* End of reserve area. */
-  /* The following fields are used to support backing up and undo. */
-  char *_IO_save_base; /* Pointer to start of non-current get area. */
-  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
-  char *_IO_save_end; /* Pointer to end of non-current get area. */
-
-  struct _IO_marker *_markers;
-
-  struct _IO_FILE *_chain;
-
-  int _fileno;
-#if 0
-  int _blksize;
-#else
-  int _flags2;
-#endif
-  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
-
-#define __HAVE_COLUMN /* temporary */
-  /* 1+column number of pbase(); 0 is unknown. */
-  unsigned short _cur_column;
-  signed char _vtable_offset;
-  char _shortbuf[1];
-
-  /*  char* _save_gptr;  char* _save_egptr; */
-
-  _IO_lock_t *_lock;
-#ifdef _IO_USE_OLD_IO_FILE
-};
-
-struct _IO_FILE_complete
-{
-  struct _IO_FILE _file;
-#endif
-#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
-  _IO_off64_t _offset;
-# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-  /* Wide character stream stuff.  */
-  struct _IO_codecvt *_codecvt;
-  struct _IO_wide_data *_wide_data;
-  struct _IO_FILE *_freeres_list;
-  void *_freeres_buf;
-# else
-  void *__pad1;
-  void *__pad2;
-  void *__pad3;
-  void *__pad4;
-# endif
-  size_t __pad5;
-  int _mode;
-  /* Make sure we don't get into trouble again.  */
-  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
-#endif
-};
-
-#ifndef __cplusplus
-typedef struct _IO_FILE _IO_FILE;
-#endif
-
-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_;
-#ifndef _LIBC
-#define _IO_stdin ((_IO_FILE*)(&_IO_2_1_stdin_))
-#define _IO_stdout ((_IO_FILE*)(&_IO_2_1_stdout_))
-#define _IO_stderr ((_IO_FILE*)(&_IO_2_1_stderr_))
-#else
 extern _IO_FILE *_IO_stdin attribute_hidden;
 extern _IO_FILE *_IO_stdout attribute_hidden;
 extern _IO_FILE *_IO_stderr attribute_hidden;
-#endif
-
 
 /* Functions to do I/O and file management for a stream.  */
+#include <bits/types/cookie_io_functions_t.h>
 
-/* Read NBYTES bytes from COOKIE into a buffer pointed to by BUF.
-   Return number of bytes read.  */
-typedef __ssize_t __io_read_fn (void *__cookie, char *__buf, size_t __nbytes);
+/* Legacy internal names for cookie I/O types.  */
+typedef cookie_read_function_t __io_read_fn;
+typedef cookie_write_function_t __io_write_fn;
+typedef cookie_seek_function_t __io_seek_fn;
+typedef cookie_close_function_t __io_close_fn;
 
-/* Write N bytes pointed to by BUF to COOKIE.  Write all N bytes
-   unless there is an error.  Return number of bytes written.  If
-   there is an error, return 0 and do not write anything.  If the file
-   has been opened for append (__mode.__append set), then set the file
-   pointer to the end of the file and then do the write; if not, just
-   write at the current file pointer.  */
-typedef __ssize_t __io_write_fn (void *__cookie, const char *__buf,
-				 size_t __n);
-
-/* Move COOKIE's file position to *POS bytes from the
-   beginning of the file (if W is SEEK_SET),
-   the current position (if W is SEEK_CUR),
-   or the end of the file (if W is SEEK_END).
-   Set *POS to the new file position.
-   Returns zero if successful, nonzero if not.  */
-typedef int __io_seek_fn (void *__cookie, _IO_off64_t *__pos, int __w);
-
-/* Close COOKIE.  */
-typedef int __io_close_fn (void *__cookie);
-
-
-#ifdef __USE_GNU
-/* User-visible names for the above.  */
-typedef __io_read_fn cookie_read_function_t;
-typedef __io_write_fn cookie_write_function_t;
-typedef __io_seek_fn cookie_seek_function_t;
-typedef __io_close_fn cookie_close_function_t;
-
-/* The structure with the cookie function pointers.  */
-typedef struct
-{
-  __io_read_fn *read;		/* Read bytes.  */
-  __io_write_fn *write;		/* Write bytes.  */
-  __io_seek_fn *seek;		/* Seek/tell file position.  */
-  __io_close_fn *close;		/* Close file.  */
-} _IO_cookie_io_functions_t;
-typedef _IO_cookie_io_functions_t cookie_io_functions_t;
+typedef cookie_io_functions_t _IO_cookie_io_functions_t;
 
 struct _IO_cookie_file;
 
 /* Initialize one of those.  */
 extern void _IO_cookie_init (struct _IO_cookie_file *__cfile, int __read_write,
 			     void *__cookie, _IO_cookie_io_functions_t __fns);
-#endif
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
 
 extern int __underflow (_IO_FILE *);
 extern int __uflow (_IO_FILE *);
 extern int __overflow (_IO_FILE *, int);
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
 extern _IO_wint_t __wunderflow (_IO_FILE *);
 extern _IO_wint_t __wuflow (_IO_FILE *);
 extern _IO_wint_t __woverflow (_IO_FILE *, _IO_wint_t);
-#endif
 
-#if  __GNUC__ >= 3
-# define _IO_BE(expr, res) __builtin_expect ((expr), res)
-#else
-# define _IO_BE(expr, res) (expr)
-#endif
+#define _IO_BE(expr, res) __builtin_expect ((expr), res)
 
 #define _IO_getc_unlocked(_fp) \
        (_IO_BE ((_fp)->_IO_read_ptr >= (_fp)->_IO_read_end, 0) \
@@ -408,7 +333,6 @@ extern _IO_wint_t __woverflow (_IO_FILE *, _IO_wint_t);
     ? __overflow (_fp, (unsigned char) (_ch)) \
     : (unsigned char) (*(_fp)->_IO_write_ptr++ = (_ch)))
 
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
 # define _IO_getwc_unlocked(_fp) \
   (_IO_BE ((_fp)->_wide_data == NULL					\
 	   || ((_fp)->_wide_data->_IO_read_ptr				\
@@ -420,7 +344,6 @@ extern _IO_wint_t __woverflow (_IO_FILE *, _IO_wint_t);
 	       >= (_fp)->_wide_data->_IO_write_end), 0)			\
    ? __woverflow (_fp, _wch)						\
    : (_IO_wint_t) (*(_fp)->_wide_data->_IO_write_ptr++ = (_wch)))
-#endif
 
 #define _IO_feof_unlocked(__fp) (((__fp)->_flags & _IO_EOF_SEEN) != 0)
 #define _IO_ferror_unlocked(__fp) (((__fp)->_flags & _IO_ERR_SEEN) != 0)
@@ -463,28 +386,28 @@ extern _IO_off64_t _IO_seekpos (_IO_FILE *, _IO_off64_t, int);
 
 extern void _IO_free_backup_area (_IO_FILE *) __THROW;
 
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
 extern _IO_wint_t _IO_getwc (_IO_FILE *__fp);
 extern _IO_wint_t _IO_putwc (wchar_t __wc, _IO_FILE *__fp);
 extern int _IO_fwide (_IO_FILE *__fp, int __mode) __THROW;
-# if __GNUC__ >= 2
+
 /* While compiling glibc we have to handle compatibility with very old
    versions.  */
-#  if defined _LIBC && defined SHARED
-#   include <shlib-compat.h>
-#   if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
-#    define _IO_fwide_maybe_incompatible \
+#if defined SHARED
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
+#  define _IO_fwide_maybe_incompatible \
   (__builtin_expect (&_IO_stdin_used == NULL, 0))
 extern const int _IO_stdin_used;
 weak_extern (_IO_stdin_used);
-#   endif
-#  endif
-#  ifndef _IO_fwide_maybe_incompatible
-#   define _IO_fwide_maybe_incompatible (0)
-#  endif
+# endif
+#endif
+#ifndef _IO_fwide_maybe_incompatible
+# define _IO_fwide_maybe_incompatible (0)
+#endif
+
 /* A special optimized version of the function above.  It optimizes the
    case of initializing an unoriented byte stream.  */
-#  define _IO_fwide(__fp, __mode) \
+#define _IO_fwide(__fp, __mode) \
   ({ int __result = (__mode);						      \
      if (__result < 0 && ! _IO_fwide_maybe_incompatible)		      \
        {								      \
@@ -498,7 +421,6 @@ weak_extern (_IO_stdin_used);
      else								      \
        __result = _IO_fwide (__fp, __result);				      \
      __result; })
-# endif
 
 extern int _IO_vfwscanf (_IO_FILE * __restrict, const wchar_t * __restrict,
 			 _IO_va_list, int *__restrict);
@@ -506,14 +428,9 @@ extern int _IO_vfwprintf (_IO_FILE *__restrict, const wchar_t *__restrict,
 			  _IO_va_list);
 extern _IO_ssize_t _IO_wpadn (_IO_FILE *, wint_t, _IO_ssize_t);
 extern void _IO_free_wbackup_area (_IO_FILE *) __THROW;
-#endif
 
 #ifdef __LDBL_COMPAT
 # include <bits/libio-ldbl.h>
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* _IO_STDIO_H */
diff --git a/libio/libioP.h b/libio/libioP.h
index eb93418c8d..f537914a71 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -347,12 +347,9 @@ struct _IO_FILE_plus
 };
 
 #ifdef _IO_USE_OLD_IO_FILE
-/* This structure is used by the compatibility code as if it were an
-   _IO_FILE_plus, but has enough space to initialize the _mode argument
-   of an _IO_FILE_complete.  */
-struct _IO_FILE_complete_plus
+struct _IO_FILE_old_plus
 {
-  struct _IO_FILE_complete file;
+  struct _IO_FILE_old file;
   const struct _IO_jump_t *vtable;
 };
 #endif
diff --git a/libio/oldfileops.c b/libio/oldfileops.c
index 2eceefa68a..a61a1de87d 100644
--- a/libio/oldfileops.c
+++ b/libio/oldfileops.c
@@ -114,7 +114,7 @@ extern int errno;
 
 void
 attribute_compat_text_section
-_IO_old_file_init_internal (struct _IO_FILE_plus *fp)
+_IO_old_file_init_internal (struct _IO_FILE_old_plus *fp)
 {
   /* POSIX.1 allows another file handle to be used to change the position
      of our file descriptor.  Hence we actually don't know the actual
@@ -123,24 +123,24 @@ _IO_old_file_init_internal (struct _IO_FILE_plus *fp)
   fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS;
 
   _IO_link_in (fp);
-  fp->file._vtable_offset = ((int) sizeof (struct _IO_FILE)
-			     - (int) sizeof (struct _IO_FILE_complete));
+  fp->file._vtable_offset = ((int) sizeof (struct _IO_FILE_old)
+			     - (int) sizeof (struct _IO_FILE));
   fp->file._fileno = -1;
 
 #if defined SHARED && defined _LIBC
   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))
+      || (fp != (struct _IO_FILE_old_plus *) _IO_stdin
+	  && fp != (struct _IO_FILE_old_plus *) _IO_stdout
+	  && fp != (struct _IO_FILE_old_plus *) _IO_stderr))
     /* The object is dynamically allocated and large enough.  Initialize
        the _mode element as well.  */
-    ((struct _IO_FILE_complete *) fp)->_mode = -1;
+    ((struct _IO_FILE *) fp)->_mode = -1;
 #endif
 }
 
 void
 attribute_compat_text_section
-_IO_old_file_init (struct _IO_FILE_plus *fp)
+_IO_old_file_init (struct _IO_FILE_old_plus *fp)
 {
   IO_set_accept_foreign_vtables (&_IO_vtable_check);
   _IO_old_file_init_internal (fp);
@@ -148,7 +148,7 @@ _IO_old_file_init (struct _IO_FILE_plus *fp)
 
 int
 attribute_compat_text_section
-_IO_old_file_close_it (_IO_FILE *fp)
+_IO_old_file_close_it (_IO_FILE_old *fp)
 {
   int write_status, close_status;
   if (!_IO_file_is_open (fp))
@@ -166,7 +166,7 @@ _IO_old_file_close_it (_IO_FILE *fp)
   _IO_setg (fp, NULL, NULL, NULL);
   _IO_setp (fp, NULL, NULL);
 
-  _IO_un_link ((struct _IO_FILE_plus *) fp);
+  _IO_un_link ((struct _IO_FILE_old_plus *) fp);
   fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
   fp->_fileno = -1;
   fp->_old_offset = _IO_pos_BAD;
@@ -176,7 +176,7 @@ _IO_old_file_close_it (_IO_FILE *fp)
 
 void
 attribute_compat_text_section
-_IO_old_file_finish (_IO_FILE *fp, int dummy)
+_IO_old_file_finish (_IO_FILE_old *fp, int dummy)
 {
   if (_IO_file_is_open (fp))
     {
@@ -187,9 +187,9 @@ _IO_old_file_finish (_IO_FILE *fp, int dummy)
   _IO_default_finish (fp, 0);
 }
 
-_IO_FILE *
+_IO_FILE_old *
 attribute_compat_text_section
-_IO_old_file_fopen (_IO_FILE *fp, const char *filename, const char *mode)
+_IO_old_file_fopen (_IO_FILE_old *fp, const char *filename, const char *mode)
 {
   int oflags = 0, omode;
   int read_write, fdesc;
@@ -230,13 +230,13 @@ _IO_old_file_fopen (_IO_FILE *fp, const char *filename, const char *mode)
     if (_IO_SEEKOFF (fp, (_IO_off_t)0, _IO_seek_end, _IOS_INPUT|_IOS_OUTPUT)
 	== _IO_pos_BAD && errno != ESPIPE)
       return NULL;
-  _IO_link_in ((struct _IO_FILE_plus *) fp);
+  _IO_link_in ((struct _IO_FILE_old_plus *) fp);
   return fp;
 }
 
-_IO_FILE *
+_IO_FILE_old *
 attribute_compat_text_section
-_IO_old_file_attach (_IO_FILE *fp, int fd)
+_IO_old_file_attach (_IO_FILE_old *fp, int fd)
 {
   if (_IO_file_is_open (fp))
     return NULL;
@@ -252,9 +252,9 @@ _IO_old_file_attach (_IO_FILE *fp, int fd)
   return fp;
 }
 
-_IO_FILE *
+_IO_FILE_old *
 attribute_compat_text_section
-_IO_old_file_setbuf (_IO_FILE *fp, char *p, _IO_ssize_t len)
+_IO_old_file_setbuf (_IO_FILE_old *fp, char *p, _IO_ssize_t len)
 {
     if (_IO_default_setbuf (fp, p, len) == NULL)
       return NULL;
@@ -266,14 +266,14 @@ _IO_old_file_setbuf (_IO_FILE *fp, char *p, _IO_ssize_t len)
     return fp;
 }
 
-static int old_do_write (_IO_FILE *, const char *, _IO_size_t);
+static int old_do_write (_IO_FILE_old *, const char *, _IO_size_t);
 
 /* Write TO_DO bytes from DATA to FP.
    Then mark FP as having empty buffers. */
 
 int
 attribute_compat_text_section
-_IO_old_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
+_IO_old_do_write (_IO_FILE_old *fp, const char *data, _IO_size_t to_do)
 {
   return (to_do == 0 || (_IO_size_t) old_do_write (fp, data, to_do) == to_do)
 	 ? 0 : EOF;
@@ -281,7 +281,7 @@ _IO_old_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
 
 static int
 attribute_compat_text_section
-old_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
+old_do_write (_IO_FILE_old *fp, const char *data, _IO_size_t to_do)
 {
   _IO_size_t count;
   if (fp->_flags & _IO_IS_APPENDING)
@@ -311,7 +311,7 @@ old_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
 
 int
 attribute_compat_text_section
-_IO_old_file_underflow (_IO_FILE *fp)
+_IO_old_file_underflow (_IO_FILE_old *fp)
 {
   _IO_ssize_t count;
 #if 0
@@ -375,7 +375,7 @@ _IO_old_file_underflow (_IO_FILE *fp)
 
 int
 attribute_compat_text_section
-_IO_old_file_overflow (_IO_FILE *f, int ch)
+_IO_old_file_overflow (_IO_FILE_old *f, int ch)
 {
   if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
     {
@@ -425,7 +425,7 @@ _IO_old_file_overflow (_IO_FILE *f, int ch)
 
 int
 attribute_compat_text_section
-_IO_old_file_sync (_IO_FILE *fp)
+_IO_old_file_sync (_IO_FILE_old *fp)
 {
   _IO_ssize_t delta;
   int retval = 0;
@@ -459,7 +459,7 @@ _IO_old_file_sync (_IO_FILE *fp)
 
 _IO_off64_t
 attribute_compat_text_section
-_IO_old_file_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
+_IO_old_file_seekoff (_IO_FILE_old *fp, _IO_off64_t offset, int dir, int mode)
 {
   _IO_off_t result;
   _IO_off64_t delta, new_offset;
@@ -642,7 +642,7 @@ resync:
 
 _IO_ssize_t
 attribute_compat_text_section
-_IO_old_file_write (_IO_FILE *f, const void *data, _IO_ssize_t n)
+_IO_old_file_write (_IO_FILE_old *f, const void *data, _IO_ssize_t n)
 {
   _IO_ssize_t to_do = n;
   while (to_do > 0)
@@ -664,7 +664,7 @@ _IO_old_file_write (_IO_FILE *f, const void *data, _IO_ssize_t n)
 
 _IO_size_t
 attribute_compat_text_section
-_IO_old_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
+_IO_old_file_xsputn (_IO_FILE_old *f, const void *data, _IO_size_t n)
 {
   const char *s = (char *) data;
   _IO_size_t to_do = n;
diff --git a/libio/stdio.h b/libio/stdio.h
index 47490c8299..7019c1d28f 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -32,18 +32,22 @@ __BEGIN_DECLS
 #define __need_NULL
 #include <stddef.h>
 
+#define __need___va_list
+#include <stdarg.h>
+
 #include <bits/types.h>
 #include <bits/types/__FILE.h>
 #include <bits/types/FILE.h>
+#include <bits/types/__fpos_t.h>
 
-#define _STDIO_USES_IOSTREAM
-
-#include <libio.h>
+#ifdef __USE_GNU
+# include <bits/types/cookie_io_functions_t.h>
+#endif
 
 #if defined __USE_XOPEN || defined __USE_XOPEN2K8
 # ifdef __GNUC__
 #  ifndef _VA_LIST_DEFINED
-typedef _G_va_list va_list;
+typedef __gnuc_va_list va_list;
 #   define _VA_LIST_DEFINED
 #  endif
 # else
@@ -75,12 +79,12 @@ typedef __ssize_t ssize_t;
 
 /* The type of the second argument to `fgetpos' and `fsetpos'.  */
 #ifndef __USE_FILE_OFFSET64
-typedef _G_fpos_t fpos_t;
+typedef __fpos_t fpos_t;
 #else
-typedef _G_fpos64_t fpos_t;
+typedef __fpos64_t fpos_t;
 #endif
 #ifdef __USE_LARGEFILE64
-typedef _G_fpos64_t fpos64_t;
+typedef __fpos64_t fpos64_t;
 #endif
 
 /* The possibilities for the third argument to `setvbuf'.  */
@@ -91,7 +95,7 @@ typedef _G_fpos64_t fpos64_t;
 
 /* Default buffer size.  */
 #ifndef BUFSIZ
-# define BUFSIZ _IO_BUFSIZ
+# define BUFSIZ 8192
 #endif
 
 
@@ -132,9 +136,9 @@ typedef _G_fpos64_t fpos64_t;
 
 
 /* Standard streams.  */
-extern struct _IO_FILE *stdin;		/* Standard input stream.  */
-extern struct _IO_FILE *stdout;		/* Standard output stream.  */
-extern struct _IO_FILE *stderr;		/* Standard error output stream.  */
+extern FILE *stdin;		/* Standard input stream.  */
+extern FILE *stdout;		/* Standard output stream.  */
+extern FILE *stderr;		/* Standard error output stream.  */
 /* C89/C99 say they're macros.  Make them happy.  */
 #define stdin stdin
 #define stdout stdout
@@ -270,7 +274,7 @@ extern FILE *fdopen (int __fd, const char *__modes) __THROW __wur;
    and uses the given functions for input and output.  */
 extern FILE *fopencookie (void *__restrict __magic_cookie,
 			  const char *__restrict __modes,
-			  _IO_cookie_io_functions_t __io_funcs) __THROW __wur;
+			  cookie_io_functions_t __io_funcs) __THROW __wur;
 #endif
 
 #if defined __USE_XOPEN2K8 || __GLIBC_USE (LIB_EXT2)
@@ -325,15 +329,15 @@ extern int sprintf (char *__restrict __s,
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
 extern int vfprintf (FILE *__restrict __s, const char *__restrict __format,
-		     _G_va_list __arg);
+		     __gnuc_va_list __arg);
 /* Write formatted output to stdout from argument list ARG.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
-extern int vprintf (const char *__restrict __format, _G_va_list __arg);
+extern int vprintf (const char *__restrict __format, __gnuc_va_list __arg);
 /* Write formatted output to S from argument list ARG.  */
 extern int vsprintf (char *__restrict __s, const char *__restrict __format,
-		     _G_va_list __arg) __THROWNL;
+		     __gnuc_va_list __arg) __THROWNL;
 
 #if defined __USE_ISOC99 || defined __USE_UNIX98
 /* Maximum chars of output to write in MAXLEN.  */
@@ -342,7 +346,7 @@ extern int snprintf (char *__restrict __s, size_t __maxlen,
      __THROWNL __attribute__ ((__format__ (__printf__, 3, 4)));
 
 extern int vsnprintf (char *__restrict __s, size_t __maxlen,
-		      const char *__restrict __format, _G_va_list __arg)
+		      const char *__restrict __format, __gnuc_va_list __arg)
      __THROWNL __attribute__ ((__format__ (__printf__, 3, 0)));
 #endif
 
@@ -350,7 +354,7 @@ extern int vsnprintf (char *__restrict __s, size_t __maxlen,
 /* Write formatted output to a string dynamically allocated with `malloc'.
    Store the address of the string in *PTR.  */
 extern int vasprintf (char **__restrict __ptr, const char *__restrict __f,
-		      _G_va_list __arg)
+		      __gnuc_va_list __arg)
      __THROWNL __attribute__ ((__format__ (__printf__, 2, 0))) __wur;
 extern int __asprintf (char **__restrict __ptr,
 		       const char *__restrict __fmt, ...)
@@ -363,7 +367,7 @@ extern int asprintf (char **__restrict __ptr,
 #ifdef __USE_XOPEN2K8
 /* Write formatted output to a file descriptor.  */
 extern int vdprintf (int __fd, const char *__restrict __fmt,
-		     _G_va_list __arg)
+		     __gnuc_va_list __arg)
      __attribute__ ((__format__ (__printf__, 2, 0)));
 extern int dprintf (int __fd, const char *__restrict __fmt, ...)
      __attribute__ ((__format__ (__printf__, 2, 3)));
@@ -418,19 +422,19 @@ extern int __isoc99_sscanf (const char *__restrict __s,
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
 extern int vfscanf (FILE *__restrict __s, const char *__restrict __format,
-		    _G_va_list __arg)
+		    __gnuc_va_list __arg)
      __attribute__ ((__format__ (__scanf__, 2, 0))) __wur;
 
 /* Read formatted input from stdin into argument list ARG.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
-extern int vscanf (const char *__restrict __format, _G_va_list __arg)
+extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg)
      __attribute__ ((__format__ (__scanf__, 1, 0))) __wur;
 
 /* Read formatted input from S into argument list ARG.  */
 extern int vsscanf (const char *__restrict __s,
-		    const char *__restrict __format, _G_va_list __arg)
+		    const char *__restrict __format, __gnuc_va_list __arg)
      __THROW __attribute__ ((__format__ (__scanf__, 2, 0)));
 
 # if !defined __USE_GNU \
@@ -442,26 +446,26 @@ extern int vsscanf (const char *__restrict __s,
    s, S or [.  */
 extern int __REDIRECT (vfscanf,
 		       (FILE *__restrict __s,
-			const char *__restrict __format, _G_va_list __arg),
+			const char *__restrict __format, __gnuc_va_list __arg),
 		       __isoc99_vfscanf)
      __attribute__ ((__format__ (__scanf__, 2, 0))) __wur;
 extern int __REDIRECT (vscanf, (const char *__restrict __format,
-				_G_va_list __arg), __isoc99_vscanf)
+				__gnuc_va_list __arg), __isoc99_vscanf)
      __attribute__ ((__format__ (__scanf__, 1, 0))) __wur;
 extern int __REDIRECT_NTH (vsscanf,
 			   (const char *__restrict __s,
 			    const char *__restrict __format,
-			    _G_va_list __arg), __isoc99_vsscanf)
+			    __gnuc_va_list __arg), __isoc99_vsscanf)
      __attribute__ ((__format__ (__scanf__, 2, 0)));
 #  else
 extern int __isoc99_vfscanf (FILE *__restrict __s,
 			     const char *__restrict __format,
-			     _G_va_list __arg) __wur;
+			     __gnuc_va_list __arg) __wur;
 extern int __isoc99_vscanf (const char *__restrict __format,
-			    _G_va_list __arg) __wur;
+			    __gnuc_va_list __arg) __wur;
 extern int __isoc99_vsscanf (const char *__restrict __s,
 			     const char *__restrict __format,
-			     _G_va_list __arg) __THROW;
+			     __gnuc_va_list __arg) __THROW;
 #   define vfscanf __isoc99_vfscanf
 #   define vscanf __isoc99_vscanf
 #   define vsscanf __isoc99_vsscanf
@@ -483,10 +487,6 @@ extern int getc (FILE *__stream);
    marked with __THROW.  */
 extern int getchar (void);
 
-/* The C standard explicitly says this is a macro, so we always do the
-   optimization for it.  */
-#define getc(_fp) _IO_getc (_fp)
-
 #ifdef __USE_POSIX199506
 /* These are defined in POSIX.1:1996.
 
@@ -523,10 +523,6 @@ extern int putc (int __c, FILE *__stream);
    marked with __THROW.  */
 extern int putchar (int __c);
 
-/* The C standard explicitly says this can be a macro,
-   so we always do the optimization for it.  */
-#define putc(_ch, _fp) _IO_putc (_ch, _fp)
-
 #ifdef __USE_MISC
 /* Faster version when locking is not necessary.
 
@@ -600,12 +596,12 @@ extern char *fgets_unlocked (char *__restrict __s, int __n,
    cancellation point.  But due to similarity with an POSIX interface
    or due to the implementation they are cancellation points and
    therefore not marked with __THROW.  */
-extern _IO_ssize_t __getdelim (char **__restrict __lineptr,
-			       size_t *__restrict __n, int __delimiter,
-			       FILE *__restrict __stream) __wur;
-extern _IO_ssize_t getdelim (char **__restrict __lineptr,
+extern __ssize_t __getdelim (char **__restrict __lineptr,
 			     size_t *__restrict __n, int __delimiter,
 			     FILE *__restrict __stream) __wur;
+extern __ssize_t getdelim (char **__restrict __lineptr,
+			   size_t *__restrict __n, int __delimiter,
+			   FILE *__restrict __stream) __wur;
 
 /* Like `getdelim', but reads up to a newline.
 
@@ -613,9 +609,9 @@ extern _IO_ssize_t getdelim (char **__restrict __lineptr,
    cancellation point.  But due to similarity with an POSIX interface
    or due to the implementation it is a cancellation point and
    therefore not marked with __THROW.  */
-extern _IO_ssize_t getline (char **__restrict __lineptr,
-			    size_t *__restrict __n,
-			    FILE *__restrict __stream) __wur;
+extern __ssize_t getline (char **__restrict __lineptr,
+			  size_t *__restrict __n,
+			  FILE *__restrict __stream) __wur;
 #endif
 
 
@@ -828,7 +824,7 @@ extern int obstack_printf (struct obstack *__restrict __obstack,
      __THROWNL __attribute__ ((__format__ (__printf__, 2, 3)));
 extern int obstack_vprintf (struct obstack *__restrict __obstack,
 			    const char *__restrict __format,
-			    _G_va_list __args)
+			    __gnuc_va_list __args)
      __THROWNL __attribute__ ((__format__ (__printf__, 2, 0)));
 #endif /* Use GNU.  */
 
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 068ffc1684..06c0054709 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -222,6 +222,7 @@
 
 #include <unistd.h>
 #include <stdio.h>    /* needed for malloc_stats */
+#include <libio.h>    /* needed for FILE internals in malloc_stats */
 #include <errno.h>
 
 #include <shlib-compat.h>
diff --git a/misc/err.c b/misc/err.c
index 3da4e9d6b0..7b4b7ef814 100644
--- a/misc/err.c
+++ b/misc/err.c
@@ -22,10 +22,8 @@
 #include <errno.h>
 #include <string.h>
 #include <stdio.h>
-
+#include <libio.h>
 #include <wchar.h>
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
 
 extern char *__progname;
 
diff --git a/misc/getpass.c b/misc/getpass.c
index 89c783fe20..9d84a7f9cd 100644
--- a/misc/getpass.c
+++ b/misc/getpass.c
@@ -22,8 +22,6 @@
 #include <unistd.h>
 
 #include <wchar.h>
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
 #include <libc-lock.h>
 
 /* It is desirable to use this bit on systems that have it.
diff --git a/misc/getttyent.c b/misc/getttyent.c
index 73002f52d1..64a93e9003 100644
--- a/misc/getttyent.c
+++ b/misc/getttyent.c
@@ -37,9 +37,6 @@ static char sccsid[] = "@(#)getttyent.c	8.1 (Berkeley) 6/4/93";
 #include <ctype.h>
 #include <string.h>
 
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
-
 static char zapchar;
 static FILE *tf;
 
diff --git a/misc/mntent_r.c b/misc/mntent_r.c
index 30f55212be..02319d152b 100644
--- a/misc/mntent_r.c
+++ b/misc/mntent_r.c
@@ -23,9 +23,6 @@
 #include <string.h>
 #include <sys/types.h>
 
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
-
 #undef __setmntent
 #undef __endmntent
 #undef __getmntent_r
diff --git a/posix/getopt.c b/posix/getopt.c
index 543c8e7284..2efdea85dd 100644
--- a/posix/getopt.c
+++ b/posix/getopt.c
@@ -29,17 +29,13 @@
 #include <unistd.h>
 
 #ifdef _LIBC
-/* When used as part of glibc, error printing must be done differently
-   for standards compliance.  getopt is not a cancellation point, so
-   it must not call functions that are, and it is specified by an
-   older standard than stdio locking, so it must not refer to
-   functions in the "user namespace" related to stdio locking.
-   Finally, it must use glibc's internal message translation so that
-   the messages are looked up in the proper text domain.  */
+/* When used as part of glibc, error printing must be done
+   differently: getopt is not a cancellation point, so it must not
+   call functions that are, and it must use glibc's internal message
+   translation so that the messages are looked up in the proper text
+   domain.  */
 # include <libintl.h>
 # define fprintf __fxprintf_nocancel
-# define flockfile(fp) _IO_flockfile (fp)
-# define funlockfile(fp) _IO_funlockfile (fp)
 #else
 # include "gettext.h"
 # define _(msgid) gettext (msgid)
diff --git a/pwd/fgetpwent_r.c b/pwd/fgetpwent_r.c
index 8aa8f69372..044f19b20b 100644
--- a/pwd/fgetpwent_r.c
+++ b/pwd/fgetpwent_r.c
@@ -20,9 +20,6 @@
 #include <stdio.h>
 #include <pwd.h>
 
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
-
 /* Define a line parsing function using the common code
    used in the nss_files module.  */
 
diff --git a/shadow/fgetspent_r.c b/shadow/fgetspent_r.c
index 42106302c1..260f59bbc5 100644
--- a/shadow/fgetspent_r.c
+++ b/shadow/fgetspent_r.c
@@ -20,8 +20,6 @@
 #include <shadow.h>
 #include <stdio.h>
 
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
 
 /* Define a line parsing function using the common code
    used in the nss_files module.  */
diff --git a/shadow/putspent.c b/shadow/putspent.c
index 5e27340b5d..6dad45f401 100644
--- a/shadow/putspent.c
+++ b/shadow/putspent.c
@@ -20,8 +20,6 @@
 #include <stdio.h>
 #include <shadow.h>
 
-#define flockfile(s) _IO_flockfile (s)
-#define funlockfile(s) _IO_funlockfile (s)
 
 #define _S(x)	x ? x : ""
 
diff --git a/stdio-common/tstgetln.c b/stdio-common/tstgetln.c
index 79ab90c7cc..3b7ecefc0a 100644
--- a/stdio-common/tstgetln.c
+++ b/stdio-common/tstgetln.c
@@ -15,9 +15,8 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <sys/types.h>
 #include <stdio.h>
-#undef ssize_t
-#define ssize_t _IO_ssize_t
 
 int
 main (int argc, char *argv[])
diff --git a/sysdeps/generic/_G_config.h b/sysdeps/generic/_G_config.h
index c49eed395b..d98cb3824d 100644
--- a/sysdeps/generic/_G_config.h
+++ b/sysdeps/generic/_G_config.h
@@ -1,58 +1,8 @@
-/* This file is needed by libio to define various configuration parameters.
-   These are always the same in the GNU C library.  */
+/* Configuration parameter overrides for libio - generic version.  */
 
 #ifndef _G_config_h
 #define _G_config_h 1
 
-/* Define types for libio in terms of the standard internal type names.  */
-
-#include <bits/types.h>
-#define __need_size_t
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-# define __need_wchar_t
-#endif
-#define __need_NULL
-#include <stddef.h>
-
-#include <bits/types/__mbstate_t.h>
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-# include <bits/types/wint_t.h>
-#endif
-
-typedef struct
-{
-  __off_t __pos;
-  __mbstate_t __state;
-} _G_fpos_t;
-typedef struct
-{
-  __off64_t __pos;
-  __mbstate_t __state;
-} _G_fpos64_t;
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-# include <gconv.h>
-typedef union
-{
-  struct __gconv_info __cd;
-  struct
-  {
-    struct __gconv_info __cd;
-    struct __gconv_step_data __data;
-  } __combined;
-} _G_iconv_t;
-#endif
-
-
-/* These library features are always available in the GNU C library.  */
-#define _G_va_list __gnuc_va_list
-
-#define _G_HAVE_MMAP 1
-
-#define _G_IO_IO_FILE_VERSION 0x20001
-
-/* This is defined by <bits/stat.h> if `st_blksize' exists.  */
-#define _G_HAVE_ST_BLKSIZE defined (_STATBUF_ST_BLKSIZE)
-
-#define _G_BUFSIZ 8192
+#undef _G_HAVE_MREMAP
 
 #endif	/* _G_config.h */
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.h b/sysdeps/ieee754/ldbl-opt/nldbl-compat.h
index 72ec0db390..a4f3c37d63 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.h
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.h
@@ -82,22 +82,23 @@ extern ssize_t __nldbl___vstrfmon (char *, size_t, const char *, va_list)
 /* These don't use __typeof because they were not declared by the headers,
    since we don't compile with _FORTIFY_SOURCE.  */
 extern int __nldbl___vfprintf_chk (FILE *__restrict, int,
-				   const char *__restrict, _G_va_list);
+				   const char *__restrict, __gnuc_va_list);
 extern int __nldbl___vfwprintf_chk (FILE *__restrict, int,
 				    const wchar_t *__restrict, __gnuc_va_list);
 extern int __nldbl___vsprintf_chk (char *__restrict, int, size_t,
-				   const char *__restrict, _G_va_list) __THROW;
+				   const char *__restrict, __gnuc_va_list)
+  __THROW;
 extern int __nldbl___vsnprintf_chk (char *__restrict, size_t, int, size_t,
-				    const char *__restrict, _G_va_list)
+				    const char *__restrict, __gnuc_va_list)
   __THROW;
 extern int __nldbl___vswprintf_chk (wchar_t *__restrict, size_t, int, size_t,
 				    const wchar_t *__restrict, __gnuc_va_list)
   __THROW;
-extern int __nldbl___vasprintf_chk (char **, int, const char *, _G_va_list)
+extern int __nldbl___vasprintf_chk (char **, int, const char *, __gnuc_va_list)
   __THROW;
-extern int __nldbl___vdprintf_chk (int, int, const char *, _G_va_list);
+extern int __nldbl___vdprintf_chk (int, int, const char *, __gnuc_va_list);
 extern int __nldbl___obstack_vprintf_chk (struct obstack *, int, const char *,
-					  _G_va_list) __THROW;
+					  __gnuc_va_list) __THROW;
 extern void __nldbl___vsyslog_chk (int, int, const char *, va_list);
 
 
diff --git a/sysdeps/unix/sysv/linux/_G_config.h b/sysdeps/unix/sysv/linux/_G_config.h
index 3bc6cfd595..69d5f4694f 100644
--- a/sysdeps/unix/sysv/linux/_G_config.h
+++ b/sysdeps/unix/sysv/linux/_G_config.h
@@ -1,59 +1,8 @@
-/* This file is needed by libio to define various configuration parameters.
-   These are always the same in the GNU C library.  */
+/* Configuration parameter overrides for libio - Linux version.  */
 
 #ifndef _G_config_h
 #define _G_config_h 1
 
-/* Define types for libio in terms of the standard internal type names.  */
-
-#include <bits/types.h>
-#define __need_size_t
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-# define __need_wchar_t
-#endif
-#define __need_NULL
-#include <stddef.h>
-
-#include <bits/types/__mbstate_t.h>
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-# include <bits/types/wint_t.h>
-#endif
-
-typedef struct
-{
-  __off_t __pos;
-  __mbstate_t __state;
-} _G_fpos_t;
-typedef struct
-{
-  __off64_t __pos;
-  __mbstate_t __state;
-} _G_fpos64_t;
-#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
-# include <gconv.h>
-typedef union
-{
-  struct __gconv_info __cd;
-  struct
-  {
-    struct __gconv_info __cd;
-    struct __gconv_step_data __data;
-  } __combined;
-} _G_iconv_t;
-#endif
-
-
-/* These library features are always available in the GNU C library.  */
-#define _G_va_list __gnuc_va_list
-
-#define _G_HAVE_MMAP 1
 #define _G_HAVE_MREMAP 1
 
-#define _G_IO_IO_FILE_VERSION 0x20001
-
-/* This is defined by <bits/stat.h> if `st_blksize' exists.  */
-#define _G_HAVE_ST_BLKSIZE defined (_STATBUF_ST_BLKSIZE)
-
-#define _G_BUFSIZ 8192
-
 #endif	/* _G_config.h */
-- 
2.11.0


diff mbox

Patch

diff --git a/ChangeLog b/ChangeLog
index 205da06..28d9012 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@ 
+2016-08-05  Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+	[BZ #18241]
+	[BZ #20181]
+	* libio/Makefile (test): Add tst-memstream3 and tst-wmemstream3.
+	* libio/memstream.c (_IO_mem_sync): Only append a null byte if
+	write position is at the end the buffer.
+	* libio/wmemstream.c (_IO_wmem_sync): Likewise.
+	* libio/strops.c (_IO_str_switch_to_get_mode): New function.
+	(_IO_str_seekoff): Set correct offset from negative displacement and
+	set EINVAL for invalid ones.
+	* libio/wstrops.c (enlarge_userbuf): Use correct function to calculate
+	buffer length.
+	(_IO_wstr_switch_to_get_mode): New function.
+	(_IO_wstr_seekoff): Set correct offset from negative displacement and
+	set EINVAL for invalid ones.
+	* libio/tst-memstream3.c: New file.
+	* libio/tst-wmemstream3.c: Likewise.
+	* manual/examples/memstrm.c: Remove warning when priting size_t.
+
 2016-08-05  Torvald Riegel  <triegel@redhat.com>
 
 	* include/atomic.h (atomic_exchange_relaxed): New.
diff --git a/libio/Makefile b/libio/Makefile
index 12589f2..0c7751c 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -56,8 +56,8 @@  tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst-mmap-eofsync tst-mmap-fflushsync bug-mmap-fflush \
 	tst-mmap2-eofsync tst-mmap-offend bug-fopena+ bug-wfflush \
 	bug-ungetc2 bug-ftell bug-ungetc3 bug-ungetc4 tst-fopenloc2 \
-	tst-memstream1 tst-memstream2 \
-	tst-wmemstream1 tst-wmemstream2 \
+	tst-memstream1 tst-memstream2 tst-memstream3 \
+	tst-wmemstream1 tst-wmemstream2 tst-wmemstream3 \
 	bug-memstream1 bug-wmemstream1 \
 	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
 	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
diff --git a/libio/memstream.c b/libio/memstream.c
index e20b9c2..f1e8d58 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -112,8 +112,6 @@  _IO_mem_sync (_IO_FILE *fp)
       _IO_str_overflow (fp, '\0');
       --fp->_IO_write_ptr;
     }
-  else
-    *fp->_IO_write_ptr = '\0';
 
   *mp->bufloc = fp->_IO_write_base;
   *mp->sizeloc = fp->_IO_write_ptr - fp->_IO_write_base;
diff --git a/libio/strops.c b/libio/strops.c
index 2ba3704..1bb8a77 100644
--- a/libio/strops.c
+++ b/libio/strops.c
@@ -230,6 +230,21 @@  enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)
   return 0;
 }
 
+static void
+_IO_str_switch_to_get_mode (_IO_FILE *fp)
+{
+  if (_IO_in_backup (fp))
+    fp->_IO_read_base = fp->_IO_backup_base;
+  else
+    {
+      fp->_IO_read_base = fp->_IO_buf_base;
+      if (fp->_IO_write_ptr > fp->_IO_read_end)
+        fp->_IO_read_end = fp->_IO_write_ptr;
+    }
+  fp->_IO_read_ptr = fp->_IO_read_end = fp->_IO_write_ptr;
+
+  fp->_flags &= ~_IO_CURRENTLY_PUTTING;
+}
 
 _IO_off64_t
 _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
@@ -239,14 +254,14 @@  _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
   if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))
     mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);
 
+  bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
+		     || _IO_in_put_mode (fp));
+  if (was_writing)
+    _IO_str_switch_to_get_mode (fp);
+
   if (mode == 0)
     {
-      /* Don't move any pointers. But there is no clear indication what
-	 mode FP is in. Let's guess. */
-      if (fp->_IO_file_flags & _IO_NO_WRITES)
-        new_pos = fp->_IO_read_ptr - fp->_IO_read_base;
-      else
-        new_pos = fp->_IO_write_ptr - fp->_IO_write_base;
+      new_pos = fp->_IO_read_ptr - fp->_IO_read_base;
     }
   else
     {
@@ -256,48 +271,62 @@  _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
       /* Move the get pointer, if requested. */
       if (mode & _IOS_INPUT)
 	{
+	  _IO_ssize_t base;
 	  switch (dir)
 	    {
-	    case _IO_seek_end:
-	      offset += cur_size;
+	    case _IO_seek_set:
+	      base = 0;
 	      break;
 	    case _IO_seek_cur:
-	      offset += fp->_IO_read_ptr - fp->_IO_read_base;
+	      base = fp->_IO_read_ptr - fp->_IO_read_base;
 	      break;
-	    default: /* case _IO_seek_set: */
+	    default: /* case _IO_seek_end: */
+	      base = cur_size;
 	      break;
 	    }
-	  if (offset < 0)
-	    return EOF;
-	  if ((_IO_ssize_t) offset > cur_size
-	      && enlarge_userbuf (fp, offset, 1) != 0)
+	  _IO_ssize_t maxval = SSIZE_MAX - base;
+	  if (offset < -base || offset > maxval)
+	    {
+	      __set_errno (EINVAL);
+	      return EOF;
+	    }
+	  base += offset;
+	  if (base > cur_size
+	      && enlarge_userbuf (fp, base, 1) != 0)
 	    return EOF;
-	  fp->_IO_read_ptr = fp->_IO_read_base + offset;
+	  fp->_IO_read_ptr = fp->_IO_read_base + base;
 	  fp->_IO_read_end = fp->_IO_read_base + cur_size;
-	  new_pos = offset;
+	  new_pos = base;
 	}
 
       /* Move the put pointer, if requested. */
       if (mode & _IOS_OUTPUT)
 	{
+	  _IO_ssize_t base;
 	  switch (dir)
 	    {
-	    case _IO_seek_end:
-	      offset += cur_size;
+	    case _IO_seek_set:
+	      base = 0;
 	      break;
 	    case _IO_seek_cur:
-	      offset += fp->_IO_write_ptr - fp->_IO_write_base;
+	      base = fp->_IO_write_ptr - fp->_IO_write_base;
 	      break;
-	    default: /* case _IO_seek_set: */
+	    default: /* case _IO_seek_end: */
+	      base = cur_size;
 	      break;
 	    }
-	  if (offset < 0)
-	    return EOF;
-	  if ((_IO_ssize_t) offset > cur_size
-	      && enlarge_userbuf (fp, offset, 0) != 0)
+	  _IO_ssize_t maxval = SSIZE_MAX - base;
+	  if (offset < -base || offset > maxval)
+	    {
+	      __set_errno (EINVAL);
+	      return EOF;
+	    }
+	  base += offset;
+	  if (base > cur_size
+	      && enlarge_userbuf (fp, base, 0) != 0)
 	    return EOF;
-	  fp->_IO_write_ptr = fp->_IO_write_base + offset;
-	  new_pos = offset;
+	  fp->_IO_write_ptr = fp->_IO_write_base + base;
+	  new_pos = base;
 	}
     }
   return new_pos;
diff --git a/libio/tst-memstream3.c b/libio/tst-memstream3.c
new file mode 100644
index 0000000..34b04e5
--- /dev/null
+++ b/libio/tst-memstream3.c
@@ -0,0 +1,165 @@ 
+/* Test for open_memstream implementation.
+   Copyright (C) 2016 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/>.  */
+
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+
+#ifndef CHAR_T
+# define CHAR_T char
+# define W(o) o
+# define OPEN_MEMSTREAM open_memstream
+# define PRINTF printf
+# define FWRITE fwrite
+# define FPUTC fputc
+# define STRCMP strcmp
+#endif
+
+#define S(s) S1 (s)
+#define S1(s) #s
+
+static void
+mcheck_abort (enum mcheck_status ev)
+{
+  printf ("mecheck failed with status %d\n", (int) ev);
+  exit (1);
+}
+
+static void
+error_printf (int line, const char *fmt, ...)
+{
+  va_list ap;
+
+  printf ("error: %s:%i: ", __FILE__, line);
+  va_start (ap, fmt);
+  vprintf (fmt, ap);
+  va_end (ap);
+}
+
+#define ERROR_RET1(...) \
+  { error_printf(__LINE__, __VA_ARGS__); return 1; }
+
+static int
+do_test_bz18241 (void)
+{
+  CHAR_T *buf;
+  size_t size;
+
+  FILE *fp = OPEN_MEMSTREAM (&buf, &size);
+  if (fp == NULL)
+    ERROR_RET1 ("%s failed\n", S(OPEN_MEMSTREAM));
+
+  if (FPUTC (W('a'), fp) != W('a'))
+    ERROR_RET1 ("%s failed (errno = %d)\n", S(FPUTC), errno);
+  if (fflush (fp) != 0)
+    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);
+  if (fseek (fp, -2, SEEK_SET) != -1)
+    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+  if (errno != EINVAL)
+    ERROR_RET1 ("errno != EINVAL\n");
+  if (ftell (fp) != 1)
+    ERROR_RET1 ("ftell failed (errno = %d)\n", errno);
+  if (ferror (fp) != 0)
+    ERROR_RET1 ("ferror != 0\n");
+
+  if (fseek (fp, -1, SEEK_CUR) == -1)
+    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+  if (ftell (fp) != 0)
+    ERROR_RET1 ("ftell failed (errno = %d)\n", errno);
+  if (ferror (fp) != 0)
+    ERROR_RET1 ("ferror != 0\n");
+  if (FPUTC (W('b'), fp) != W('b'))
+    ERROR_RET1 ("%s failed (errno = %d)\n", S(FPUTC), errno);
+  if (fflush (fp) != 0)
+    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);
+
+  if (fclose (fp) != 0)
+    ERROR_RET1 ("fclose failed (errno = %d\n", errno);
+
+  if (STRCMP (buf, W("b")) != 0)
+    ERROR_RET1 ("%s failed\n", S(STRCMP));
+
+  free (buf);
+
+  return 0;
+}
+
+static int
+do_test_bz20181 (void)
+{
+  CHAR_T *buf;
+  size_t size;
+  size_t ret;
+
+  FILE *fp = OPEN_MEMSTREAM (&buf, &size);
+  if (fp == NULL)
+    ERROR_RET1 ("%s failed\n", S(OPEN_MEMSTREAM));
+
+  if ((ret = FWRITE (W("abc"), 1, 3, fp)) != 3)
+    ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);
+
+  if (fseek (fp, 0, SEEK_SET) != 0)
+    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+
+  if (FWRITE (W("z"), 1, 1, fp) != 1)
+    ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);
+
+  if (fflush (fp) != 0)
+    ERROR_RET1 ("fflush failed (errno = %d)\n", errno);
+
+  /* Avoid truncating the buffer on close.  */
+  if (fseek (fp, 3, SEEK_SET) != 0)
+    ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+
+  if (fclose (fp) != 0)
+    ERROR_RET1 ("fclose failed (errno = %d\n", errno);
+
+  if (size != 3)
+    ERROR_RET1 ("size != 3\n");
+
+  if (buf[0] != W('z')
+      || buf[1] != W('b')
+      || buf[2] != W('c'))
+    {
+      PRINTF (W("error: buf {%c,%c,%c} != {z,b,c}\n"),
+	      buf[0], buf[1], buf[2]);
+      return 1;
+    }
+    
+  free (buf);
+
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  int ret = 0;
+
+  mcheck_pedantic (mcheck_abort);
+
+  ret += do_test_bz18241 ();
+  ret += do_test_bz20181 ();
+
+  return ret;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libio/tst-wmemstream3.c b/libio/tst-wmemstream3.c
new file mode 100644
index 0000000..190283a
--- /dev/null
+++ b/libio/tst-wmemstream3.c
@@ -0,0 +1,44 @@ 
+/* Test for open_memstream implementation.
+   Copyright (C) 2016 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/>.  */
+
+#include <wchar.h>
+
+/* Straighforward implementation so tst-memstream3 could use check
+   fwrite on open_memstream.  */
+static size_t
+fwwrite (const void *ptr, size_t size, size_t nmemb, FILE *arq)
+{
+  const wchar_t *wcs = (const wchar_t*) (ptr);
+  for (size_t s = 0; s < size; s++)
+    {
+      for (size_t n = 0; n < nmemb; n++)
+        if (fputwc (wcs[n], arq) == WEOF)
+          return n; 
+    }
+  return size * nmemb; 
+}
+
+#define CHAR_T wchar_t
+#define W(o) L##o
+#define OPEN_MEMSTREAM open_wmemstream
+#define PRINTF wprintf
+#define FWRITE fwwrite
+#define FPUTC  fputwc
+#define STRCMP wcscmp
+
+#include "tst-memstream3.c"
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index bf2a50b..fd01be0 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -112,8 +112,6 @@  _IO_wmem_sync (_IO_FILE *fp)
       _IO_wstr_overflow (fp, '\0');
       --fp->_wide_data->_IO_write_ptr;
     }
-  else
-    *fp->_wide_data->_IO_write_ptr = '\0';
 
   *mp->bufloc = fp->_wide_data->_IO_write_base;
   *mp->sizeloc = (fp->_wide_data->_IO_write_ptr
diff --git a/libio/wstrops.c b/libio/wstrops.c
index 09fa543..0b2bec3 100644
--- a/libio/wstrops.c
+++ b/libio/wstrops.c
@@ -169,7 +169,7 @@  _IO_wstr_count (_IO_FILE *fp)
 static int
 enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)
 {
-  if ((_IO_ssize_t) offset <= _IO_blen (fp))
+  if ((_IO_ssize_t) offset <= _IO_wblen (fp))
     return 0;
 
   struct _IO_wide_data *wd = fp->_wide_data;
@@ -235,6 +235,22 @@  enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading)
   return 0;
 }
 
+static void
+_IO_wstr_switch_to_get_mode (_IO_FILE *fp)
+{
+  if (_IO_in_backup (fp))
+    fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_backup_base;
+  else
+    {
+      fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_buf_base;
+      if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)
+        fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
+    }
+  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;
+  fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
+
+  fp->_flags &= ~_IO_CURRENTLY_PUTTING;
+}
 
 _IO_off64_t
 _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
@@ -244,15 +260,16 @@  _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
   if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))
     mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);
 
+  bool was_writing = (fp->_wide_data->_IO_write_ptr >
+			fp->_wide_data->_IO_write_base
+		     || _IO_in_put_mode (fp));
+  if (was_writing)
+    _IO_wstr_switch_to_get_mode (fp);
+
   if (mode == 0)
     {
-      /* Don't move any pointers. But there is no clear indication what
-	 mode FP is in. Let's guess. */
-      if (fp->_IO_file_flags & _IO_NO_WRITES)
-        new_pos = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_base;
-      else
-        new_pos = (fp->_wide_data->_IO_write_ptr
-		   - fp->_wide_data->_IO_write_base);
+      new_pos = (fp->_wide_data->_IO_write_ptr
+		 - fp->_wide_data->_IO_write_base);
     }
   else
     {
@@ -262,25 +279,32 @@  _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
       /* Move the get pointer, if requested. */
       if (mode & _IOS_INPUT)
 	{
+	  _IO_ssize_t base;
 	  switch (dir)
 	    {
-	    case _IO_seek_end:
-	      offset += cur_size;
+	    case _IO_seek_set:
+	      base = 0;
 	      break;
 	    case _IO_seek_cur:
-	      offset += (fp->_wide_data->_IO_read_ptr
-			 - fp->_wide_data->_IO_read_base);
+	      base = (fp->_wide_data->_IO_read_ptr
+		     - fp->_wide_data->_IO_read_base);
 	      break;
-	    default: /* case _IO_seek_set: */
+	    default: /* case _IO_seek_end: */
+	      base = cur_size;
 	      break;
 	    }
-	  if (offset < 0)
-	    return EOF;
-	  if ((_IO_ssize_t) offset > cur_size
-	      && enlarge_userbuf (fp, offset, 1) != 0)
+	  _IO_ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;
+	  if (offset < -base || offset > maxval)
+	    {
+	      __set_errno (EINVAL);
+	      return EOF;
+	    }
+	  base += offset;
+	  if (base > cur_size
+	      && enlarge_userbuf (fp, base, 1) != 0)
 	    return EOF;
 	  fp->_wide_data->_IO_read_ptr = (fp->_wide_data->_IO_read_base
-					  + offset);
+					  + base);
 	  fp->_wide_data->_IO_read_end = (fp->_wide_data->_IO_read_base
 					  + cur_size);
 	  new_pos = offset;
@@ -289,26 +313,33 @@  _IO_wstr_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
       /* Move the put pointer, if requested. */
       if (mode & _IOS_OUTPUT)
 	{
+	  _IO_ssize_t base;
 	  switch (dir)
 	    {
-	    case _IO_seek_end:
-	      offset += cur_size;
+	    case _IO_seek_set:
+	      base = 0;
 	      break;
 	    case _IO_seek_cur:
-	      offset += (fp->_wide_data->_IO_write_ptr
-			 - fp->_wide_data->_IO_write_base);
+	      base = (fp->_wide_data->_IO_write_ptr
+		     - fp->_wide_data->_IO_write_base);
 	      break;
-	    default: /* case _IO_seek_set: */
+	    default: /* case _IO_seek_end: */
+	      base = cur_size;
 	      break;
 	    }
-	  if (offset < 0)
-	    return EOF;
-	  if ((_IO_ssize_t) offset > cur_size
-	      && enlarge_userbuf (fp, offset, 0) != 0)
+	  _IO_ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;
+	  if (offset < -base || offset > maxval)
+	    {
+	      __set_errno (EINVAL);
+	      return EOF;
+	    }
+	  base += offset;
+	  if (base > cur_size
+	      && enlarge_userbuf (fp, base, 0) != 0)
 	    return EOF;
 	  fp->_wide_data->_IO_write_ptr = (fp->_wide_data->_IO_write_base
-					   + offset);
-	  new_pos = offset;
+					   + base);
+	  new_pos = base;
 	}
     }
   return new_pos;
diff --git a/manual/examples/memstrm.c b/manual/examples/memstrm.c
index 0d443b1..5701ba1 100644
--- a/manual/examples/memstrm.c
+++ b/manual/examples/memstrm.c
@@ -27,10 +27,10 @@  main (void)
   stream = open_memstream (&bp, &size);
   fprintf (stream, "hello");
   fflush (stream);
-  printf ("buf = `%s', size = %d\n", bp, size);
+  printf ("buf = `%s', size = %zu\n", bp, size);
   fprintf (stream, ", world");
   fclose (stream);
-  printf ("buf = `%s', size = %d\n", bp, size);
+  printf ("buf = `%s', size = %zu\n", bp, size);
 
   return 0;
 }