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