This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH v4] libio: use PTR_MANGLE/PTR_DEMANGLE for FILE vtables
- From: Kees Cook <keescook at chromium dot org>
- To: libc-alpha at sourceware dot org
- Cc: Mike Frysinger <vapier at gentoo dot org>, Florian Weimer <fweimer at redhat dot com>, Adam Conrad <adconrad at 0c3 dot net>, Joseph Myers <joseph at codesourcery dot com>, Yunlian Jiang <yunlian at google dot com>
- Date: Wed, 25 May 2016 14:09:03 -0700
- Subject: [PATCH v4] libio: use PTR_MANGLE/PTR_DEMANGLE for FILE vtables
- Authentication-results: sourceware.org; auth=none
Hi!
This is a continuation of an earlier discussion[1] about hardening the
FILE vtable with PTR_MANGLE, which previously ended with the request that
a new configure variable be used to control it[2]. This series now carries
the new "--disable-libio-compatibility" configure flag to break libio
compat with pre-roughly-2007 binaries using libstdc++.so.5 and earlier to
gain the new PTR_MANGLE code.
Running the glibc test suite shows no regressions for me.
(FWIW, this patch has seen a lot of use, as it has been in Chrome OS
for over 3 years now.)
-Kees
[1] https://sourceware.org/ml/libc-alpha/2015-10/msg00009.html
[2] https://sourceware.org/ml/libc-alpha/2015-10/msg00057.html
v4:
- keep compat by default (fw).
v3:
- adjust whitespace, rename mangle/demangle inlines (vapier).
v2:
- added "--enable-libio-compatibility" (fw).
v1:
- URL [1] above
---
2016-05-23 Kees Cook <keescook@chromium.org>
* configure.ac (AC_ARG_ENABLE(libio-compatibility)): New configure
flag.
* config.h.in: add USE_COMPAT_LIBIO
* configure: Regenerate.
* manual/install.texi (--disable-libio-compatibility): Document new
flag.
* INSTALL: Regenerate.
* libio/libioP.h: Create inline helpers to run the PTR_MANGLE and
PTR_DEMANGLE routines on vtable pointers when USE_COMPAT_LIBIO
is not set. 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.
---
INSTALL | 6 ++++
config.h.in | 3 ++
configure | 16 +++++++++++
configure.ac | 11 +++++++
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 | 67 +++++++++++++++++++++++++++++++------------
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 +-
manual/install.texi | 5 ++++
misc/init-misc.c | 15 ++++++++++
stdio-common/isoc99_vsscanf.c | 2 +-
stdio-common/vfprintf.c | 2 +-
stdlib/strfmon_l.c | 2 +-
34 files changed, 152 insertions(+), 67 deletions(-)
diff --git a/INSTALL b/INSTALL
index 31e256d..99f47dc 100644
--- a/INSTALL
+++ b/INSTALL
@@ -116,6 +116,12 @@ will be used, and CFLAGS sets optimization options for the compiler.
program linked statically with the NSS libraries cannot be
dynamically reconfigured to use a different name database.
+'--disable-libio-compatibility'
+ Instead of remaining compatibile with pre-libstdc++.so.6 binaries
+ that may be sharing the vtable (i.e. roughly pre-2007 binaries),
+ compile libio with PTR_MANGLE of the FILE vtable, as a security
+ flaw mitigation.
+
'--without-tls'
By default the C library is built with support for thread-local
storage if the used tools support it. By using '--without-tls'
diff --git a/config.h.in b/config.h.in
index 0147ba3..9a7894b 100644
--- a/config.h.in
+++ b/config.h.in
@@ -161,6 +161,9 @@
/* Define if the dynamic linker should consult an ld.so.cache file. */
#undef USE_LDCONFIG
+/* Define if we should remain compatibile with pre-libstdc++.so.6 libio. */
+#undef USE_COMPAT_LIBIO
+
/* Define to 1 if STT_GNU_IFUNC support actually works. */
#define HAVE_IFUNC 0
diff --git a/configure b/configure
index 8fe5937..5ffc25a 100755
--- a/configure
+++ b/configure
@@ -761,6 +761,7 @@ enable_add_ons
enable_hidden_plt
enable_bind_now
enable_static_nss
+enable_libio_compatibility
enable_force_install
enable_maintainer_mode
enable_kernel
@@ -1422,6 +1423,9 @@ Optional Features:
--disable-hidden-plt do not hide internal function calls to avoid PLT
--enable-bind-now disable lazy relocations in DSOs
--enable-static-nss build static NSS modules [default=no]
+ --disable-libio-compatibility
+ enable PTR_MANGLE for FILE vtable (breaks
+ pre-libstdc++.so.6 binaries)
--disable-force-install don't force installation of files from this package,
even if they are older than the installed files
--enable-maintainer-mode
@@ -3430,6 +3434,18 @@ if test x"$static_nss" = xyes || test x"$shared" = xno; then
fi
+# Check whether --enable-libio-compatibility was given.
+if test "${enable_libio_compatibility+set}" = set; then :
+ enableval=$enable_libio_compatibility; libio_compatibility=$enableval
+else
+ libio_compatibility=yes
+fi
+
+if test "$libio_compatibility" = yes; then
+ $as_echo "#define USE_COMPAT_LIBIO 1" >>confdefs.h
+
+fi
+
# Check whether --enable-force-install was given.
if test "${enable_force_install+set}" = set; then :
enableval=$enable_force_install; force_install=$enableval
diff --git a/configure.ac b/configure.ac
index 3c766b7..e184265 100644
--- a/configure.ac
+++ b/configure.ac
@@ -245,6 +245,17 @@ if test x"$static_nss" = xyes || test x"$shared" = xno; then
AC_DEFINE(DO_STATIC_NSS)
fi
+dnl To enable PTR_MANGLE on the FILE vtable, we break compatibility with
+dnl pre-libstdc++.so.6 libio.
+AC_ARG_ENABLE([libio-compatibility],
+ AC_HELP_STRING([--disable-libio-compatibility],
+ [enable PTR_MANGLE for FILE vtable (breaks pre-libstdc++.so.6 binaries)]),
+ [libio_compatibility=$enableval],
+ [libio_compatibility=yes])
+if test "$libio_compatibility" = yes; then
+ AC_DEFINE(USE_COMPAT_LIBIO)
+fi
+
AC_ARG_ENABLE([force-install],
AC_HELP_STRING([--disable-force-install],
[don't force installation of files from this package, even if they are older than the installed files]),
diff --git a/debug/obprintf_chk.c b/debug/obprintf_chk.c
index 8469b5f..77ed19f 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 cb1f74a..db81a47 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 05d0bcd..1daeb4c 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 cc559d2..2a91815 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 aa1587c..f6605e2 100644
--- a/debug/vsprintf_chk.c
+++ b/debug/vsprintf_chk.c
@@ -71,7 +71,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 8e83b1c..bdf30e6 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 8a2a417..466cce6 100644
--- a/libio/freopen.c
+++ b/libio/freopen.c
@@ -56,16 +56,16 @@ freopen (const char *filename, const char *mode, FILE *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 ba85c3e..7eccb3e 100644
--- a/libio/freopen64.c
+++ b/libio/freopen64.c
@@ -47,9 +47,9 @@ freopen64 (const char *filename, const char *mode, FILE *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 5803cbf..8542f78 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -615,7 +615,7 @@ _IO_no_init (_IO_FILE *fp, int flags, int orientation,
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 e00f337..8633055 100644
--- a/libio/iofdopen.c
+++ b/libio/iofdopen.c
@@ -148,11 +148,11 @@ _IO_new_fdopen (int fd, const char *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 13e3910..6195fec 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 9eda7c1..c745e0c 100644
--- a/libio/iofopncook.c
+++ b/libio/iofopncook.c
@@ -131,7 +131,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;
@@ -249,7 +249,7 @@ _IO_old_fopencookie (void *cookie, const char *mode,
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 bc82e8b..9137d77 100644
--- a/libio/iofwide.c
+++ b/libio/iofwide.c
@@ -182,7 +182,7 @@ _IO_fwide (_IO_FILE *fp, int 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 9ddde23..6029c71 100644
--- a/libio/iopopen.c
+++ b/libio/iopopen.c
@@ -288,7 +288,7 @@ _IO_new_popen (const char *command, const char *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 8ca55fc..f3dcb9a 100644
--- a/libio/iovdprintf.c
+++ b/libio/iovdprintf.c
@@ -38,7 +38,7 @@ _IO_vdprintf (int d, const char *format, _IO_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/libio/iovsprintf.c b/libio/iovsprintf.c
index 712d178..6baa376 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 18d9aaa..07db19a 100644
--- a/libio/iovsscanf.c
+++ b/libio/iovsscanf.c
@@ -36,7 +36,7 @@ _IO_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);
ret = _IO_vfscanf (&sf._sbf._f, format, args, NULL);
return ret;
diff --git a/libio/libioP.h b/libio/libioP.h
index 8706af2..3efdde8 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 always 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,17 @@ extern "C" {
# define _IO_JUMPS_OFFSET 0
#endif
+static inline void
+_IO_mangle_vtable (const struct _IO_jump_t **vtable,
+ const struct _IO_jump_t *table)
+{
+ struct _IO_jump_t *ptr = (struct _IO_jump_t *)table;
+#ifndef USE_COMPAT_LIBIO
+ PTR_MANGLE (ptr);
+#endif
+ *vtable = ptr;
+}
+
/* Type of MEMBER in struct type TYPE. */
#define _IO_MEMBER_TYPE(TYPE, MEMBER) __typeof__ (((TYPE){}).MEMBER)
@@ -115,24 +125,43 @@ 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) \
+ _IO_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) \
+ _IO_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) \
+ _IO_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 *
+_IO_demangle_vtable (const struct _IO_jump_t *vtable)
+{
+ struct _IO_jump_t *ptr = (struct _IO_jump_t *)vtable;
+#ifndef USE_COMPAT_LIBIO
+ PTR_DEMANGLE (ptr);
+#endif
+ 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) _IO_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) _IO_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) _IO_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 7fa5245..6ad4355 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -87,7 +87,7 @@ __open_memstream (char **bufloc, _IO_size_t *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 aa17b46..1452b8d 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 33406ff..b99e39f 100644
--- a/libio/oldiofdopen.c
+++ b/libio/oldiofdopen.c
@@ -111,7 +111,7 @@ _IO_old_fdopen (int fd, const char *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 cc7c342..89cb848 100644
--- a/libio/oldiofopen.c
+++ b/libio/oldiofopen.c
@@ -50,7 +50,7 @@ _IO_old_fopen (const char *filename, const char *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 ea75b4f..dec2b3c 100644
--- a/libio/oldiopopen.c
+++ b/libio/oldiopopen.c
@@ -210,7 +210,7 @@ _IO_old_popen (const char *command, const char *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 7460f1e..146bd36 100644
--- a/libio/vasprintf.c
+++ b/libio/vasprintf.c
@@ -51,7 +51,7 @@ _IO_vasprintf (char **result_ptr, 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, 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 f1063a1..75a15a9 100644
--- a/libio/vsnprintf.c
+++ b/libio/vsnprintf.c
@@ -108,7 +108,7 @@ _IO_vsnprintf (char *string, _IO_size_t maxlen, const char *format,
}
_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/manual/install.texi b/manual/install.texi
index 95021b4..093b3fd 100644
--- a/manual/install.texi
+++ b/manual/install.texi
@@ -146,6 +146,11 @@ This is not recommended because it defeats the purpose of NSS; a program
linked statically with the NSS libraries cannot be dynamically
reconfigured to use a different name database.
+@item --disable-libio-compatibility
+Instead of remaining compatibile with pre-@file{libstdc++.so.6} binaries
+that may be sharing the vtable (i.e. roughly pre-2007 binaries), compile
+libio with PTR_MANGLE of the FILE vtable, as a security flaw mitigation.
+
@item --without-tls
By default the C library is built with support for thread-local storage
if the used tools support it. By using @samp{--without-tls} this can be
diff --git a/misc/init-misc.c b/misc/init-misc.c
index a9bf1da..7491500 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,15 @@ __init_misc (int argc, char **argv, char **envp)
__progname = p + 1;
__progname_full = argv[0];
}
+
+#ifndef USE_COMPAT_LIBIO
+ 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));
+#endif
}
diff --git a/stdio-common/isoc99_vsscanf.c b/stdio-common/isoc99_vsscanf.c
index 720f122..edbcf72 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 f24020a..392b48d 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -2318,7 +2318,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 5851a5b..5efaab0 100644
--- a/stdlib/strfmon_l.c
+++ b/stdlib/strfmon_l.c
@@ -513,7 +513,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. */
--
2.6.3
--
Kees Cook
Chrome OS & Brillo Security