about summary refs log tree commit diff
path: root/libio
diff options
context:
space:
mode:
Diffstat (limited to 'libio')
-rw-r--r--libio/Makefile2
-rw-r--r--libio/fileops.c18
-rw-r--r--libio/genops.c10
-rw-r--r--libio/iofdopen.c12
-rw-r--r--libio/iofopen.c2
-rw-r--r--libio/iofopncook.c8
-rw-r--r--libio/iopopen.c6
-rw-r--r--libio/iovdprintf.c2
-rw-r--r--libio/libioP.h76
-rw-r--r--libio/memstream.c4
-rw-r--r--libio/obprintf.c2
-rw-r--r--libio/oldfileops.c12
-rw-r--r--libio/oldiofdopen.c2
-rw-r--r--libio/oldiofopen.c2
-rw-r--r--libio/oldiopopen.c4
-rw-r--r--libio/strops.c2
-rw-r--r--libio/vsnprintf.c2
-rw-r--r--libio/vswprintf.c2
-rw-r--r--libio/vtables.c70
-rw-r--r--libio/wfileops.c6
-rw-r--r--libio/wmemstream.c2
-rw-r--r--libio/wstrops.c2
22 files changed, 195 insertions, 53 deletions
diff --git a/libio/Makefile b/libio/Makefile
index 4189bc4ad0..12589f2875 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -46,7 +46,7 @@ routines	:=							      \
 	__fbufsize __freading __fwriting __freadable __fwritable __flbf	      \
 	__fpurge __fpending __fsetlocking				      \
 									      \
-	libc_fatal fmemopen oldfmemopen
+	libc_fatal fmemopen oldfmemopen vtables
 
 tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \
diff --git a/libio/fileops.c b/libio/fileops.c
index 8e83b1cd7b..1315735427 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -140,7 +140,7 @@ extern struct __gconv_trans_data __libio_translit attribute_hidden;
 
 
 void
-_IO_new_file_init (struct _IO_FILE_plus *fp)
+_IO_new_file_init_internal (struct _IO_FILE_plus *fp)
 {
   /* POSIX.1 allows another file handle to be used to change the position
      of our file descriptor.  Hence we actually don't know the actual
@@ -151,7 +151,15 @@ _IO_new_file_init (struct _IO_FILE_plus *fp)
   _IO_link_in (fp);
   fp->file._fileno = -1;
 }
-libc_hidden_ver (_IO_new_file_init, _IO_file_init)
+
+/* External version of _IO_new_file_init_internal which switches off
+   vtable validation.  */
+void
+_IO_new_file_init (struct _IO_FILE_plus *fp)
+{
+  IO_set_accept_foreign_vtables (&_IO_vtable_check);
+  _IO_new_file_init_internal (fp);
+}
 
 int
 _IO_new_file_close_it (_IO_FILE *fp)
@@ -1534,7 +1542,7 @@ versioned_symbol (libc, _IO_new_file_write, _IO_file_write, GLIBC_2_1);
 versioned_symbol (libc, _IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2_1);
 #endif
 
-const struct _IO_jump_t _IO_file_jumps =
+const struct _IO_jump_t _IO_file_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_file_finish),
@@ -1559,7 +1567,7 @@ const struct _IO_jump_t _IO_file_jumps =
 };
 libc_hidden_data_def (_IO_file_jumps)
 
-const struct _IO_jump_t _IO_file_jumps_mmap =
+const struct _IO_jump_t _IO_file_jumps_mmap libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_file_finish),
@@ -1583,7 +1591,7 @@ const struct _IO_jump_t _IO_file_jumps_mmap =
   JUMP_INIT(imbue, _IO_default_imbue)
 };
 
-const struct _IO_jump_t _IO_file_jumps_maybe_mmap =
+const struct _IO_jump_t _IO_file_jumps_maybe_mmap libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_file_finish),
diff --git a/libio/genops.c b/libio/genops.c
index 5803cbf04f..6234bf9046 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -558,11 +558,17 @@ _IO_default_doallocate (_IO_FILE *fp)
 libc_hidden_def (_IO_default_doallocate)
 
 void
-_IO_init (_IO_FILE *fp, int flags)
+_IO_init_internal (_IO_FILE *fp, int flags)
 {
   _IO_no_init (fp, flags, -1, NULL, NULL);
 }
-libc_hidden_def (_IO_init)
+
+void
+_IO_init (_IO_FILE *fp, int flags)
+{
+  IO_set_accept_foreign_vtables (&_IO_vtable_check);
+  _IO_init_internal (fp, flags);
+}
 
 void
 _IO_old_init (_IO_FILE *fp, int flags)
diff --git a/libio/iofdopen.c b/libio/iofdopen.c
index e00f337521..a4b6757942 100644
--- a/libio/iofdopen.c
+++ b/libio/iofdopen.c
@@ -153,15 +153,15 @@ _IO_new_fdopen (int fd, const char *mode)
     (use_mmap && (read_write & _IO_NO_WRITES)) ? &_IO_file_jumps_maybe_mmap :
 #endif
       &_IO_file_jumps;
-  _IO_file_init (&new_f->fp);
+  _IO_new_file_init_internal (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
 #endif
-  /* We only need to record the fd because _IO_file_init will have unset the
-     offset.  It is important to unset the cached offset because the real
-     offset in the file could change between now and when the handle is
-     activated and we would then mislead ftell into believing that we have a
-     valid offset.  */
+  /* We only need to record the fd because _IO_file_init_internal will
+     have unset the offset.  It is important to unset the cached
+     offset because the real offset in the file could change between
+     now and when the handle is activated and we would then mislead
+     ftell into believing that we have a valid offset.  */
   new_f->fp.file._fileno = fd;
   new_f->fp.file._flags &= ~_IO_DELETE_DONT_CLOSE;
 
diff --git a/libio/iofopen.c b/libio/iofopen.c
index 13e3910b63..855fe2fae5 100644
--- a/libio/iofopen.c
+++ b/libio/iofopen.c
@@ -79,7 +79,7 @@ __fopen_internal (const char *filename, const char *mode, int is32)
   _IO_no_init (&new_f->fp.file, 1, 0, NULL, NULL);
 #endif
   _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
-  _IO_file_init (&new_f->fp);
+  _IO_new_file_init_internal (&new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
 #endif
diff --git a/libio/iofopncook.c b/libio/iofopncook.c
index ceb444af7e..ae5df1707a 100644
--- a/libio/iofopncook.c
+++ b/libio/iofopncook.c
@@ -110,7 +110,7 @@ _IO_cookie_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
 }
 
 
-static const struct _IO_jump_t _IO_cookie_jumps = {
+static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_file_finish),
   JUMP_INIT(overflow, _IO_file_overflow),
@@ -151,13 +151,13 @@ void
 _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_init_internal (&cfile->__fp.file, 0);
   _IO_JUMPS (&cfile->__fp) = &_IO_cookie_jumps;
 
   cfile->__cookie = cookie;
   set_callbacks (&cfile->__io_functions, io_functions);
 
-  _IO_file_init (&cfile->__fp);
+  _IO_new_file_init_internal (&cfile->__fp);
 
   _IO_mask_flags (&cfile->__fp.file, read_write,
 		  _IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
@@ -238,7 +238,7 @@ _IO_old_cookie_seek (_IO_FILE *fp, _IO_off64_t offset, int dir)
   return (ret == -1) ? _IO_pos_BAD : ret;
 }
 
-static const struct _IO_jump_t _IO_old_cookie_jumps = {
+static const struct _IO_jump_t _IO_old_cookie_jumps libio_vtable = {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_file_finish),
   JUMP_INIT(overflow, _IO_file_overflow),
diff --git a/libio/iopopen.c b/libio/iopopen.c
index 9ddde23494..d85370c01f 100644
--- a/libio/iopopen.c
+++ b/libio/iopopen.c
@@ -287,9 +287,9 @@ _IO_new_popen (const char *command, const char *mode)
   new_f->fpx.file.file._lock = &new_f->lock;
 #endif
   fp = &new_f->fpx.file.file;
-  _IO_init (fp, 0);
+  _IO_init_internal (fp, 0);
   _IO_JUMPS (&new_f->fpx.file) = &_IO_proc_jumps;
-  _IO_new_file_init (&new_f->fpx.file);
+  _IO_new_file_init_internal (&new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
 #endif
@@ -344,7 +344,7 @@ _IO_new_proc_close (_IO_FILE *fp)
   return wstatus;
 }
 
-static const struct _IO_jump_t _IO_proc_jumps = {
+static const struct _IO_jump_t _IO_proc_jumps libio_vtable = {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_new_file_finish),
   JUMP_INIT(overflow, _IO_new_file_overflow),
diff --git a/libio/iovdprintf.c b/libio/iovdprintf.c
index 8ca55fccae..d279e34e87 100644
--- a/libio/iovdprintf.c
+++ b/libio/iovdprintf.c
@@ -39,7 +39,7 @@ _IO_vdprintf (int d, const char *format, _IO_va_list arg)
 #endif
   _IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
   _IO_JUMPS (&tmpfil) = &_IO_file_jumps;
-  _IO_file_init (&tmpfil);
+  _IO_new_file_init_internal (&tmpfil);
 #if  !_IO_UNIFIED_JUMPTABLES
   tmpfil.vtable = NULL;
 #endif
diff --git a/libio/libioP.h b/libio/libioP.h
index 8706af2d90..54dc35cdb6 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -125,11 +125,12 @@ extern "C" {
 
 #if _IO_JUMPS_OFFSET
 # define _IO_JUMPS_FUNC(THIS) \
- (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
-			   + (THIS)->_vtable_offset))
+  (IO_validate_vtable                                                   \
+   (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (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_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
 # define _IO_vtable_offset(THIS) 0
 #endif
 #define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
@@ -378,8 +379,7 @@ extern void _IO_switch_to_main_get_area (_IO_FILE *) __THROW;
 extern void _IO_switch_to_backup_area (_IO_FILE *) __THROW;
 extern int _IO_switch_to_get_mode (_IO_FILE *);
 libc_hidden_proto (_IO_switch_to_get_mode)
-extern void _IO_init (_IO_FILE *, int) __THROW;
-libc_hidden_proto (_IO_init)
+extern void _IO_init_internal (_IO_FILE *, int) attribute_hidden;
 extern int _IO_sputbackc (_IO_FILE *, int) __THROW;
 libc_hidden_proto (_IO_sputbackc)
 extern int _IO_sungetc (_IO_FILE *) __THROW;
@@ -587,8 +587,6 @@ extern int _IO_file_underflow_maybe_mmap (_IO_FILE *);
 extern int _IO_file_overflow (_IO_FILE *, int);
 libc_hidden_proto (_IO_file_overflow)
 #define _IO_file_is_open(__fp) ((__fp)->_fileno != -1)
-extern void _IO_file_init (struct _IO_FILE_plus *) __THROW;
-libc_hidden_proto (_IO_file_init)
 extern _IO_FILE* _IO_file_attach (_IO_FILE *, int);
 libc_hidden_proto (_IO_file_attach)
 extern _IO_FILE* _IO_file_open (_IO_FILE *, const char *, int, int, int, int);
@@ -614,7 +612,8 @@ extern _IO_FILE* _IO_new_file_fopen (_IO_FILE *, const char *, const char *,
 				     int);
 extern void _IO_no_init (_IO_FILE *, int, int, struct _IO_wide_data *,
 			 const struct _IO_jump_t *) __THROW;
-extern void _IO_new_file_init (struct _IO_FILE_plus *) __THROW;
+extern void _IO_new_file_init_internal (struct _IO_FILE_plus *)
+  __THROW attribute_hidden;
 extern _IO_FILE* _IO_new_file_setbuf (_IO_FILE *, char *, _IO_ssize_t);
 extern _IO_FILE* _IO_file_setbuf_mmap (_IO_FILE *, char *, _IO_ssize_t);
 extern int _IO_new_file_sync (_IO_FILE *);
@@ -629,7 +628,8 @@ extern _IO_off64_t _IO_old_file_seekoff (_IO_FILE *, _IO_off64_t, int, int);
 extern _IO_size_t _IO_old_file_xsputn (_IO_FILE *, const void *, _IO_size_t);
 extern int _IO_old_file_underflow (_IO_FILE *);
 extern int _IO_old_file_overflow (_IO_FILE *, int);
-extern void _IO_old_file_init (struct _IO_FILE_plus *) __THROW;
+extern void _IO_old_file_init_internal (struct _IO_FILE_plus *)
+  __THROW attribute_hidden;
 extern _IO_FILE* _IO_old_file_attach (_IO_FILE *, int);
 extern _IO_FILE* _IO_old_file_fopen (_IO_FILE *, const char *, const char *);
 extern _IO_ssize_t _IO_old_file_write (_IO_FILE *, const void *, _IO_ssize_t);
@@ -673,10 +673,6 @@ extern void _IO_str_finish (_IO_FILE *, int) __THROW;
 
 /* Other strfile functions */
 struct _IO_strfile_;
-extern void _IO_str_init_static (struct _IO_strfile_ *, char *, int, char *)
-     __THROW;
-extern void _IO_str_init_readonly (struct _IO_strfile_ *, const char *, int)
-     __THROW;
 extern _IO_ssize_t _IO_str_count (_IO_FILE *) __THROW;
 
 /* And the wide character versions.  */
@@ -890,3 +886,57 @@ _IO_acquire_lock_clear_flags2_fct (_IO_FILE **p)
                                           | _IO_FLAGS2_SCANF_STD);	      \
   } while (0)
 #endif
+
+/* Collect all vtables in a special section for vtable verification.
+   These symbols cover the extent of this section.  */
+symbol_set_declare (__libc_IO_vtables)
+
+/* libio vtables need to carry this attribute so that they pass
+   validation.  */
+#define libio_vtable __attribute__ ((section ("__libc_IO_vtables")))
+
+#ifdef SHARED
+/* If equal to &_IO_vtable_check (with pointer guard protection),
+   unknown vtable pointers are valid.  This function pointer is solely
+   used as a flag.  */
+extern void (*IO_accept_foreign_vtables) (void) attribute_hidden;
+
+/* Assigns the passed function pointer (either NULL or
+   &_IO_vtable_check) to IO_accept_foreign_vtables.  */
+static inline void
+IO_set_accept_foreign_vtables (void (*flag) (void))
+{
+  PTR_MANGLE (flag);
+  atomic_store_relaxed (&IO_accept_foreign_vtables, flag);
+}
+
+#else  /* !SHARED */
+
+/* The statically-linked version does nothing. */
+static inline void
+IO_set_accept_foreign_vtables (void (*flag) (void))
+{
+}
+
+#endif
+
+/* Check if unknown vtable pointers are permitted; otherwise,
+   terminate the process.  */
+void _IO_vtable_check (void) attribute_hidden;
+
+/* Perform vtable pointer validation.  If validation fails, terminate
+   the process.  */
+static inline const struct _IO_jump_t *
+IO_validate_vtable (const struct _IO_jump_t *vtable)
+{
+  /* Fast path: The vtable pointer is within the __libc_IO_vtables
+     section.  */
+  uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
+  const char *ptr = (const char *) vtable;
+  uintptr_t offset = ptr - __start___libc_IO_vtables;
+  if (__glibc_unlikely (offset >= section_length))
+    /* The vtable pointer is not in the expected section.  Use the
+       slow path, which will terminate the process if necessary.  */
+    _IO_vtable_check ();
+  return vtable;
+}
diff --git a/libio/memstream.c b/libio/memstream.c
index 7fa5245e72..e20b9c2250 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -33,7 +33,7 @@ static int _IO_mem_sync (_IO_FILE* fp) __THROW;
 static void _IO_mem_finish (_IO_FILE* fp, int) __THROW;
 
 
-static const struct _IO_jump_t _IO_mem_jumps =
+static const struct _IO_jump_t _IO_mem_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT (finish, _IO_mem_finish),
@@ -86,7 +86,7 @@ __open_memstream (char **bufloc, _IO_size_t *sizeloc)
       free (new_f);
       return NULL;
     }
-  _IO_init (&new_f->fp._sf._sbf._f, 0);
+  _IO_init_internal (&new_f->fp._sf._sbf._f, 0);
   _IO_JUMPS_FILE_plus (&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;
diff --git a/libio/obprintf.c b/libio/obprintf.c
index aa17b46da8..d7144e8e39 100644
--- a/libio/obprintf.c
+++ b/libio/obprintf.c
@@ -91,7 +91,7 @@ _IO_obstack_xsputn (_IO_FILE *fp, const void *data, _IO_size_t n)
 
 
 /* the jump table.  */
-const struct _IO_jump_t _IO_obstack_jumps attribute_hidden =
+const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, NULL),
diff --git a/libio/oldfileops.c b/libio/oldfileops.c
index 4f3bdfe489..26b2bf5cf1 100644
--- a/libio/oldfileops.c
+++ b/libio/oldfileops.c
@@ -114,7 +114,7 @@ extern int errno;
 
 void
 attribute_compat_text_section
-_IO_old_file_init (struct _IO_FILE_plus *fp)
+_IO_old_file_init_internal (struct _IO_FILE_plus *fp)
 {
   /* POSIX.1 allows another file handle to be used to change the position
      of our file descriptor.  Hence we actually don't know the actual
@@ -138,6 +138,14 @@ _IO_old_file_init (struct _IO_FILE_plus *fp)
 #endif
 }
 
+void
+attribute_compat_text_section
+_IO_old_file_init (struct _IO_FILE_plus *fp)
+{
+  IO_set_accept_foreign_vtables (&_IO_vtable_check);
+  _IO_old_file_init_internal (fp);
+}
+
 int
 attribute_compat_text_section
 _IO_old_file_close_it (_IO_FILE *fp)
@@ -745,7 +753,7 @@ _IO_old_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
 }
 
 
-const struct _IO_jump_t _IO_old_file_jumps =
+const struct _IO_jump_t _IO_old_file_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_old_file_finish),
diff --git a/libio/oldiofdopen.c b/libio/oldiofdopen.c
index 33406ff240..59fcfa4bf1 100644
--- a/libio/oldiofdopen.c
+++ b/libio/oldiofdopen.c
@@ -112,7 +112,7 @@ _IO_old_fdopen (int fd, const char *mode)
 #endif
   _IO_old_init (&new_f->fp.file._file, 0);
   _IO_JUMPS_FILE_plus (&new_f->fp) = &_IO_old_file_jumps;
-  _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
+  _IO_old_file_init_internal ((struct _IO_FILE_plus *) &new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
 #endif
diff --git a/libio/oldiofopen.c b/libio/oldiofopen.c
index cc7c34282c..ce6a33d624 100644
--- a/libio/oldiofopen.c
+++ b/libio/oldiofopen.c
@@ -51,7 +51,7 @@ _IO_old_fopen (const char *filename, const char *mode)
 #endif
   _IO_old_init (&new_f->fp.file._file, 0);
   _IO_JUMPS_FILE_plus (&new_f->fp) = &_IO_old_file_jumps;
-  _IO_old_file_init ((struct _IO_FILE_plus *) &new_f->fp);
+  _IO_old_file_init_internal ((struct _IO_FILE_plus *) &new_f->fp);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fp.vtable = NULL;
 #endif
diff --git a/libio/oldiopopen.c b/libio/oldiopopen.c
index ea75b4fb90..bce74b1a5b 100644
--- a/libio/oldiopopen.c
+++ b/libio/oldiopopen.c
@@ -211,7 +211,7 @@ _IO_old_popen (const char *command, const char *mode)
   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_old_file_init ((struct _IO_FILE_plus *) &new_f->fpx.file);
+  _IO_old_file_init_internal ((struct _IO_FILE_plus *) &new_f->fpx.file);
 #if  !_IO_UNIFIED_JUMPTABLES
   new_f->fpx.file.vtable = NULL;
 #endif
@@ -267,7 +267,7 @@ _IO_old_proc_close (_IO_FILE *fp)
   return wstatus;
 }
 
-const struct _IO_jump_t _IO_old_proc_jumps = {
+const struct _IO_jump_t _IO_old_proc_jumps libio_vtable = {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_old_file_finish),
   JUMP_INIT(overflow, _IO_old_file_overflow),
diff --git a/libio/strops.c b/libio/strops.c
index 0932d4c1b1..2ba3704dd2 100644
--- a/libio/strops.c
+++ b/libio/strops.c
@@ -323,7 +323,7 @@ _IO_str_finish (_IO_FILE *fp, int dummy)
   _IO_default_finish (fp, 0);
 }
 
-const struct _IO_jump_t _IO_str_jumps =
+const struct _IO_jump_t _IO_str_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_str_finish),
diff --git a/libio/vsnprintf.c b/libio/vsnprintf.c
index f1063a17be..ee7f75290f 100644
--- a/libio/vsnprintf.c
+++ b/libio/vsnprintf.c
@@ -64,7 +64,7 @@ _IO_strn_overflow (_IO_FILE *fp, int c)
 }
 
 
-const struct _IO_jump_t _IO_strn_jumps attribute_hidden =
+const struct _IO_jump_t _IO_strn_jumps libio_vtable attribute_hidden =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_str_finish),
diff --git a/libio/vswprintf.c b/libio/vswprintf.c
index b90441ad77..df7b719149 100644
--- a/libio/vswprintf.c
+++ b/libio/vswprintf.c
@@ -63,7 +63,7 @@ _IO_wstrn_overflow (_IO_FILE *fp, wint_t c)
 }
 
 
-const struct _IO_jump_t _IO_wstrn_jumps attribute_hidden =
+const struct _IO_jump_t _IO_wstrn_jumps libio_vtable attribute_hidden =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_wstr_finish),
diff --git a/libio/vtables.c b/libio/vtables.c
new file mode 100644
index 0000000000..e364ea03ed
--- /dev/null
+++ b/libio/vtables.c
@@ -0,0 +1,70 @@
+/* libio vtable validation.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libioP.h>
+#include <stdio.h>
+
+#ifdef SHARED
+
+void (*IO_accept_foreign_vtables) (void) attribute_hidden;
+
+/* Used to detected multiple libcs.  */
+extern struct dl_open_hook *_dl_open_hook;
+libc_hidden_proto (_dl_open_hook);
+
+#else  /* !SHARED */
+
+/* Used to check whether static dlopen support is needed.  */
+# pragma weak __dlopen
+
+#endif
+
+void attribute_hidden
+_IO_vtable_check (void)
+{
+#ifdef SHARED
+  /* Honor the compatibility flag.  */
+  void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables);
+  PTR_DEMANGLE (flag);
+  if (flag == &_IO_vtable_check)
+    return;
+
+  /* In case this libc copy is in a non-default namespace, we always
+     need to accept foreign vtables because there is always a
+     possibility that FILE * objects are passed across the linking
+     boundary.  */
+  {
+    Dl_info di;
+    struct link_map *l;
+    if (_dl_open_hook != NULL
+        || (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0
+            && l->l_ns != LM_ID_BASE))
+      return;
+  }
+
+#else /* !SHARED */
+  /* We cannot perform vtable validation in the static dlopen case
+     because FILE * handles might be passed back and forth across the
+     boundary.  Therefore, we disable checking in this case.  */
+  if (__dlopen != NULL)
+    return;
+#endif
+
+  __libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
+}
diff --git a/libio/wfileops.c b/libio/wfileops.c
index df1fbdaf8b..d88d08a242 100644
--- a/libio/wfileops.c
+++ b/libio/wfileops.c
@@ -1042,7 +1042,7 @@ _IO_wfile_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
 libc_hidden_def (_IO_wfile_xsputn)
 
 
-const struct _IO_jump_t _IO_wfile_jumps =
+const struct _IO_jump_t _IO_wfile_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_new_file_finish),
@@ -1068,7 +1068,7 @@ const struct _IO_jump_t _IO_wfile_jumps =
 libc_hidden_data_def (_IO_wfile_jumps)
 
 
-const struct _IO_jump_t _IO_wfile_jumps_mmap =
+const struct _IO_jump_t _IO_wfile_jumps_mmap libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_new_file_finish),
@@ -1092,7 +1092,7 @@ const struct _IO_jump_t _IO_wfile_jumps_mmap =
   JUMP_INIT(imbue, _IO_default_imbue)
 };
 
-const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap =
+const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_new_file_finish),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index 1bdbae9fe8..bf2a50b523 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -34,7 +34,7 @@ static int _IO_wmem_sync (_IO_FILE* fp) __THROW;
 static void _IO_wmem_finish (_IO_FILE* fp, int) __THROW;
 
 
-static const struct _IO_jump_t _IO_wmem_jumps =
+static const struct _IO_jump_t _IO_wmem_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT (finish, _IO_wmem_finish),
diff --git a/libio/wstrops.c b/libio/wstrops.c
index 8d0e1cb331..09fa543f77 100644
--- a/libio/wstrops.c
+++ b/libio/wstrops.c
@@ -332,7 +332,7 @@ _IO_wstr_finish (_IO_FILE *fp, int dummy)
   _IO_wdefault_finish (fp, 0);
 }
 
-const struct _IO_jump_t _IO_wstr_jumps =
+const struct _IO_jump_t _IO_wstr_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_wstr_finish),