[v2] libio: use PTR_MANGLE/PTR_DEMANGLE for FILE vtables

Message ID 20151001184048.GA31563@www.outflux.net
State New, archived
Headers

Commit Message

Kees Cook Oct. 1, 2015, 6:40 p.m. UTC
  Hi!

This is a resend (from 2011!) of my PTR_MANGLE series for the FILE vtable.
It was previously discussed here:
https://sourceware.org/ml/libc-alpha/2011-12/msg00073.html

It sounds like only very very old code was sharing FILE externally,
so this appears safe now. "make xcheck" passes. If this is NOT okay for
general use, I'd like to make it a configure option, since for modern
glibc users, it works fine. (e.g. Chrome OS has been using this patch
for 3 years now.)

Thanks!

-Kees

[v2: fixed GNU style, URL, thanks to vapier]
---
2015-08-12  Kees Cook <keescook@chromium.org>

	* libio/libioP.h: Create inline helpers to run the PTR_MANGLE and
	PTR_DEMANGLE routines on vtable pointers. Create _IO_JUMPS_SET and
	_IO_WIDE_JUMPS_SET macros to use the new inline helpers. Update the
	_IO_JUMPS_FUNC and _IO_WIDE_JUMPS_FUNC to use the new inline helpers.
	* debug/obprintf_chk.c: Replace direct vtable assignment with macro.
	* debug/vasprintf_chk.c: Likewise.
	* debug/vdprintf_chk.c: Likewise.
	* debug/vsnprintf_chk.c: Likewise.
	* debug/vsprintf_chk.c: Likewise.
	* libio/fileops.c: Likewise.
	* libio/freopen.c: Likewise.
	* libio/freopen64.c: Likewise.
	* libio/genops.c: Likewise.
	* libio/iofdopen.c: Likewise.
	* libio/iofopen.c: Likewise.
	* libio/iofopncook.c: Likewise.
	* libio/iofwide.c: Likewise.
	* libio/iopopen.c: Likewise.
	* libio/iovdprintf.c: Likewise.
	* libio/iovsprintf.c: Likewise.
	* libio/iovsscanf.c: Likewise.
	* libio/memstream.c: Likewise.
	* libio/obprintf.c: Likewise.
	* libio/oldiofdopen.c: Likewise.
	* libio/oldiofopen.c: Likewise.
	* libio/oldiopopen.c: Likewise.
	* libio/vasprintf.c: Likewise.
	* libio/vsnprintf.c: Likewise.
	* stdio-common/isoc99_vsscanf.c: Likewise.
	* stdio-common/vfprintf.c: Likewise.
	* stdlib/strfmon_l.c: Likewise.
	* misc/init-misc.c: Mangle predefined stdio FILE vtables at startup.
---
 debug/obprintf_chk.c          |  2 +-
 debug/vasprintf_chk.c         |  2 +-
 debug/vdprintf_chk.c          |  2 +-
 debug/vsnprintf_chk.c         |  2 +-
 debug/vsprintf_chk.c          |  2 +-
 libio/fileops.c               | 28 +++++++++----------
 libio/freopen.c               |  6 ++---
 libio/freopen64.c             |  4 +--
 libio/genops.c                |  2 +-
 libio/iofdopen.c              |  4 +--
 libio/iofopen.c               |  8 +++---
 libio/iofopncook.c            |  4 +--
 libio/iofwide.c               |  2 +-
 libio/iopopen.c               |  2 +-
 libio/iovdprintf.c            |  2 +-
 libio/iovsprintf.c            |  2 +-
 libio/iovsscanf.c             |  2 +-
 libio/libioP.h                | 62 ++++++++++++++++++++++++++++++-------------
 libio/memstream.c             |  2 +-
 libio/obprintf.c              |  2 +-
 libio/oldiofdopen.c           |  2 +-
 libio/oldiofopen.c            |  2 +-
 libio/oldiopopen.c            |  2 +-
 libio/vasprintf.c             |  2 +-
 libio/vsnprintf.c             |  2 +-
 misc/init-misc.c              | 13 +++++++++
 stdio-common/isoc99_vsscanf.c |  2 +-
 stdio-common/vfprintf.c       |  2 +-
 stdlib/strfmon_l.c            |  2 +-
 29 files changed, 104 insertions(+), 67 deletions(-)
  

Comments

Florian Weimer Oct. 1, 2015, 6:52 p.m. UTC | #1
On 10/01/2015 08:40 PM, Kees Cook wrote:
> Hi!
> 
> This is a resend (from 2011!) of my PTR_MANGLE series for the FILE vtable.
> It was previously discussed here:
> https://sourceware.org/ml/libc-alpha/2011-12/msg00073.html
> 
> It sounds like only very very old code was sharing FILE externally,
> so this appears safe now. "make xcheck" passes. If this is NOT okay for
> general use, I'd like to make it a configure option, since for modern
> glibc users, it works fine. (e.g. Chrome OS has been using this patch
> for 3 years now.)

This needs a configure option for now, but we can enable it for some
architectures per default.

At one point, someone needs to do the thankless job and unearth where
the cut-over point to modern libstdc++ was, and which architectures
actually need the vtable compatibility.  (If there was no GCC version
that could compile the relevant libstdc++ versions, then compatibility
is obviously unnecessary.)

Even better, if we can grab a few old binaries and they do not run on
current systems for other libstdc++-related reasons (or perhaps we
subtly broke the libio ABI over time), we might justify giving up
backwards compatibility in this area.
  
Kees Cook Oct. 1, 2015, 7:01 p.m. UTC | #2
On Thu, Oct 1, 2015 at 11:52 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 10/01/2015 08:40 PM, Kees Cook wrote:
>> Hi!
>>
>> This is a resend (from 2011!) of my PTR_MANGLE series for the FILE vtable.
>> It was previously discussed here:
>> https://sourceware.org/ml/libc-alpha/2011-12/msg00073.html
>>
>> It sounds like only very very old code was sharing FILE externally,
>> so this appears safe now. "make xcheck" passes. If this is NOT okay for
>> general use, I'd like to make it a configure option, since for modern
>> glibc users, it works fine. (e.g. Chrome OS has been using this patch
>> for 3 years now.)
>
> This needs a configure option for now, but we can enable it for some
> architectures per default.

How do you think this should best be handled? (Are there examples of
similar configure flags?)

> At one point, someone needs to do the thankless job and unearth where
> the cut-over point to modern libstdc++ was, and which architectures
> actually need the vtable compatibility.  (If there was no GCC version
> that could compile the relevant libstdc++ versions, then compatibility
> is obviously unnecessary.)

Is there a hint about how to detect this? I don't know what I'd be looking for.

> Even better, if we can grab a few old binaries and they do not run on
> current systems for other libstdc++-related reasons (or perhaps we
> subtly broke the libio ABI over time), we might justify giving up
> backwards compatibility in this area.

I'm open to suggestions on what to test. :)

-Kees
  
Florian Weimer Oct. 1, 2015, 7:31 p.m. UTC | #3
On 10/01/2015 09:01 PM, Kees Cook wrote:
> On Thu, Oct 1, 2015 at 11:52 AM, Florian Weimer <fweimer@redhat.com> wrote:
>> On 10/01/2015 08:40 PM, Kees Cook wrote:
>>> Hi!
>>>
>>> This is a resend (from 2011!) of my PTR_MANGLE series for the FILE vtable.
>>> It was previously discussed here:
>>> https://sourceware.org/ml/libc-alpha/2011-12/msg00073.html
>>>
>>> It sounds like only very very old code was sharing FILE externally,
>>> so this appears safe now. "make xcheck" passes. If this is NOT okay for
>>> general use, I'd like to make it a configure option, since for modern
>>> glibc users, it works fine. (e.g. Chrome OS has been using this patch
>>> for 3 years now.)
>>
>> This needs a configure option for now, but we can enable it for some
>> architectures per default.
> 
> How do you think this should best be handled? (Are there examples of
> similar configure flags?)

Probably something like this in configure.ac:

AC_ARG_ENABLE([stackguard-randomization],
              AC_HELP_STRING([--enable-stackguard-randomization],
                             [initialize __stack_chk_guard canary with a
random number at program start]),
              [enable_stackguard_randomize=$enableval],
              [enable_stackguard_randomize=no])
if test "$enable_stackguard_randomize" = yes; then
  AC_DEFINE(ENABLE_STACKGUARD_RANDOMIZE)
fi

And you need to update config.h.in.

>> At one point, someone needs to do the thankless job and unearth where
>> the cut-over point to modern libstdc++ was, and which architectures
>> actually need the vtable compatibility.  (If there was no GCC version
>> that could compile the relevant libstdc++ versions, then compatibility
>> is obviously unnecessary.)
> 
> Is there a hint about how to detect this? I don't know what I'd be looking for.

libio.h is apparently an installed header, so it is still part of the
public API.  This means that this API is technically supported on all
current architectures, even those which never saw the old libstdc++
version.  (libstdc++ switched in 2003…)

This means that vtable mangling is very much a backwards-incompatible
change.  We can still salvage this in some way.

Do you care mainly about statically linked libc, dynamically linked
libc, or both?
  
Kees Cook Oct. 1, 2015, 7:40 p.m. UTC | #4
On Thu, Oct 1, 2015 at 12:31 PM, Florian Weimer <fweimer@redhat.com> wrote:
> On 10/01/2015 09:01 PM, Kees Cook wrote:
>> On Thu, Oct 1, 2015 at 11:52 AM, Florian Weimer <fweimer@redhat.com> wrote:
>>> On 10/01/2015 08:40 PM, Kees Cook wrote:
>>>> Hi!
>>>>
>>>> This is a resend (from 2011!) of my PTR_MANGLE series for the FILE vtable.
>>>> It was previously discussed here:
>>>> https://sourceware.org/ml/libc-alpha/2011-12/msg00073.html
>>>>
>>>> It sounds like only very very old code was sharing FILE externally,
>>>> so this appears safe now. "make xcheck" passes. If this is NOT okay for
>>>> general use, I'd like to make it a configure option, since for modern
>>>> glibc users, it works fine. (e.g. Chrome OS has been using this patch
>>>> for 3 years now.)
>>>
>>> This needs a configure option for now, but we can enable it for some
>>> architectures per default.
>>
>> How do you think this should best be handled? (Are there examples of
>> similar configure flags?)
>
> Probably something like this in configure.ac:
>
> AC_ARG_ENABLE([stackguard-randomization],
>               AC_HELP_STRING([--enable-stackguard-randomization],
>                              [initialize __stack_chk_guard canary with a
> random number at program start]),
>               [enable_stackguard_randomize=$enableval],
>               [enable_stackguard_randomize=no])
> if test "$enable_stackguard_randomize" = yes; then
>   AC_DEFINE(ENABLE_STACKGUARD_RANDOMIZE)
> fi
>
> And you need to update config.h.in.

Thanks! I'll poke around...

>
>>> At one point, someone needs to do the thankless job and unearth where
>>> the cut-over point to modern libstdc++ was, and which architectures
>>> actually need the vtable compatibility.  (If there was no GCC version
>>> that could compile the relevant libstdc++ versions, then compatibility
>>> is obviously unnecessary.)
>>
>> Is there a hint about how to detect this? I don't know what I'd be looking for.
>
> libio.h is apparently an installed header, so it is still part of the
> public API.  This means that this API is technically supported on all
> current architectures, even those which never saw the old libstdc++
> version.  (libstdc++ switched in 2003…)
>
> This means that vtable mangling is very much a backwards-incompatible
> change.  We can still salvage this in some way.

Well, no, I don't think that itself makes it backward-incompatible:
_IO_jump_t and _IO_FILE are opaque structures in libio.h. Am I missing
something?

> Do you care mainly about statically linked libc, dynamically linked
> libc, or both?

I care about both, but I tend to only use dynamic.

-Kees
  
Florian Weimer Oct. 1, 2015, 8:14 p.m. UTC | #5
On 10/01/2015 09:40 PM, Kees Cook wrote:

>>> How do you think this should best be handled? (Are there examples of
>>> similar configure flags?)
>>
>> Probably something like this in configure.ac:

>> And you need to update config.h.in.
> 
> Thanks! I'll poke around...

It's probably not needed if we come up with a completely
backwards-compatible approach.

>> libio.h is apparently an installed header, so it is still part of the
>> public API.  This means that this API is technically supported on all
>> current architectures, even those which never saw the old libstdc++
>> version.  (libstdc++ switched in 2003…)
>>
>> This means that vtable mangling is very much a backwards-incompatible
>> change.  We can still salvage this in some way.
> 
> Well, no, I don't think that itself makes it backward-incompatible:
> _IO_jump_t and _IO_FILE are opaque structures in libio.h. Am I missing
> something?

There is a full definition, and there is a widely-used getopt.c file
which references it (although not the vtable bits).  Let's hope it's not
actually unused. :-(

Functions like putc also use it.  Removing libio.h from the API likely
breaks the papi package.

>> Do you care mainly about statically linked libc, dynamically linked
>> libc, or both?
> 
> I care about both, but I tend to only use dynamic.

In the dynamic case, we compile in all the vtables we need.  We should
put them into an array, so that we can check easily if we have a
known-good vtable.  If we deprecate libio API (remove the installed
header and the default symbol version), we could set flag once we
encounter a reference to a libio symbol, and when that happens, we set a
flag that disables the vtable check.

The difficult question is how many symbol version bumps we need for this
to work.  Do we have to bump all stdio symbols, too, because someone
could write their own _IO_FILE class and pass it to fread?

Florian
  
Kees Cook Oct. 1, 2015, 8:28 p.m. UTC | #6
On Thu, Oct 1, 2015 at 1:14 PM, Florian Weimer <fweimer@redhat.com> wrote:
> On 10/01/2015 09:40 PM, Kees Cook wrote:
>
>>>> How do you think this should best be handled? (Are there examples of
>>>> similar configure flags?)
>>>
>>> Probably something like this in configure.ac:
>
>>> And you need to update config.h.in.
>>
>> Thanks! I'll poke around...
>
> It's probably not needed if we come up with a completely
> backwards-compatible approach.
>
>>> libio.h is apparently an installed header, so it is still part of the
>>> public API.  This means that this API is technically supported on all
>>> current architectures, even those which never saw the old libstdc++
>>> version.  (libstdc++ switched in 2003…)
>>>
>>> This means that vtable mangling is very much a backwards-incompatible
>>> change.  We can still salvage this in some way.
>>
>> Well, no, I don't think that itself makes it backward-incompatible:
>> _IO_jump_t and _IO_FILE are opaque structures in libio.h. Am I missing
>> something?
>
> There is a full definition, and there is a widely-used getopt.c file
> which references it (although not the vtable bits).  Let's hope it's not
> actually unused. :-(

I can't find reference to the vtable portion of the structure outside
of the glibc sources.

> Functions like putc also use it.  Removing libio.h from the API likely
> breaks the papi package.
>
>>> Do you care mainly about statically linked libc, dynamically linked
>>> libc, or both?
>>
>> I care about both, but I tend to only use dynamic.
>
> In the dynamic case, we compile in all the vtables we need.  We should
> put them into an array, so that we can check easily if we have a
> known-good vtable.  If we deprecate libio API (remove the installed
> header and the default symbol version), we could set flag once we
> encounter a reference to a libio symbol, and when that happens, we set a
> flag that disables the vtable check.
>
> The difficult question is how many symbol version bumps we need for this
> to work.  Do we have to bump all stdio symbols, too, because someone
> could write their own _IO_FILE class and pass it to fread?

I can't find where it's possible to write one's own _IO_FILE class.

-Kees
  
Joseph Myers Oct. 1, 2015, 8:55 p.m. UTC | #7
On Thu, 1 Oct 2015, Florian Weimer wrote:

> >> At one point, someone needs to do the thankless job and unearth where
> >> the cut-over point to modern libstdc++ was, and which architectures
> >> actually need the vtable compatibility.  (If there was no GCC version
> >> that could compile the relevant libstdc++ versions, then compatibility
> >> is obviously unnecessary.)
> > 
> > Is there a hint about how to detect this? I don't know what I'd be looking for.
> 
> libio.h is apparently an installed header, so it is still part of the
> public API.  This means that this API is technically supported on all
> current architectures, even those which never saw the old libstdc++
> version.  (libstdc++ switched in 2003…)

I don't think it's part of the public API; it should only ever be included 
via <stdio.h>, and only the non-reserved-namespace <stdio.h> interfaces 
are part of the public API.  (Note that various _IO_* names may be used in 
<stdio.h> macros / inlines so may be part of the public ABI that way.)  By 
modern glibc standards, this header, and _G_config.h, should be entirely 
in bits/.

(It looks like, while it was GCC 3.4 that removed all traces of libio use, 
GCC 3.0 through 3.3 didn't actually default to using it in libstdc++-v3.  
I don't know if libstdc++ versions using libio actually did so in a way 
that embedded dependencies on vtable contents in the libstdc++ binaries or 
in executables using it.)
  
Joseph Myers Oct. 1, 2015, 9 p.m. UTC | #8
On Thu, 1 Oct 2015, Florian Weimer wrote:

> Even better, if we can grab a few old binaries and they do not run on
> current systems for other libstdc++-related reasons (or perhaps we

The libstdc++ SONAME changed in GCC 3.4, so any relevant binaries will be 
using an old libstdc++ shared library (or possibly statically linked with 
libstdc++ but dynamically linked with libc).
  
Kees Cook Oct. 1, 2015, 9:15 p.m. UTC | #9
On Thu, Oct 1, 2015 at 2:00 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> On Thu, 1 Oct 2015, Florian Weimer wrote:
>
>> Even better, if we can grab a few old binaries and they do not run on
>> current systems for other libstdc++-related reasons (or perhaps we
>
> The libstdc++ SONAME changed in GCC 3.4, so any relevant binaries will be
> using an old libstdc++ shared library (or possibly statically linked with
> libstdc++ but dynamically linked with libc).

So system without ancient static binaries and no libstdc++-v3 or
earlier should be safe for this change? When did v4 happen? Seems like
a long time ago?

-Kees
  
Joseph Myers Oct. 1, 2015, 9:41 p.m. UTC | #10
On Thu, 1 Oct 2015, Kees Cook wrote:

> On Thu, Oct 1, 2015 at 2:00 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> > On Thu, 1 Oct 2015, Florian Weimer wrote:
> >
> >> Even better, if we can grab a few old binaries and they do not run on
> >> current systems for other libstdc++-related reasons (or perhaps we
> >
> > The libstdc++ SONAME changed in GCC 3.4, so any relevant binaries will be
> > using an old libstdc++ shared library (or possibly statically linked with
> > libstdc++ but dynamically linked with libc).
> 
> So system without ancient static binaries and no libstdc++-v3 or
> earlier should be safe for this change? When did v4 happen? Seems like
> a long time ago?

It's still libstdc++-v3 even though it's gone through multiple SONAMEs 
(it's been v3 since GCC 3.0).  The question is libstdc++.so.5 or older - 
or dynamically linked binaries from compilers of that vintage with 
libstdc++.a linked in.
  
Kees Cook Oct. 1, 2015, 10:27 p.m. UTC | #11
On Thu, Oct 1, 2015 at 2:41 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> On Thu, 1 Oct 2015, Kees Cook wrote:
>
>> On Thu, Oct 1, 2015 at 2:00 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>> > On Thu, 1 Oct 2015, Florian Weimer wrote:
>> >
>> >> Even better, if we can grab a few old binaries and they do not run on
>> >> current systems for other libstdc++-related reasons (or perhaps we
>> >
>> > The libstdc++ SONAME changed in GCC 3.4, so any relevant binaries will be
>> > using an old libstdc++ shared library (or possibly statically linked with
>> > libstdc++ but dynamically linked with libc).
>>
>> So system without ancient static binaries and no libstdc++-v3 or
>> earlier should be safe for this change? When did v4 happen? Seems like
>> a long time ago?
>
> It's still libstdc++-v3 even though it's gone through multiple SONAMEs
> (it's been v3 since GCC 3.0).  The question is libstdc++.so.5 or older -
> or dynamically linked binaries from compilers of that vintage with
> libstdc++.a linked in.

Okay, so, since roughly 2007? Should I name the config "--only-v6+" or
something?

-Kees
  
Mike Frysinger Oct. 2, 2015, 10:22 p.m. UTC | #12
On 01 Oct 2015 15:27, Kees Cook wrote:
> On Thu, Oct 1, 2015 at 2:41 PM, Joseph Myers wrote:
> > On Thu, 1 Oct 2015, Kees Cook wrote:
> >> On Thu, Oct 1, 2015 at 2:00 PM, Joseph Myers wrote:
> >> > On Thu, 1 Oct 2015, Florian Weimer wrote:
> >> >> Even better, if we can grab a few old binaries and they do not run on
> >> >> current systems for other libstdc++-related reasons (or perhaps we
> >> >
> >> > The libstdc++ SONAME changed in GCC 3.4, so any relevant binaries will be
> >> > using an old libstdc++ shared library (or possibly statically linked with
> >> > libstdc++ but dynamically linked with libc).
> >>
> >> So system without ancient static binaries and no libstdc++-v3 or
> >> earlier should be safe for this change? When did v4 happen? Seems like
> >> a long time ago?
> >
> > It's still libstdc++-v3 even though it's gone through multiple SONAMEs
> > (it's been v3 since GCC 3.0).  The question is libstdc++.so.5 or older -
> > or dynamically linked binaries from compilers of that vintage with
> > libstdc++.a linked in.
> 
> Okay, so, since roughly 2007? Should I name the config "--only-v6+" or
> something?

i wouldn't make it opaque.  just call it something like "libio-file-ptr-mangle".
then in the documentation, describe the possible limitations.
-mike
  
Florian Weimer Oct. 3, 2015, 7:49 p.m. UTC | #13
On 10/03/2015 12:22 AM, Mike Frysinger wrote:
> On 01 Oct 2015 15:27, Kees Cook wrote:
>> Okay, so, since roughly 2007? Should I name the config "--only-v6+" or
>> something?

It's more like 2003, but I need to double-check the dates.

> i wouldn't make it opaque.  just call it something like "libio-file-ptr-mangle".

It should be --disable-libio-compatibility or something like that.  If
we know all the possible vtables, we can do much better in terms of
hardening, and likely even devirtualize some of the function calls.
Pointer mangling is better than nothing, but I don't think the option
should be tied to that.

> then in the documentation, describe the possible limitations.

I plan to put the results of my libio archaeology on the wiki.  It
should make documenting the impact of the new configure option easier.

Florian
  
Florian Weimer Oct. 15, 2015, 12:57 p.m. UTC | #14
On 10/01/2015 10:28 PM, Kees Cook wrote:

>>>> libio.h is apparently an installed header, so it is still part of the
>>>> public API.  This means that this API is technically supported on all
>>>> current architectures, even those which never saw the old libstdc++
>>>> version.  (libstdc++ switched in 2003…)
>>>>
>>>> This means that vtable mangling is very much a backwards-incompatible
>>>> change.  We can still salvage this in some way.

> I can't find reference to the vtable portion of the structure outside
> of the glibc sources.

I tried to collect relevant information regarding this matter here,
while still keeping things relatively short:

  <https://sourceware.org/glibc/wiki/LibioVtables>

>> The difficult question is how many symbol version bumps we need for this
>> to work.  Do we have to bump all stdio symbols, too, because someone
>> could write their own _IO_FILE class and pass it to fread?
> 
> I can't find where it's possible to write one's own _IO_FILE class.

The key point is that in GCC 2.95, you could introduce additional
vtables by deriving from std::streambuf (or std::filebuf and the other
classes).

I think we should proceed with some form of vtable hardening.  I think
the main choice we face is:

  Do we want backwards compatibility with binaries which use
  custom vtables with glibc's libio?

If the answer is “no”, I suggest we apply Kees' patch *and* remove the
libio symbols listed only in <libioP.h> (technically an ABI break), so
that affected applications break in a well-defined way, instead of
crashing with a segmentation fault.

Initial internal feedback from Red Hat is that we do not need vtable
compatibility for libio.

Florian
  
Joseph Myers Oct. 15, 2015, 3:31 p.m. UTC | #15
On Thu, 15 Oct 2015, Florian Weimer wrote:

> The key point is that in GCC 2.95, you could introduce additional
> vtables by deriving from std::streambuf (or std::filebuf and the other
> classes).
> 
> I think we should proceed with some form of vtable hardening.  I think
> the main choice we face is:
> 
>   Do we want backwards compatibility with binaries which use
>   custom vtables with glibc's libio?

Does that mean only binaries that passed such derived classes directly to 
glibc functions, not any binaries that simply made use of standard C++ 
features?

> If the answer is “no”, I suggest we apply Kees' patch *and* remove the
> libio symbols listed only in <libioP.h> (technically an ABI break), so
> that affected applications break in a well-defined way, instead of
> crashing with a segmentation fault.

Would that still keep working (a) C++ binaries, whether using shared or 
static libstdc++, built with any official GCC release supporting glibc2, 
where those binaries did not pass C++ classes to C interfaces, and (b) C 
binaries built against any glibc version (i.e. none of the libioP.h 
interfaces have ever been used through stdio.h interfaces in glibc 2.0 or 
later, and none are used by old libstdc++.so)?

(I'm not concerned with binaries built with non-default configurations of 
GCC 3.0 or later, which restricts compatibility concerns to architectures 
supported in 2.95 with ABIs still supported in glibc.  I think that means 
alpha, i386 32-bit, m68k, mips 32-bit, powerpc 32-bit, sparc 32-bit and 
64-bit.)
  
Florian Weimer Oct. 15, 2015, 3:37 p.m. UTC | #16
On 10/15/2015 05:31 PM, Joseph Myers wrote:
> On Thu, 15 Oct 2015, Florian Weimer wrote:
> 
>> The key point is that in GCC 2.95, you could introduce additional
>> vtables by deriving from std::streambuf (or std::filebuf and the other
>> classes).
>>
>> I think we should proceed with some form of vtable hardening.  I think
>> the main choice we face is:
>>
>>   Do we want backwards compatibility with binaries which use
>>   custom vtables with glibc's libio?
> 
> Does that mean only binaries that passed such derived classes directly to 
> glibc functions, not any binaries that simply made use of standard C++ 
> features?

All binaries which used the old libio-based libstdc++ would be affected.

>> If the answer is “no”, I suggest we apply Kees' patch *and* remove the
>> libio symbols listed only in <libioP.h> (technically an ABI break), so
>> that affected applications break in a well-defined way, instead of
>> crashing with a segmentation fault.
> 
> Would that still keep working (a) C++ binaries, whether using shared or 
> static libstdc++, built with any official GCC release supporting glibc2, 
> where those binaries did not pass C++ classes to C interfaces,

No, they would no longer work.

> and (b) C 
> binaries built against any glibc version (i.e. none of the libioP.h 
> interfaces have ever been used through stdio.h interfaces in glibc 2.0 or 
> later, and none are used by old libstdc++.so)?

Yes, for libioP.h (the private, non-installed header).  We may have
overlooked something, and code which uses <libio.h> directly may be
affected by changes (certainly if we proceed to clean up things and
devirtualize some of the functions).  But it has been suggested on this
thread that struct _IO_FILE is internal (along with the support
functions in <libio.h>) although it is an installed header file.

> (I'm not concerned with binaries built with non-default configurations of 
> GCC 3.0 or later, which restricts compatibility concerns to architectures 
> supported in 2.95 with ABIs still supported in glibc.  I think that means 
> alpha, i386 32-bit, m68k, mips 32-bit, powerpc 32-bit, sparc 32-bit and 
> 64-bit.)

We have libstdc++ compat libraries for ia64, s390 and s390x, too.

Florian
  
Joseph Myers Oct. 15, 2015, 3:56 p.m. UTC | #17
On Thu, 15 Oct 2015, Florian Weimer wrote:

> > Would that still keep working (a) C++ binaries, whether using shared or 
> > static libstdc++, built with any official GCC release supporting glibc2, 
> > where those binaries did not pass C++ classes to C interfaces,
> 
> No, they would no longer work.

Well, I think the starting point is that those are valid binaries that 
users who've kept their binaries and libstdc++.so shared libraries should 
be able to expect to continue to work - that we don't break binary 
compatibility for users using documented interfaces in the documented way.

We have symbol versioning for backwards compatibility with binaries built 
with glibc 2.0.  Is it not possible to do something like that for 
compatibility with these C++ binaries (if necessary, increasing the value 
of _G_IO_IO_FILE_VERSION / _IO_stdin_used again)?

(I don't know how much compatibility the existing versioning provides if 
the executable and shared libraries mix binaries built with 2.0 and 
binaries built with >= 2.1.  If we introduce new versions I suppose we'd 
want mixtures of pre- and post-2.23 executables and shared libraries to 
work as long as they don't have any code using old libstdc++, but the old 
libstdc++ case could probably be limited to having the executable and all 
the non-glibc shared libraries it uses built with old glibc.  And 
architectures not supported before GCC 3.0 wouldn't need the compatibility 
code.)

> > (I'm not concerned with binaries built with non-default configurations of 
> > GCC 3.0 or later, which restricts compatibility concerns to architectures 
> > supported in 2.95 with ABIs still supported in glibc.  I think that means 
> > alpha, i386 32-bit, m68k, mips 32-bit, powerpc 32-bit, sparc 32-bit and 
> > 64-bit.)
> 
> We have libstdc++ compat libraries for ia64, s390 and s390x, too.

Is that about 2.96 or backported architecture support (since 2.95.3 
doesn't have ports to those architectures at all)?
  
Florian Weimer Oct. 15, 2015, 4:16 p.m. UTC | #18
On 10/15/2015 05:56 PM, Joseph Myers wrote:
> On Thu, 15 Oct 2015, Florian Weimer wrote:
> 
>>> Would that still keep working (a) C++ binaries, whether using shared or 
>>> static libstdc++, built with any official GCC release supporting glibc2, 
>>> where those binaries did not pass C++ classes to C interfaces,
>>
>> No, they would no longer work.
> 
> Well, I think the starting point is that those are valid binaries that 
> users who've kept their binaries and libstdc++.so shared libraries should 
> be able to expect to continue to work - that we don't break binary 
> compatibility for users using documented interfaces in the documented way.

I respect your view, but we did that for errno (and presumably _res and
h_errno, but I have less experience with that) and the i386 ABI bump for
SSE stack alignment.

> We have symbol versioning for backwards compatibility with binaries built 
> with glibc 2.0.  Is it not possible to do something like that for 
> compatibility with these C++ binaries (if necessary, increasing the value 
> of _G_IO_IO_FILE_VERSION / _IO_stdin_used again)?

If we can detect that a binary references these symbols:

_IO_file_init@GLIBC_2.1
_IO_init@GLIBC_2.0
_IO_str_init_readonly@GLIBC_2.0
_IO_str_init_static@GLIBC_2.0

we can disable vtable hardening globally.  This would not cover the
scenario where the binary dlopens something that uses an old libstdc++
(linked statically or dynamically) later, but it would provide
compatibility where a copy of libstdc++ is pulled in through DT_NEEDED.

This will not enable us to do substantial implementation changes in
libio because we would still be committed to the tight coupling with old
libstdc++, and we would have to keep maintaining two code paths
indefinitely (one of which is impossible to test without a historic
toolchain).

Bumping symbol versions for stdio functions would cause significant pain
for distributions which do not do mass rebuilds because FILE * streams
are often passed across library boundaries.

>>> (I'm not concerned with binaries built with non-default configurations of 
>>> GCC 3.0 or later, which restricts compatibility concerns to architectures 
>>> supported in 2.95 with ABIs still supported in glibc.  I think that means 
>>> alpha, i386 32-bit, m68k, mips 32-bit, powerpc 32-bit, sparc 32-bit and 
>>> 64-bit.)
>>
>> We have libstdc++ compat libraries for ia64, s390 and s390x, too.
> 
> Is that about 2.96 or backported architecture support (since 2.95.3 
> doesn't have ports to those architectures at all)?

ia64 seems to be 2.96, s390 and s390x are 2.95.

Florian
  
Joseph Myers Oct. 15, 2015, 4:55 p.m. UTC | #19
On Thu, 15 Oct 2015, Florian Weimer wrote:

> > Well, I think the starting point is that those are valid binaries that 
> > users who've kept their binaries and libstdc++.so shared libraries should 
> > be able to expect to continue to work - that we don't break binary 
> > compatibility for users using documented interfaces in the documented way.
> 
> I respect your view, but we did that for errno (and presumably _res and
> h_errno, but I have less experience with that) and the i386 ABI bump for
> SSE stack alignment.

As I said, starting point.  The basic presumption is against breaking any 
valid binaries; sometimes it may be rebutted, but that needs a compelling 
case to justify the breakage.

In the SSE stack alignment case I think we ought to be realigning the 
stack on entry to glibc at entry points that existed long enough ago and 
where SSE is used in a way that requires 16-byte alignment, as I said in 
<https://sourceware.org/ml/libc-alpha/2013-08/msg00372.html>.

It would, admittedly, be convenient if we could do the occasional global 
ABI transition with SONAME bump to limit how long compatibility is needed 
with problematic old ABIs - we've had libc.so.6 a lot longer than 
GNU/Linux had either libc.so.4 or libc.so.5.  But given how it seems to 
have been concluded that's not practical for libstdc++ I suppose it's not 
practical for libc either (on some architectures - the ARM move to EABI 
went fine).
  
Florian Weimer Oct. 15, 2015, 5:43 p.m. UTC | #20
On 10/15/2015 06:16 PM, Florian Weimer wrote:
> If we can detect that a binary references these symbols:
> 
> _IO_file_init@GLIBC_2.1
> _IO_init@GLIBC_2.0
> _IO_str_init_readonly@GLIBC_2.0
> _IO_str_init_static@GLIBC_2.0
> 
> we can disable vtable hardening globally.  This would not cover the
> scenario where the binary dlopens something that uses an old libstdc++
> (linked statically or dynamically) later, but it would provide
> compatibility where a copy of libstdc++ is pulled in through DT_NEEDED.

This just occurred to me:

If we use the array-of-vtables approach I mentioned earlier (a range
check on the vtable pointer, as opposed to PTR_MANGLE/PTR_DEMANGLE), we
could enable that until one of the functions list above is called.  As a
result, we would have both vtable hardening *and* full backwards
compatibility.

We just need a safe way to store the flag and reset it if needed (see
the previous thread, “Encoding page size in the ELF header” :-).  For a
start, we could simply use a global variable.

And the change adds more than one instruction, we should benchmark a few
functions like fread_unlocked or fflush_unlocked to see if it has
measurable impact.

Florian
  

Patch

diff --git a/debug/obprintf_chk.c b/debug/obprintf_chk.c
index c49d1e9..203b403 100644
--- a/debug/obprintf_chk.c
+++ b/debug/obprintf_chk.c
@@ -54,7 +54,7 @@  __obstack_vprintf_chk (struct obstack *obstack, int flags, const char *format,
 #endif
 
   _IO_no_init (&new_f.ofile.file.file, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&new_f.ofile.file) = &_IO_obstack_jumps;
+  _IO_JUMPS_SET (&new_f.ofile.file, &_IO_obstack_jumps);
   room = obstack_room (obstack);
   size = obstack_object_size (obstack) + room;
   if (size == 0)
diff --git a/debug/vasprintf_chk.c b/debug/vasprintf_chk.c
index 8ecb9e8..4779836 100644
--- a/debug/vasprintf_chk.c
+++ b/debug/vasprintf_chk.c
@@ -52,7 +52,7 @@  __vasprintf_chk (char **result_ptr, int flags, const char *format,
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, string, init_string_size, string);
   sf._sbf._f._flags &= ~_IO_USER_BUF;
   sf._s._allocate_buffer = (_IO_alloc_type) malloc;
diff --git a/debug/vdprintf_chk.c b/debug/vdprintf_chk.c
index bf43ed8..eb86c0b 100644
--- a/debug/vdprintf_chk.c
+++ b/debug/vdprintf_chk.c
@@ -38,7 +38,7 @@  __vdprintf_chk (int d, int flags, const char *format, va_list arg)
   tmpfil.file._lock = NULL;
 #endif
   _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
-  _IO_JUMPS (&tmpfil) = &_IO_file_jumps;
+  _IO_JUMPS_SET (&tmpfil, &_IO_file_jumps);
   _IO_file_init (&tmpfil);
 #if  !_IO_UNIFIED_JUMPTABLES
   tmpfil.vtable = NULL;
diff --git a/debug/vsnprintf_chk.c b/debug/vsnprintf_chk.c
index c6cb383..5b09121 100644
--- a/debug/vsnprintf_chk.c
+++ b/debug/vsnprintf_chk.c
@@ -51,7 +51,7 @@  ___vsnprintf_chk (char *s, size_t maxlen, int flags, size_t slen,
     }
 
   _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf.f._sbf) = &_IO_strn_jumps;
+  _IO_JUMPS_SET (&sf.f._sbf, &_IO_strn_jumps);
   s[0] = '\0';
 
   /* For flags > 0 (i.e. __USE_FORTIFY_LEVEL > 1) request that %n
diff --git a/debug/vsprintf_chk.c b/debug/vsprintf_chk.c
index 1ec67b5..3805788 100644
--- a/debug/vsprintf_chk.c
+++ b/debug/vsprintf_chk.c
@@ -73,7 +73,7 @@  ___vsprintf_chk (char *s, int flags, size_t slen, const char *format,
     __chk_fail ();
 
   _IO_no_init (&f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&f._sbf) = &_IO_str_chk_jumps;
+  _IO_JUMPS_SET (&f._sbf, &_IO_str_chk_jumps);
   s[0] = '\0';
   _IO_str_init_static_internal (&f, s, slen - 1, s);
 
diff --git a/libio/fileops.c b/libio/fileops.c
index cbcd6f5..1bfe883 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -414,7 +414,7 @@  _IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode,
 	    &result->_wide_data->_IO_state;
 
 	  /* From now on use the wide character callback functions.  */
-	  _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
+	  _IO_JUMPS_FILE_plus_SET (fp, _IO_WIDE_JUMPS_FUNC (fp));
 
 	  /* Set the mode now.  */
 	  result->_mode = 1;
@@ -466,8 +466,8 @@  _IO_file_setbuf_mmap (_IO_FILE *fp, char *p, _IO_ssize_t len)
   _IO_FILE *result;
 
   /* Change the function table.  */
-  _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
-  fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+  _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
+  _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
 
   /* And perform the normal operation.  */
   result = _IO_new_file_setbuf (fp, p, len);
@@ -475,8 +475,8 @@  _IO_file_setbuf_mmap (_IO_FILE *fp, char *p, _IO_ssize_t len)
   /* If the call failed, restore to using mmap.  */
   if (result == NULL)
     {
-      _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap;
-      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap;
+      _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps_mmap);
+      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps_mmap);
     }
 
   return result;
@@ -703,10 +703,10 @@  mmap_remap_check (_IO_FILE *fp)
       fp->_IO_buf_base = fp->_IO_buf_end = NULL;
       _IO_setg (fp, NULL, NULL, NULL);
       if (fp->_mode <= 0)
-	_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
       else
-	_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
-      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps);
+      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
 
       return 1;
     }
@@ -773,10 +773,10 @@  decide_maybe_mmap (_IO_FILE *fp)
 	      fp->_offset = st.st_size;
 
 	      if (fp->_mode <= 0)
-		_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap;
+		_IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps_mmap);
 	      else
-		_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_mmap;
-	      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap;
+		_IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps_mmap);
+	      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps_mmap);
 
 	      return;
 	    }
@@ -786,10 +786,10 @@  decide_maybe_mmap (_IO_FILE *fp)
   /* We couldn't use mmap, so revert to the vanilla file operations.  */
 
   if (fp->_mode <= 0)
-    _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+    _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
   else
-    _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
-  fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+    _IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps);
+  _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
 }
 
 int
diff --git a/libio/freopen.c b/libio/freopen.c
index 035fa1f..a9203fd 100644
--- a/libio/freopen.c
+++ b/libio/freopen.c
@@ -59,16 +59,16 @@  freopen (filename, mode, fp)
 	 to the old libio may be passed into shared C library and wind
 	 up here. */
       _IO_old_file_close_it (fp);
-      _IO_JUMPS_FILE_plus (fp) = &_IO_old_file_jumps;
+      _IO_JUMPS_FILE_plus_SET (fp, &_IO_old_file_jumps);
       result = _IO_old_file_fopen (fp, gfilename, mode);
     }
   else
 #endif
     {
       _IO_file_close_it (fp);
-      _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+      _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
       if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
-	fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+	_IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
       result = _IO_file_fopen (fp, gfilename, mode, 1);
       if (result != NULL)
 	result = __fopen_maybe_mmap (result);
diff --git a/libio/freopen64.c b/libio/freopen64.c
index fc6ccb1..eb883d6 100644
--- a/libio/freopen64.c
+++ b/libio/freopen64.c
@@ -50,9 +50,9 @@  freopen64 (filename, mode, fp)
 			   ? fd_to_filename (fd) : filename);
   fp->_flags2 |= _IO_FLAGS2_NOCLOSE;
   _IO_file_close_it (fp);
-  _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+  _IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps);
   if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
-    fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+    _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps);
   result = _IO_file_fopen (fp, gfilename, mode, 0);
   fp->_flags2 &= ~_IO_FLAGS2_NOCLOSE;
   if (result != NULL)
diff --git a/libio/genops.c b/libio/genops.c
index 45c9d41..acf7b79 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -662,7 +662,7 @@  _IO_no_init (fp, flags, orientation, wd, jmp)
       fp->_wide_data->_IO_backup_base = NULL;
       fp->_wide_data->_IO_save_end = NULL;
 
-      fp->_wide_data->_wide_vtable = jmp;
+      _IO_WIDE_JUMPS_SET (fp, jmp);
     }
   else
     /* Cause predictable crash when a wide function is called on a byte
diff --git a/libio/iofdopen.c b/libio/iofdopen.c
index e7d84ae..d272568 100644
--- a/libio/iofdopen.c
+++ b/libio/iofdopen.c
@@ -150,11 +150,11 @@  _IO_new_fdopen (fd, mode)
 	       ? &_IO_wfile_jumps_maybe_mmap :
 #endif
 	       &_IO_wfile_jumps);
-  _IO_JUMPS (&new_f->fp) =
+  _IO_JUMPS_SET (&new_f->fp,
 #ifdef _G_HAVE_MMAP
     (use_mmap && (read_write & _IO_NO_WRITES)) ? &_IO_file_jumps_maybe_mmap :
 #endif
-      &_IO_file_jumps;
+      &_IO_file_jumps);
   _IO_file_init (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/iofopen.c b/libio/iofopen.c
index be2bbb6..2d9fc41 100644
--- a/libio/iofopen.c
+++ b/libio/iofopen.c
@@ -46,10 +46,10 @@  __fopen_maybe_mmap (_IO_FILE *fp)
 	 vanilla file operations and reset the jump table accordingly.  */
 
       if (fp->_mode <= 0)
-	_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_maybe_mmap;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_file_jumps_maybe_mmap);
       else
-	_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_maybe_mmap;
-      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_maybe_mmap;
+	_IO_JUMPS_FILE_plus_SET (fp, &_IO_wfile_jumps_maybe_mmap);
+      _IO_WIDE_JUMPS_SET (fp, &_IO_wfile_jumps_maybe_mmap);
     }
 #endif
   return fp;
@@ -78,7 +78,7 @@  __fopen_internal (const char *filename, const char *mode, int is32)
 #else
   _IO_no_init (&new_f->fp.file, 1, 0, NULL, NULL);
 #endif
-  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
+  _IO_JUMPS_SET (&new_f->fp, &_IO_file_jumps);
   _IO_file_init (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/iofopncook.c b/libio/iofopncook.c
index 978a7fa..76285a5 100644
--- a/libio/iofopncook.c
+++ b/libio/iofopncook.c
@@ -145,7 +145,7 @@  _IO_cookie_init (struct _IO_cookie_file *cfile, int read_write,
 		 void *cookie, _IO_cookie_io_functions_t io_functions)
 {
   _IO_init (&cfile->__fp.file, 0);
-  _IO_JUMPS (&cfile->__fp) = &_IO_cookie_jumps;
+  _IO_JUMPS_SET (&cfile->__fp, &_IO_cookie_jumps);
 
   cfile->__cookie = cookie;
   cfile->__io_functions = io_functions;
@@ -270,7 +270,7 @@  _IO_old_fopencookie (cookie, mode, io_functions)
 
   ret = _IO_fopencookie (cookie, mode, io_functions);
   if (ret != NULL)
-    _IO_JUMPS_FILE_plus (ret) = &_IO_old_cookie_jumps;
+    _IO_JUMPS_FILE_plus_SET (ret, &_IO_old_cookie_jumps);
 
   return ret;
 }
diff --git a/libio/iofwide.c b/libio/iofwide.c
index 0c175d1..8d1c39a 100644
--- a/libio/iofwide.c
+++ b/libio/iofwide.c
@@ -184,7 +184,7 @@  _IO_fwide (fp, mode)
 #endif
 
       /* From now on use the wide character callback functions.  */
-      _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
+      _IO_JUMPS_FILE_plus_SET (fp, _IO_WIDE_JUMPS_FUNC (fp));
     }
 
   /* Set the mode now.  */
diff --git a/libio/iopopen.c b/libio/iopopen.c
index 53da776..b19e62f 100644
--- a/libio/iopopen.c
+++ b/libio/iopopen.c
@@ -293,7 +293,7 @@  _IO_new_popen (command, mode)
 #endif
   fp = &new_f->fpx.file.file;
   _IO_init (fp, 0);
-  _IO_JUMPS (&new_f->fpx.file) = &_IO_proc_jumps;
+  _IO_JUMPS_SET (&new_f->fpx.file, &_IO_proc_jumps);
   _IO_new_file_init (&new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
diff --git a/libio/iovdprintf.c b/libio/iovdprintf.c
index 116912a..e449443 100644
--- a/libio/iovdprintf.c
+++ b/libio/iovdprintf.c
@@ -41,7 +41,7 @@  _IO_vdprintf (d, format, arg)
   tmpfil.file._lock = NULL;
 #endif
   _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
-  _IO_JUMPS (&tmpfil) = &_IO_file_jumps;
+  _IO_JUMPS_SET (&tmpfil, &_IO_file_jumps);
   _IO_file_init (&tmpfil);
 #if  !_IO_UNIFIED_JUMPTABLES
   tmpfil.vtable = NULL;
diff --git a/libio/iovsprintf.c b/libio/iovsprintf.c
index b6cc6b3..e624b29 100644
--- a/libio/iovsprintf.c
+++ b/libio/iovsprintf.c
@@ -37,7 +37,7 @@  __IO_vsprintf (char *string, const char *format, _IO_va_list args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, string, -1, string);
   ret = _IO_vfprintf (&sf._sbf._f, format, args);
   _IO_putc_unlocked ('\0', &sf._sbf._f);
diff --git a/libio/iovsscanf.c b/libio/iovsscanf.c
index c4fbd1b..35330e2 100644
--- a/libio/iovsscanf.c
+++ b/libio/iovsscanf.c
@@ -39,7 +39,7 @@  _IO_vsscanf (string, format, args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, (char*)string, 0, NULL);
   ret = _IO_vfscanf (&sf._sbf._f, format, args, NULL);
   return ret;
diff --git a/libio/libioP.h b/libio/libioP.h
index b1ca774..ddcd2c9 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -70,17 +70,16 @@  extern "C" {
 
 /* THE JUMPTABLE FUNCTIONS.
 
- * The _IO_FILE type is used to implement the FILE type in GNU libc,
- * as well as the streambuf class in GNU iostreams for C++.
- * These are all the same, just used differently.
- * An _IO_FILE (or FILE) object is allows followed by a pointer to
- * a jump table (of pointers to functions).  The pointer is accessed
- * with the _IO_JUMPS macro.  The jump table has an eccentric format,
- * so as to be compatible with the layout of a C++ virtual function table.
- * (as implemented by g++).  When a pointer to a streambuf object is
- * coerced to an (_IO_FILE*), then _IO_JUMPS on the result just
- * happens to point to the virtual function table of the streambuf.
- * Thus the _IO_JUMPS function table used for C stdio/libio does
+ * The _IO_FILE type is used to implement the FILE type in GNU libc, as well
+ * as the streambuf class in GNU iostreams for C++. These are all the same,
+ * just used differently. An _IO_FILE (or FILE) object is allows followed by
+ * a pointer to a jump table (of pointers to functions). The pointer is
+ * accessed with the _IO_JUMPS_SET and _IO_JUMPS_FUNC macros. The jump table
+ * has an eccentric format, so as to be compatible with the layout of a C++
+ * virtual function table. (as implemented by g++). When a pointer to a
+ * streambuf object is coerced to an (_IO_FILE*), then _IO_JUMPS on the
+ * result just happens to point to the virtual function table of the
+ * streambuf. Thus the _IO_JUMPS function table used for C stdio/libio does
  * double duty as the virtual function table for C++ streambuf.
  *
  * The entries in the _IO_JUMPS function table (and hence also the
@@ -106,6 +105,14 @@  extern "C" {
 # define _IO_JUMPS_OFFSET 0
 #endif
 
+static inline void
+__mangle_vtable(const struct _IO_jump_t **vtable, const struct _IO_jump_t *table)
+{
+    struct _IO_jump_t *ptr = (struct _IO_jump_t *)table;
+    PTR_MANGLE (ptr);
+    *vtable = ptr;
+}
+
 /* Type of MEMBER in struct type TYPE.  */
 #define _IO_MEMBER_TYPE(TYPE, MEMBER) __typeof__ (((TYPE){}).MEMBER)
 
@@ -115,24 +122,41 @@  extern "C" {
   (*(_IO_MEMBER_TYPE (TYPE, MEMBER) *)(((char *) (THIS)) \
 				       + offsetof(TYPE, MEMBER)))
 
-#define _IO_JUMPS(THIS) (THIS)->vtable
-#define _IO_JUMPS_FILE_plus(THIS) \
+#define _IO_JUMPS_RAW(THIS) (THIS)->vtable
+#define _IO_JUMPS_SET(THIS, TABLE) \
+  __mangle_vtable (&_IO_JUMPS_RAW (THIS), (TABLE))
+
+#define _IO_JUMPS_FILE_plus_RAW(THIS) \
   _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE_plus, vtable)
-#define _IO_WIDE_JUMPS(THIS) \
+#define _IO_JUMPS_FILE_plus_SET(THIS, TABLE) \
+  __mangle_vtable (&_IO_JUMPS_FILE_plus_RAW (THIS), (TABLE))
+
+#define _IO_WIDE_JUMPS_RAW(THIS) \
   _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data)->_wide_vtable
+#define _IO_WIDE_JUMPS_SET(THIS, TABLE) \
+  __mangle_vtable (&_IO_WIDE_JUMPS_RAW (THIS), (TABLE))
+
 #define _IO_CHECK_WIDE(THIS) \
   (_IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data) != NULL)
 
+static inline const struct _IO_jump_t *
+__demangle_vtable(const struct _IO_jump_t *vtable)
+{
+    struct _IO_jump_t *ptr = (struct _IO_jump_t *)vtable;
+    PTR_DEMANGLE (ptr);
+    return (const struct _IO_jump_t *)ptr;
+}
+
 #if _IO_JUMPS_OFFSET
-# define _IO_JUMPS_FUNC(THIS) \
- (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
-			   + (THIS)->_vtable_offset))
+# define _IO_JUMPS_FUNC(THIS) __demangle_vtable (\
+ (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus_RAW (THIS) \
+              + (THIS)->_vtable_offset)))
 # define _IO_vtable_offset(THIS) (THIS)->_vtable_offset
 #else
-# define _IO_JUMPS_FUNC(THIS) _IO_JUMPS_FILE_plus (THIS)
+# define _IO_JUMPS_FUNC(THIS) __demangle_vtable (_IO_JUMPS_FILE_plus_RAW (THIS))
 # define _IO_vtable_offset(THIS) 0
 #endif
-#define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
+#define _IO_WIDE_JUMPS_FUNC(THIS) __demangle_vtable (_IO_WIDE_JUMPS_RAW (THIS))
 #define JUMP_FIELD(TYPE, NAME) TYPE NAME
 #define JUMP0(FUNC, THIS) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS)
 #define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
diff --git a/libio/memstream.c b/libio/memstream.c
index 84742d1..640740e 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -89,7 +89,7 @@  __open_memstream (bufloc, sizeloc)
       return NULL;
     }
   _IO_init (&new_f->fp._sf._sbf._f, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fp._sf._sbf) = &_IO_mem_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fp._sf._sbf, &_IO_mem_jumps);
   _IO_str_init_static_internal (&new_f->fp._sf, buf, _IO_BUFSIZ, buf);
   new_f->fp._sf._sbf._f._flags &= ~_IO_USER_BUF;
   new_f->fp._sf._s._allocate_buffer = (_IO_alloc_type) malloc;
diff --git a/libio/obprintf.c b/libio/obprintf.c
index d61de6a..b166362 100644
--- a/libio/obprintf.c
+++ b/libio/obprintf.c
@@ -132,7 +132,7 @@  _IO_obstack_vprintf (struct obstack *obstack, const char *format, va_list args)
 #endif
 
   _IO_no_init (&new_f.ofile.file.file, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&new_f.ofile.file) = &_IO_obstack_jumps;
+  _IO_JUMPS_SET (&new_f.ofile.file, &_IO_obstack_jumps);
   room = obstack_room (obstack);
   size = obstack_object_size (obstack) + room;
   if (size == 0)
diff --git a/libio/oldiofdopen.c b/libio/oldiofdopen.c
index e068ec7..5a571b5 100644
--- a/libio/oldiofdopen.c
+++ b/libio/oldiofdopen.c
@@ -113,7 +113,7 @@  _IO_old_fdopen (fd, mode)
   new_f->fp.file._file._lock = &new_f->lock;
 #endif
   _IO_old_init (&new_f->fp.file._file, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fp) = &_IO_old_file_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fp, &_IO_old_file_jumps);
   _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/oldiofopen.c b/libio/oldiofopen.c
index a90a601..0b27b00 100644
--- a/libio/oldiofopen.c
+++ b/libio/oldiofopen.c
@@ -52,7 +52,7 @@  _IO_old_fopen (filename, mode)
   new_f->fp.file._file._lock = &new_f->lock;
 #endif
   _IO_old_init (&new_f->fp.file._file, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fp) = &_IO_old_file_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fp, &_IO_old_file_jumps);
   _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
diff --git a/libio/oldiopopen.c b/libio/oldiopopen.c
index fb4c7b8..9d0b817 100644
--- a/libio/oldiopopen.c
+++ b/libio/oldiopopen.c
@@ -215,7 +215,7 @@  _IO_old_popen (command, mode)
 #endif
   fp = &new_f->fpx.file.file._file;
   _IO_old_init (fp, 0);
-  _IO_JUMPS_FILE_plus (&new_f->fpx.file) = &_IO_old_proc_jumps;
+  _IO_JUMPS_FILE_plus_SET (&new_f->fpx.file, &_IO_old_proc_jumps);
   _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
diff --git a/libio/vasprintf.c b/libio/vasprintf.c
index 7f9c105..96e1d73 100644
--- a/libio/vasprintf.c
+++ b/libio/vasprintf.c
@@ -54,7 +54,7 @@  _IO_vasprintf (result_ptr, format, args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, string, init_string_size, string);
   sf._sbf._f._flags &= ~_IO_USER_BUF;
   sf._s._allocate_buffer = (_IO_alloc_type) malloc;
diff --git a/libio/vsnprintf.c b/libio/vsnprintf.c
index e2752d8..66f03cf 100644
--- a/libio/vsnprintf.c
+++ b/libio/vsnprintf.c
@@ -113,7 +113,7 @@  _IO_vsnprintf (string, maxlen, format, args)
     }
 
   _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf.f._sbf) = &_IO_strn_jumps;
+  _IO_JUMPS_SET (&sf.f._sbf, &_IO_strn_jumps);
   string[0] = '\0';
   _IO_str_init_static_internal (&sf.f, string, maxlen - 1, string);
   ret = _IO_vfprintf (&sf.f._sbf._f, format, args);
diff --git a/misc/init-misc.c b/misc/init-misc.c
index 9254f02..bcf26a2 100644
--- a/misc/init-misc.c
+++ b/misc/init-misc.c
@@ -16,7 +16,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include "libioP.h"
+
 #include <string.h>
+#include <stdio.h>
+#include <sysdep.h>
 #include <libc-internal.h>
 
 char *__progname_full = (char *) "";
@@ -37,4 +41,13 @@  __init_misc (int argc, char **argv, char **envp)
 	__progname = p + 1;
       __progname_full = argv[0];
     }
+
+  PTR_MANGLE (_IO_JUMPS_FILE_plus_RAW (stdin));
+  PTR_MANGLE (_IO_WIDE_JUMPS_RAW (stdin));
+
+  PTR_MANGLE (_IO_JUMPS_FILE_plus_RAW (stdout));
+  PTR_MANGLE (_IO_WIDE_JUMPS_RAW (stdout));
+
+  PTR_MANGLE (_IO_JUMPS_FILE_plus_RAW (stderr));
+  PTR_MANGLE (_IO_WIDE_JUMPS_RAW (stderr));
 }
diff --git a/stdio-common/isoc99_vsscanf.c b/stdio-common/isoc99_vsscanf.c
index dadd125..513e065 100644
--- a/stdio-common/isoc99_vsscanf.c
+++ b/stdio-common/isoc99_vsscanf.c
@@ -37,7 +37,7 @@  __isoc99_vsscanf (const char *string, const char *format, _IO_va_list args)
   sf._sbf._f._lock = NULL;
 #endif
   _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
-  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
+  _IO_JUMPS_SET (&sf._sbf, &_IO_str_jumps);
   _IO_str_init_static_internal (&sf, (char*)string, 0, NULL);
   sf._sbf._f._flags2 |= _IO_FLAGS2_SCANF_STD;
   ret = _IO_vfscanf (&sf._sbf._f, format, args, NULL);
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 5e408d2..5940887 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -2343,7 +2343,7 @@  buffered_vfprintf (_IO_FILE *s, const CHAR_T *format,
   hp->_lock = NULL;
 #endif
   hp->_flags2 = s->_flags2;
-  _IO_JUMPS (&helper._f) = (struct _IO_jump_t *) &_IO_helper_jumps;
+  _IO_JUMPS_SET (&helper._f, (struct _IO_jump_t *) &_IO_helper_jumps);
 
   /* Now print to helper instead.  */
 #ifndef COMPILE_WPRINTF
diff --git a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c
index 3fae78b..ba734da 100644
--- a/stdlib/strfmon_l.c
+++ b/stdlib/strfmon_l.c
@@ -516,7 +516,7 @@  __vstrfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format,
       f._sbf._f._lock = NULL;
 #endif
       _IO_init (&f._sbf._f, 0);
-      _IO_JUMPS (&f._sbf) = &_IO_str_jumps;
+      _IO_JUMPS_SET (&f._sbf, &_IO_str_jumps);
       _IO_str_init_static_internal (&f, dest, (s + maxsize) - dest, dest);
       /* We clear the last available byte so we can find out whether
 	 the numeric representation is too long.  */