fa3bfd
commit db3476aff19b75c4fdefbe65fcd5f0a90588ba51
fa3bfd
Author: Florian Weimer <fweimer@redhat.com>
fa3bfd
Date:   Thu Jun 23 20:01:40 2016 +0200
fa3bfd
fa3bfd
    libio: Implement vtable verification [BZ #20191]
fa3bfd
    
fa3bfd
    This commit puts all libio vtables in a dedicated, read-only ELF
fa3bfd
    section, so that they are consecutive in memory.  Before any indirect
fa3bfd
    jump, the vtable pointer is checked against the section boundaries,
fa3bfd
    and the process is terminated if the vtable pointer does not fall into
fa3bfd
    the special ELF section.
fa3bfd
    
fa3bfd
    To enable backwards compatibility, a special flag variable
fa3bfd
    (_IO_accept_foreign_vtables), protected by the pointer guard, avoids
fa3bfd
    process termination if libio stream object constructor functions have
fa3bfd
    been called earlier.  Such constructor functions are called by the GCC
fa3bfd
    2.95 libstdc++ library, and this mechanism ensures compatibility with
fa3bfd
    old binaries.  Existing callers inside glibc of these functions are
fa3bfd
    adjusted to call the original functions, not the wrappers which enable
fa3bfd
    vtable compatiblity.
fa3bfd
    
fa3bfd
    The compatibility mechanism is used to enable passing FILE * objects
fa3bfd
    across a static dlopen boundary, too.
fa3bfd
fa3bfd
diff -rupN a/Makerules b/Makerules
fa3bfd
--- a/Makerules	2017-08-24 14:50:27.000000000 -0400
fa3bfd
+++ b/Makerules	2017-08-24 14:56:40.194819878 -0400
fa3bfd
@@ -520,6 +520,9 @@ $(common-objpfx)shlib.lds: $(common-objp
fa3bfd
 		 PROVIDE(__start___libc_thread_subfreeres = .);\
fa3bfd
 		 __libc_thread_subfreeres : { *(__libc_thread_subfreeres) }\
fa3bfd
 		 PROVIDE(__stop___libc_thread_subfreeres = .);\
fa3bfd
+		 PROVIDE(__start___libc_IO_vtables = .);\
fa3bfd
+		 __libc_IO_vtables : { *(__libc_IO_vtables) }\
fa3bfd
+		 PROVIDE(__stop___libc_IO_vtables = .);\
fa3bfd
 		 /DISCARD/ : { *(.gnu.glibc-stub.*) }@'
fa3bfd
 	test -s $@T
fa3bfd
 	mv -f $@T $@
fa3bfd
diff -rupN a/debug/obprintf_chk.c b/debug/obprintf_chk.c
fa3bfd
--- a/debug/obprintf_chk.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/debug/obprintf_chk.c	2017-08-24 14:56:40.194819878 -0400
fa3bfd
@@ -35,7 +35,7 @@ struct _IO_obstack_file
fa3bfd
   struct obstack *obstack;
fa3bfd
 };
fa3bfd
 
fa3bfd
-extern const struct _IO_jump_t _IO_obstack_jumps attribute_hidden;
fa3bfd
+extern const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden;
fa3bfd
 
fa3bfd
 int
fa3bfd
 __obstack_vprintf_chk (struct obstack *obstack, int flags, const char *format,
fa3bfd
diff -rupN a/debug/vdprintf_chk.c b/debug/vdprintf_chk.c
fa3bfd
--- a/debug/vdprintf_chk.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/debug/vdprintf_chk.c	2017-08-24 14:56:40.194819878 -0400
fa3bfd
@@ -39,7 +39,7 @@ __vdprintf_chk (int d, int flags, const
fa3bfd
 #endif
fa3bfd
   _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
fa3bfd
   _IO_JUMPS (&tmpfil) = &_IO_file_jumps;
fa3bfd
-  _IO_file_init (&tmpfil);
fa3bfd
+  _IO_new_file_init_internal (&tmpfil);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   tmpfil.vtable = NULL;
fa3bfd
 #endif
fa3bfd
diff -rupN a/debug/vsnprintf_chk.c b/debug/vsnprintf_chk.c
fa3bfd
--- a/debug/vsnprintf_chk.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/debug/vsnprintf_chk.c	2017-08-24 14:56:40.194819878 -0400
fa3bfd
@@ -20,7 +20,7 @@
fa3bfd
 #include "../libio/libioP.h"
fa3bfd
 #include "../libio/strfile.h"
fa3bfd
 
fa3bfd
-extern const struct _IO_jump_t _IO_strn_jumps attribute_hidden;
fa3bfd
+extern const struct _IO_jump_t _IO_strn_jumps libio_vtable attribute_hidden;
fa3bfd
 
fa3bfd
 /* Write formatted output into S, according to the format
fa3bfd
    string FORMAT, writing no more than MAXLEN characters.  */
fa3bfd
diff -rupN a/debug/vsprintf_chk.c b/debug/vsprintf_chk.c
fa3bfd
--- a/debug/vsprintf_chk.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/debug/vsprintf_chk.c	2017-08-24 14:56:40.194819878 -0400
fa3bfd
@@ -34,7 +34,7 @@ _IO_str_chk_overflow (fp, c)
fa3bfd
 }
fa3bfd
 
fa3bfd
 
fa3bfd
-static const struct _IO_jump_t _IO_str_chk_jumps =
fa3bfd
+static const struct _IO_jump_t _IO_str_chk_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_str_finish),
fa3bfd
diff -rupN a/libio/Makefile b/libio/Makefile
fa3bfd
--- a/libio/Makefile	2017-08-24 14:50:22.000000000 -0400
fa3bfd
+++ b/libio/Makefile	2017-08-24 17:29:03.754025266 -0400
fa3bfd
@@ -44,7 +44,7 @@ routines	:=							      \
fa3bfd
 	__fbufsize __freading __fwriting __freadable __fwritable __flbf	      \
fa3bfd
 	__fpurge __fpending __fsetlocking				      \
fa3bfd
 									      \
fa3bfd
-	libc_fatal fmemopen
fa3bfd
+	libc_fatal fmemopen vtables
fa3bfd
 
fa3bfd
 include ../Makeconfig
fa3bfd
 
fa3bfd
diff -rupN a/libio/fileops.c b/libio/fileops.c
fa3bfd
--- a/libio/fileops.c	2017-08-24 14:50:23.000000000 -0400
fa3bfd
+++ b/libio/fileops.c	2017-08-24 14:57:57.765902179 -0400
fa3bfd
@@ -139,7 +139,7 @@ extern struct __gconv_trans_data __libio
fa3bfd
 
fa3bfd
 
fa3bfd
 void
fa3bfd
-_IO_new_file_init (fp)
fa3bfd
+_IO_new_file_init_internal (fp)
fa3bfd
      struct _IO_FILE_plus *fp;
fa3bfd
 {
fa3bfd
   /* POSIX.1 allows another file handle to be used to change the position
fa3bfd
@@ -151,7 +151,15 @@ _IO_new_file_init (fp)
fa3bfd
   _IO_link_in (fp);
fa3bfd
   fp->file._fileno = -1;
fa3bfd
 }
fa3bfd
-libc_hidden_ver (_IO_new_file_init, _IO_file_init)
fa3bfd
+
fa3bfd
+/* External version of _IO_new_file_init_internal which switches off
fa3bfd
+   vtable validation.  */
fa3bfd
+void
fa3bfd
+_IO_new_file_init (struct _IO_FILE_plus *fp)
fa3bfd
+{
fa3bfd
+  IO_set_accept_foreign_vtables (&_IO_vtable_check);
fa3bfd
+  _IO_new_file_init_internal (fp);
fa3bfd
+}
fa3bfd
 
fa3bfd
 int
fa3bfd
 _IO_new_file_close_it (fp)
fa3bfd
@@ -1586,7 +1594,7 @@ versioned_symbol (libc, _IO_new_file_wri
fa3bfd
 versioned_symbol (libc, _IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2_1);
fa3bfd
 #endif
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_file_jumps =
fa3bfd
+const struct _IO_jump_t _IO_file_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_file_finish),
fa3bfd
@@ -1611,7 +1619,7 @@ const struct _IO_jump_t _IO_file_jumps =
fa3bfd
 };
fa3bfd
 libc_hidden_data_def (_IO_file_jumps)
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_file_jumps_mmap =
fa3bfd
+const struct _IO_jump_t _IO_file_jumps_mmap libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_file_finish),
fa3bfd
@@ -1635,7 +1643,7 @@ const struct _IO_jump_t _IO_file_jumps_m
fa3bfd
   JUMP_INIT(imbue, _IO_default_imbue)
fa3bfd
 };
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_file_jumps_maybe_mmap =
fa3bfd
+const struct _IO_jump_t _IO_file_jumps_maybe_mmap libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_file_finish),
fa3bfd
diff -rupN a/libio/genops.c b/libio/genops.c
fa3bfd
--- a/libio/genops.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/genops.c	2017-08-24 15:44:28.757584527 -0400
fa3bfd
@@ -595,13 +595,19 @@ _IO_default_doallocate (fp)
fa3bfd
 libc_hidden_def (_IO_default_doallocate)
fa3bfd
 
fa3bfd
 void
fa3bfd
-_IO_init (fp, flags)
fa3bfd
+_IO_init_internal (fp, flags)
fa3bfd
      _IO_FILE *fp;
fa3bfd
      int flags;
fa3bfd
 {
fa3bfd
   _IO_no_init (fp, flags, -1, NULL, NULL);
fa3bfd
 }
fa3bfd
-libc_hidden_def (_IO_init)
fa3bfd
+
fa3bfd
+void
fa3bfd
+_IO_init (_IO_FILE *fp, int flags)
fa3bfd
+{
fa3bfd
+  IO_set_accept_foreign_vtables (&_IO_vtable_check);
fa3bfd
+  _IO_init_internal (fp, flags);
fa3bfd
+}
fa3bfd
 
fa3bfd
 void
fa3bfd
 _IO_old_init (fp, flags)
fa3bfd
diff -rupN a/libio/iofdopen.c b/libio/iofdopen.c
fa3bfd
--- a/libio/iofdopen.c	2017-08-24 14:50:21.000000000 -0400
fa3bfd
+++ b/libio/iofdopen.c	2017-08-24 14:56:40.220819906 -0400
fa3bfd
@@ -158,15 +158,15 @@ _IO_new_fdopen (fd, mode)
fa3bfd
     (use_mmap && (read_write & _IO_NO_WRITES)) ? &_IO_file_jumps_maybe_mmap :
fa3bfd
 #endif
fa3bfd
       &_IO_file_jumps;
fa3bfd
-  _IO_file_init (&new_f->fp);
fa3bfd
+  _IO_new_file_init_internal (&new_f->fp);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   new_f->fp.vtable = NULL;
fa3bfd
 #endif
fa3bfd
-  /* We only need to record the fd because _IO_file_init will have unset the
fa3bfd
-     offset.  It is important to unset the cached offset because the real
fa3bfd
-     offset in the file could change between now and when the handle is
fa3bfd
-     activated and we would then mislead ftell into believing that we have a
fa3bfd
-     valid offset.  */
fa3bfd
+  /* We only need to record the fd because _IO_file_init_internal will
fa3bfd
+     have unset the offset.  It is important to unset the cached
fa3bfd
+     offset because the real offset in the file could change between
fa3bfd
+     now and when the handle is activated and we would then mislead
fa3bfd
+     ftell into believing that we have a valid offset.  */
fa3bfd
   new_f->fp.file._fileno = fd;
fa3bfd
   new_f->fp.file._flags &= ~_IO_DELETE_DONT_CLOSE;
fa3bfd
 
fa3bfd
diff -rupN a/libio/iofopen.c b/libio/iofopen.c
fa3bfd
--- a/libio/iofopen.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/iofopen.c	2017-08-24 14:56:40.223819909 -0400
fa3bfd
@@ -83,7 +83,7 @@ __fopen_internal (filename, mode, is32)
fa3bfd
   _IO_no_init (&new_f->fp.file, 1, 0, NULL, NULL);
fa3bfd
 #endif
fa3bfd
   _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
fa3bfd
-  _IO_file_init (&new_f->fp);
fa3bfd
+  _IO_new_file_init_internal (&new_f->fp);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   new_f->fp.vtable = NULL;
fa3bfd
 #endif
fa3bfd
diff -rupN a/libio/iofopncook.c b/libio/iofopncook.c
fa3bfd
--- a/libio/iofopncook.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/iofopncook.c	2017-08-24 15:46:49.061694225 -0400
fa3bfd
@@ -116,7 +116,7 @@ _IO_cookie_seekoff (fp, offset, dir, mod
fa3bfd
 }
fa3bfd
 
fa3bfd
 
fa3bfd
-static const struct _IO_jump_t _IO_cookie_jumps = {
fa3bfd
+static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_file_finish),
fa3bfd
   JUMP_INIT(overflow, _IO_file_overflow),
fa3bfd
@@ -144,13 +144,13 @@ void
fa3bfd
 _IO_cookie_init (struct _IO_cookie_file *cfile, int read_write,
fa3bfd
 		 void *cookie, _IO_cookie_io_functions_t io_functions)
fa3bfd
 {
fa3bfd
-  _IO_init (&cfile->__fp.file, 0);
fa3bfd
+  _IO_init_internal (&cfile->__fp.file, 0);
fa3bfd
   _IO_JUMPS (&cfile->__fp) = &_IO_cookie_jumps;
fa3bfd
 
fa3bfd
   cfile->__cookie = cookie;
fa3bfd
   cfile->__io_functions = io_functions;
fa3bfd
 
fa3bfd
-  _IO_file_init (&cfile->__fp);
fa3bfd
+  _IO_new_file_init_internal (&cfile->__fp);
fa3bfd
 
fa3bfd
   _IO_mask_flags (&cfile->__fp.file, read_write,
fa3bfd
 		  _IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
fa3bfd
@@ -235,7 +235,7 @@ _IO_old_cookie_seek (fp, offset, dir)
fa3bfd
   return (ret == -1) ? _IO_pos_BAD : ret;
fa3bfd
 }
fa3bfd
 
fa3bfd
-static const struct _IO_jump_t _IO_old_cookie_jumps = {
fa3bfd
+static const struct _IO_jump_t _IO_old_cookie_jumps libio_vtable = {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_file_finish),
fa3bfd
   JUMP_INIT(overflow, _IO_file_overflow),
fa3bfd
diff -rupN a/libio/iopopen.c b/libio/iopopen.c
fa3bfd
--- a/libio/iopopen.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/iopopen.c	2017-08-24 14:56:40.234819921 -0400
fa3bfd
@@ -292,9 +292,9 @@ _IO_new_popen (command, mode)
fa3bfd
   new_f->fpx.file.file._lock = &new_f->lock;
fa3bfd
 #endif
fa3bfd
   fp = &new_f->fpx.file.file;
fa3bfd
-  _IO_init (fp, 0);
fa3bfd
+  _IO_init_internal (fp, 0);
fa3bfd
   _IO_JUMPS (&new_f->fpx.file) = &_IO_proc_jumps;
fa3bfd
-  _IO_new_file_init (&new_f->fpx.file);
fa3bfd
+  _IO_new_file_init_internal (&new_f->fpx.file);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   new_f->fpx.file.vtable = NULL;
fa3bfd
 #endif
fa3bfd
@@ -350,7 +350,7 @@ _IO_new_proc_close (fp)
fa3bfd
   return wstatus;
fa3bfd
 }
fa3bfd
 
fa3bfd
-static const struct _IO_jump_t _IO_proc_jumps = {
fa3bfd
+static const struct _IO_jump_t _IO_proc_jumps libio_vtable = {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_new_file_finish),
fa3bfd
   JUMP_INIT(overflow, _IO_new_file_overflow),
fa3bfd
diff -rupN a/libio/iovdprintf.c b/libio/iovdprintf.c
fa3bfd
--- a/libio/iovdprintf.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/iovdprintf.c	2017-08-24 14:56:40.237819924 -0400
fa3bfd
@@ -42,7 +42,7 @@ _IO_vdprintf (d, format, arg)
fa3bfd
 #endif
fa3bfd
   _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
fa3bfd
   _IO_JUMPS (&tmpfil) = &_IO_file_jumps;
fa3bfd
-  _IO_file_init (&tmpfil);
fa3bfd
+  _IO_new_file_init_internal (&tmpfil);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   tmpfil.vtable = NULL;
fa3bfd
 #endif
fa3bfd
diff -rupN a/libio/libioP.h b/libio/libioP.h
fa3bfd
--- a/libio/libioP.h	2017-08-24 14:50:26.000000000 -0400
fa3bfd
+++ b/libio/libioP.h	2017-08-24 17:26:39.918165252 -0400
fa3bfd
@@ -108,11 +108,12 @@ extern "C" {
fa3bfd
 
fa3bfd
 #if _IO_JUMPS_OFFSET
fa3bfd
 # define _IO_JUMPS_FUNC(THIS) \
fa3bfd
- (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS ((struct _IO_FILE_plus *) (THIS)) \
fa3bfd
-			   + (THIS)->_vtable_offset))
fa3bfd
+  (IO_validate_vtable                                                   \
fa3bfd
+   (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS ((struct _IO_FILE_plus *) (THIS)) \
fa3bfd
+			     + (THIS)->_vtable_offset)))
fa3bfd
 # define _IO_vtable_offset(THIS) (THIS)->_vtable_offset
fa3bfd
 #else
fa3bfd
-# define _IO_JUMPS_FUNC(THIS) _IO_JUMPS ((struct _IO_FILE_plus *) (THIS))
fa3bfd
+# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS ((struct _IO_FILE_plus *) (THIS))))
fa3bfd
 # define _IO_vtable_offset(THIS) 0
fa3bfd
 #endif
fa3bfd
 #define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
fa3bfd
@@ -361,8 +362,7 @@ extern void _IO_switch_to_main_get_area
fa3bfd
 extern void _IO_switch_to_backup_area (_IO_FILE *) __THROW;
fa3bfd
 extern int _IO_switch_to_get_mode (_IO_FILE *);
fa3bfd
 libc_hidden_proto (_IO_switch_to_get_mode)
fa3bfd
-extern void _IO_init (_IO_FILE *, int) __THROW;
fa3bfd
-libc_hidden_proto (_IO_init)
fa3bfd
+extern void _IO_init_internal (_IO_FILE *, int) attribute_hidden;
fa3bfd
 extern int _IO_sputbackc (_IO_FILE *, int) __THROW;
fa3bfd
 libc_hidden_proto (_IO_sputbackc)
fa3bfd
 extern int _IO_sungetc (_IO_FILE *) __THROW;
fa3bfd
@@ -570,8 +570,6 @@ extern int _IO_file_underflow_maybe_mmap
fa3bfd
 extern int _IO_file_overflow (_IO_FILE *, int);
fa3bfd
 libc_hidden_proto (_IO_file_overflow)
fa3bfd
 #define _IO_file_is_open(__fp) ((__fp)->_fileno != -1)
fa3bfd
-extern void _IO_file_init (struct _IO_FILE_plus *) __THROW;
fa3bfd
-libc_hidden_proto (_IO_file_init)
fa3bfd
 extern _IO_FILE* _IO_file_attach (_IO_FILE *, int);
fa3bfd
 libc_hidden_proto (_IO_file_attach)
fa3bfd
 extern _IO_FILE* _IO_file_open (_IO_FILE *, const char *, int, int, int, int);
fa3bfd
@@ -597,7 +595,8 @@ extern _IO_FILE* _IO_new_file_fopen (_IO
fa3bfd
 				     int);
fa3bfd
 extern void _IO_no_init (_IO_FILE *, int, int, struct _IO_wide_data *,
fa3bfd
 			 const struct _IO_jump_t *) __THROW;
fa3bfd
-extern void _IO_new_file_init (struct _IO_FILE_plus *) __THROW;
fa3bfd
+extern void _IO_new_file_init_internal (struct _IO_FILE_plus *)
fa3bfd
+  __THROW attribute_hidden;
fa3bfd
 extern _IO_FILE* _IO_new_file_setbuf (_IO_FILE *, char *, _IO_ssize_t);
fa3bfd
 extern _IO_FILE* _IO_file_setbuf_mmap (_IO_FILE *, char *, _IO_ssize_t);
fa3bfd
 extern int _IO_new_file_sync (_IO_FILE *);
fa3bfd
@@ -612,7 +611,8 @@ extern _IO_off64_t _IO_old_file_seekoff
fa3bfd
 extern _IO_size_t _IO_old_file_xsputn (_IO_FILE *, const void *, _IO_size_t);
fa3bfd
 extern int _IO_old_file_underflow (_IO_FILE *);
fa3bfd
 extern int _IO_old_file_overflow (_IO_FILE *, int);
fa3bfd
-extern void _IO_old_file_init (struct _IO_FILE_plus *) __THROW;
fa3bfd
+extern void _IO_old_file_init_internal (struct _IO_FILE_plus *)
fa3bfd
+  __THROW attribute_hidden;
fa3bfd
 extern _IO_FILE* _IO_old_file_attach (_IO_FILE *, int);
fa3bfd
 extern _IO_FILE* _IO_old_file_fopen (_IO_FILE *, const char *, const char *);
fa3bfd
 extern _IO_ssize_t _IO_old_file_write (_IO_FILE *, const void *, _IO_ssize_t);
fa3bfd
@@ -656,10 +656,6 @@ extern void _IO_str_finish (_IO_FILE *,
fa3bfd
 
fa3bfd
 /* Other strfile functions */
fa3bfd
 struct _IO_strfile_;
fa3bfd
-extern void _IO_str_init_static (struct _IO_strfile_ *, char *, int, char *)
fa3bfd
-     __THROW;
fa3bfd
-extern void _IO_str_init_readonly (struct _IO_strfile_ *, const char *, int)
fa3bfd
-     __THROW;
fa3bfd
 extern _IO_ssize_t _IO_str_count (_IO_FILE *) __THROW;
fa3bfd
 
fa3bfd
 /* And the wide character versions.  */
fa3bfd
@@ -913,3 +909,57 @@ _IO_acquire_lock_clear_flags2_fct (_IO_F
fa3bfd
                                           | _IO_FLAGS2_SCANF_STD);	      \
fa3bfd
   } while (0)
fa3bfd
 #endif
fa3bfd
+
fa3bfd
+/* Collect all vtables in a special section for vtable verification.
fa3bfd
+   These symbols cover the extent of this section.  */
fa3bfd
+symbol_set_declare (__libc_IO_vtables)
fa3bfd
+
fa3bfd
+/* libio vtables need to carry this attribute so that they pass
fa3bfd
+   validation.  */
fa3bfd
+#define libio_vtable __attribute__ ((section ("__libc_IO_vtables")))
fa3bfd
+
fa3bfd
+#ifdef SHARED
fa3bfd
+/* If equal to &_IO_vtable_check (with pointer guard protection),
fa3bfd
+   unknown vtable pointers are valid.  This function pointer is solely
fa3bfd
+   used as a flag.  */
fa3bfd
+extern void (*IO_accept_foreign_vtables) (void) attribute_hidden;
fa3bfd
+
fa3bfd
+/* Assigns the passed function pointer (either NULL or
fa3bfd
+   &_IO_vtable_check) to IO_accept_foreign_vtables.  */
fa3bfd
+static inline void
fa3bfd
+IO_set_accept_foreign_vtables (void (*flag) (void))
fa3bfd
+{
fa3bfd
+  PTR_MANGLE (flag);
fa3bfd
+  atomic_store_relaxed (&IO_accept_foreign_vtables, flag);
fa3bfd
+}
fa3bfd
+
fa3bfd
+#else  /* !SHARED */
fa3bfd
+
fa3bfd
+/* The statically-linked version does nothing. */
fa3bfd
+static inline void
fa3bfd
+IO_set_accept_foreign_vtables (void (*flag) (void))
fa3bfd
+{
fa3bfd
+}
fa3bfd
+
fa3bfd
+#endif
fa3bfd
+
fa3bfd
+/* Check if unknown vtable pointers are permitted; otherwise,
fa3bfd
+   terminate the process.  */
fa3bfd
+void _IO_vtable_check (void) attribute_hidden;
fa3bfd
+
fa3bfd
+/* Perform vtable pointer validation.  If validation fails, terminate
fa3bfd
+   the process.  */
fa3bfd
+static inline const struct _IO_jump_t *
fa3bfd
+IO_validate_vtable (const struct _IO_jump_t *vtable)
fa3bfd
+{
fa3bfd
+  /* Fast path: The vtable pointer is within the __libc_IO_vtables
fa3bfd
+     section.  */
fa3bfd
+  uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
fa3bfd
+  const char *ptr = (const char *) vtable;
fa3bfd
+  uintptr_t offset = ptr - __start___libc_IO_vtables;
fa3bfd
+  if (__glibc_unlikely (offset >= section_length))
fa3bfd
+    /* The vtable pointer is not in the expected section.  Use the
fa3bfd
+       slow path, which will terminate the process if necessary.  */
fa3bfd
+    _IO_vtable_check ();
fa3bfd
+  return vtable;
fa3bfd
+}
fa3bfd
diff -rupN a/libio/memstream.c b/libio/memstream.c
fa3bfd
--- a/libio/memstream.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/memstream.c	2017-08-24 15:56:02.643120827 -0400
fa3bfd
@@ -33,7 +33,7 @@ static int _IO_mem_sync (_IO_FILE* fp) _
fa3bfd
 static void _IO_mem_finish (_IO_FILE* fp, int) __THROW;
fa3bfd
 
fa3bfd
 
fa3bfd
-static const struct _IO_jump_t _IO_mem_jumps =
fa3bfd
+static const struct _IO_jump_t _IO_mem_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT (finish, _IO_mem_finish),
fa3bfd
@@ -85,7 +85,7 @@ open_memstream (bufloc, sizeloc)
fa3bfd
   buf = calloc (1, _IO_BUFSIZ);
fa3bfd
   if (buf == NULL)
fa3bfd
     return NULL;
fa3bfd
-  _IO_init (&new_f->fp._sf._sbf._f, 0);
fa3bfd
+  _IO_init_internal (&new_f->fp._sf._sbf._f, 0);
fa3bfd
   _IO_JUMPS ((struct _IO_FILE_plus *) &new_f->fp._sf._sbf) = &_IO_mem_jumps;
fa3bfd
   _IO_str_init_static_internal (&new_f->fp._sf, buf, _IO_BUFSIZ, buf);
fa3bfd
   new_f->fp._sf._sbf._f._flags &= ~_IO_USER_BUF;
fa3bfd
diff -rupN a/libio/obprintf.c b/libio/obprintf.c
fa3bfd
--- a/libio/obprintf.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/obprintf.c	2017-08-24 14:56:40.255819944 -0400
fa3bfd
@@ -91,7 +91,7 @@ _IO_obstack_xsputn (_IO_FILE *fp, const
fa3bfd
 
fa3bfd
 
fa3bfd
 /* the jump table.  */
fa3bfd
-const struct _IO_jump_t _IO_obstack_jumps attribute_hidden =
fa3bfd
+const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, NULL),
fa3bfd
diff -rupN a/libio/oldfileops.c b/libio/oldfileops.c
fa3bfd
--- a/libio/oldfileops.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/oldfileops.c	2017-08-24 15:56:27.333139635 -0400
fa3bfd
@@ -113,7 +113,7 @@ extern int errno;
fa3bfd
 
fa3bfd
 void
fa3bfd
 attribute_compat_text_section
fa3bfd
-_IO_old_file_init (fp)
fa3bfd
+_IO_old_file_init_internal (fp)
fa3bfd
      struct _IO_FILE_plus *fp;
fa3bfd
 {
fa3bfd
   /* POSIX.1 allows another file handle to be used to change the position
fa3bfd
@@ -138,6 +138,14 @@ _IO_old_file_init (fp)
fa3bfd
 #endif
fa3bfd
 }
fa3bfd
 
fa3bfd
+void
fa3bfd
+attribute_compat_text_section
fa3bfd
+_IO_old_file_init (struct _IO_FILE_plus *fp)
fa3bfd
+{
fa3bfd
+  IO_set_accept_foreign_vtables (&_IO_vtable_check);
fa3bfd
+  _IO_old_file_init_internal (fp);
fa3bfd
+}
fa3bfd
+
fa3bfd
 int
fa3bfd
 attribute_compat_text_section
fa3bfd
 _IO_old_file_close_it (fp)
fa3bfd
@@ -776,7 +784,7 @@ _IO_old_file_xsputn (f, data, n)
fa3bfd
 }
fa3bfd
 
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_old_file_jumps =
fa3bfd
+const struct _IO_jump_t _IO_old_file_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_old_file_finish),
fa3bfd
diff -rupN a/libio/oldiofdopen.c b/libio/oldiofdopen.c
fa3bfd
--- a/libio/oldiofdopen.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/oldiofdopen.c	2017-08-24 15:57:15.823176524 -0400
fa3bfd
@@ -114,7 +114,7 @@ _IO_old_fdopen (fd, mode)
fa3bfd
 #endif
fa3bfd
   _IO_old_init (&new_f->fp.file._file, 0);
fa3bfd
   _IO_JUMPS ((struct _IO_FILE_plus *) &new_f->fp) = &_IO_old_file_jumps;
fa3bfd
-  _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
fa3bfd
+  _IO_old_file_init_internal ((struct _IO_FILE_plus *) &new_f->fp);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   new_f->fp.vtable = NULL;
fa3bfd
 #endif
fa3bfd
diff -rupN a/libio/oldiofopen.c b/libio/oldiofopen.c
fa3bfd
--- a/libio/oldiofopen.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/oldiofopen.c	2017-08-24 15:58:13.978220676 -0400
fa3bfd
@@ -53,7 +53,7 @@ _IO_old_fopen (filename, mode)
fa3bfd
 #endif
fa3bfd
   _IO_old_init (&new_f->fp.file._file, 0);
fa3bfd
   _IO_JUMPS ((struct _IO_FILE_plus *) &new_f->fp) = &_IO_old_file_jumps;
fa3bfd
-  _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
fa3bfd
+  _IO_old_file_init_internal ((struct _IO_FILE_plus *) &new_f->fp);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   new_f->fp.vtable = NULL;
fa3bfd
 #endif
fa3bfd
diff -rupN a/libio/oldiopopen.c b/libio/oldiopopen.c
fa3bfd
--- a/libio/oldiopopen.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/oldiopopen.c	2017-08-24 15:58:32.060234383 -0400
fa3bfd
@@ -216,7 +216,7 @@ _IO_old_popen (command, mode)
fa3bfd
   fp = &new_f->fpx.file.file._file;
fa3bfd
   _IO_old_init (fp, 0);
fa3bfd
   _IO_JUMPS ((struct _IO_FILE_plus *) &new_f->fpx.file) = &_IO_old_proc_jumps;
fa3bfd
-  _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fpx.file);
fa3bfd
+  _IO_old_file_init_internal ((struct _IO_FILE_plus *) &new_f->fpx.file);
fa3bfd
 #if  !_IO_UNIFIED_JUMPTABLES
fa3bfd
   new_f->fpx.file.vtable = NULL;
fa3bfd
 #endif
fa3bfd
@@ -273,7 +273,7 @@ _IO_old_proc_close (fp)
fa3bfd
   return wstatus;
fa3bfd
 }
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_old_proc_jumps = {
fa3bfd
+const struct _IO_jump_t _IO_old_proc_jumps libio_vtable = {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_old_file_finish),
fa3bfd
   JUMP_INIT(overflow, _IO_old_file_overflow),
fa3bfd
diff -rupN a/libio/strops.c b/libio/strops.c
fa3bfd
--- a/libio/strops.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/strops.c	2017-08-24 14:56:40.289819980 -0400
fa3bfd
@@ -345,7 +345,7 @@ _IO_str_finish (fp, dummy)
fa3bfd
   _IO_default_finish (fp, 0);
fa3bfd
 }
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_str_jumps =
fa3bfd
+const struct _IO_jump_t _IO_str_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_str_finish),
fa3bfd
diff -rupN a/libio/vsnprintf.c b/libio/vsnprintf.c
fa3bfd
--- a/libio/vsnprintf.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/vsnprintf.c	2017-08-24 14:56:40.292819983 -0400
fa3bfd
@@ -66,7 +66,7 @@ _IO_strn_overflow (fp, c)
fa3bfd
 }
fa3bfd
 
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_strn_jumps attribute_hidden =
fa3bfd
+const struct _IO_jump_t _IO_strn_jumps libio_vtable attribute_hidden =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_str_finish),
fa3bfd
diff -rupN a/libio/vswprintf.c b/libio/vswprintf.c
fa3bfd
--- a/libio/vswprintf.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/vswprintf.c	2017-08-24 14:56:40.296819988 -0400
fa3bfd
@@ -65,7 +65,7 @@ _IO_wstrn_overflow (fp, c)
fa3bfd
 }
fa3bfd
 
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_wstrn_jumps attribute_hidden =
fa3bfd
+const struct _IO_jump_t _IO_wstrn_jumps libio_vtable attribute_hidden =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_wstr_finish),
fa3bfd
diff -rupN a/libio/vtables.c b/libio/vtables.c
fa3bfd
--- a/libio/vtables.c	1969-12-31 19:00:00.000000000 -0500
fa3bfd
+++ b/libio/vtables.c	2017-08-24 14:56:40.299819991 -0400
fa3bfd
@@ -0,0 +1,70 @@
fa3bfd
+/* libio vtable validation.
fa3bfd
+   Copyright (C) 2016 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+#include <dlfcn.h>
fa3bfd
+#include <libioP.h>
fa3bfd
+#include <stdio.h>
fa3bfd
+
fa3bfd
+#ifdef SHARED
fa3bfd
+
fa3bfd
+void (*IO_accept_foreign_vtables) (void) attribute_hidden;
fa3bfd
+
fa3bfd
+/* Used to detected multiple libcs.  */
fa3bfd
+extern struct dl_open_hook *_dl_open_hook;
fa3bfd
+libc_hidden_proto (_dl_open_hook);
fa3bfd
+
fa3bfd
+#else  /* !SHARED */
fa3bfd
+
fa3bfd
+/* Used to check whether static dlopen support is needed.  */
fa3bfd
+# pragma weak __dlopen
fa3bfd
+
fa3bfd
+#endif
fa3bfd
+
fa3bfd
+void attribute_hidden
fa3bfd
+_IO_vtable_check (void)
fa3bfd
+{
fa3bfd
+#ifdef SHARED
fa3bfd
+  /* Honor the compatibility flag.  */
fa3bfd
+  void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables);
fa3bfd
+  PTR_DEMANGLE (flag);
fa3bfd
+  if (flag == &_IO_vtable_check)
fa3bfd
+    return;
fa3bfd
+
fa3bfd
+  /* In case this libc copy is in a non-default namespace, we always
fa3bfd
+     need to accept foreign vtables because there is always a
fa3bfd
+     possibility that FILE * objects are passed across the linking
fa3bfd
+     boundary.  */
fa3bfd
+  {
fa3bfd
+    Dl_info di;
fa3bfd
+    struct link_map *l;
fa3bfd
+    if (_dl_open_hook != NULL
fa3bfd
+        || (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0
fa3bfd
+            && l->l_ns != LM_ID_BASE))
fa3bfd
+      return;
fa3bfd
+  }
fa3bfd
+
fa3bfd
+#else /* !SHARED */
fa3bfd
+  /* We cannot perform vtable validation in the static dlopen case
fa3bfd
+     because FILE * handles might be passed back and forth across the
fa3bfd
+     boundary.  Therefore, we disable checking in this case.  */
fa3bfd
+  if (__dlopen != NULL)
fa3bfd
+    return;
fa3bfd
+#endif
fa3bfd
+
fa3bfd
+  __libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
fa3bfd
+}
fa3bfd
diff -rupN a/libio/wfileops.c b/libio/wfileops.c
fa3bfd
--- a/libio/wfileops.c	2017-08-24 14:50:25.000000000 -0400
fa3bfd
+++ b/libio/wfileops.c	2017-08-24 14:56:40.303819995 -0400
fa3bfd
@@ -1035,7 +1035,7 @@ _IO_wfile_xsputn (f, data, n)
fa3bfd
 libc_hidden_def (_IO_wfile_xsputn)
fa3bfd
 
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_wfile_jumps =
fa3bfd
+const struct _IO_jump_t _IO_wfile_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_new_file_finish),
fa3bfd
@@ -1061,7 +1061,7 @@ const struct _IO_jump_t _IO_wfile_jumps
fa3bfd
 libc_hidden_data_def (_IO_wfile_jumps)
fa3bfd
 
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_wfile_jumps_mmap =
fa3bfd
+const struct _IO_jump_t _IO_wfile_jumps_mmap libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_new_file_finish),
fa3bfd
@@ -1085,7 +1085,7 @@ const struct _IO_jump_t _IO_wfile_jumps_
fa3bfd
   JUMP_INIT(imbue, _IO_default_imbue)
fa3bfd
 };
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap =
fa3bfd
+const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_new_file_finish),
fa3bfd
diff -rupN a/libio/wmemstream.c b/libio/wmemstream.c
fa3bfd
--- a/libio/wmemstream.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/libio/wmemstream.c	2017-08-24 14:56:40.307819999 -0400
fa3bfd
@@ -34,7 +34,7 @@ static int _IO_wmem_sync (_IO_FILE* fp)
fa3bfd
 static void _IO_wmem_finish (_IO_FILE* fp, int) __THROW;
fa3bfd
 
fa3bfd
 
fa3bfd
-static const struct _IO_jump_t _IO_wmem_jumps =
fa3bfd
+static const struct _IO_jump_t _IO_wmem_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT (finish, _IO_wmem_finish),
fa3bfd
diff -rupN a/libio/wstrops.c b/libio/wstrops.c
fa3bfd
--- a/libio/wstrops.c	2017-08-24 14:50:23.000000000 -0400
fa3bfd
+++ b/libio/wstrops.c	2017-08-24 14:56:40.310820002 -0400
fa3bfd
@@ -347,7 +347,7 @@ _IO_wstr_finish (fp, dummy)
fa3bfd
   _IO_wdefault_finish (fp, 0);
fa3bfd
 }
fa3bfd
 
fa3bfd
-const struct _IO_jump_t _IO_wstr_jumps =
fa3bfd
+const struct _IO_jump_t _IO_wstr_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT(finish, _IO_wstr_finish),
fa3bfd
diff -rupN a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
fa3bfd
--- a/stdio-common/vfprintf.c	2017-08-24 14:50:20.000000000 -0400
fa3bfd
+++ b/stdio-common/vfprintf.c	2017-08-24 14:56:40.314820007 -0400
fa3bfd
@@ -2234,7 +2234,7 @@ _IO_helper_overflow (_IO_FILE *s, int c)
fa3bfd
 }
fa3bfd
 
fa3bfd
 #ifdef COMPILE_WPRINTF
fa3bfd
-static const struct _IO_jump_t _IO_helper_jumps =
fa3bfd
+static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT (finish, _IO_wdefault_finish),
fa3bfd
@@ -2256,7 +2256,7 @@ static const struct _IO_jump_t _IO_helpe
fa3bfd
   JUMP_INIT (stat, _IO_default_stat)
fa3bfd
 };
fa3bfd
 #else
fa3bfd
-static const struct _IO_jump_t _IO_helper_jumps =
fa3bfd
+static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
fa3bfd
 {
fa3bfd
   JUMP_INIT_DUMMY,
fa3bfd
   JUMP_INIT (finish, _IO_default_finish),
fa3bfd
diff -rupN a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c
fa3bfd
--- a/stdlib/strfmon_l.c	2012-12-24 22:02:13.000000000 -0500
fa3bfd
+++ b/stdlib/strfmon_l.c	2017-08-24 14:56:40.314820007 -0400
fa3bfd
@@ -515,7 +515,7 @@ __vstrfmon_l (char *s, size_t maxsize, _
fa3bfd
 #ifdef _IO_MTSAFE_IO
fa3bfd
       f._sbf._f._lock = NULL;
fa3bfd
 #endif
fa3bfd
-      _IO_init (&f._sbf._f, 0);
fa3bfd
+      _IO_init_internal (&f._sbf._f, 0);
fa3bfd
       _IO_JUMPS (&f._sbf) = &_IO_str_jumps;
fa3bfd
       _IO_str_init_static_internal (&f, dest, (s + maxsize) - dest, dest);
fa3bfd
       /* We clear the last available byte so we can find out whether