This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH v4] libio: use PTR_MANGLE/PTR_DEMANGLE for FILE vtables


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]