]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
libio: Implement vtable verification [BZ #20191]
authorFlorian Weimer <fweimer@redhat.com>
Thu, 23 Jun 2016 18:01:40 +0000 (20:01 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Thu, 23 Jun 2016 18:01:52 +0000 (20:01 +0200)
This commit puts all libio vtables in a dedicated, read-only ELF
section, so that they are consecutive in memory.  Before any indirect
jump, the vtable pointer is checked against the section boundaries,
and the process is terminated if the vtable pointer does not fall into
the special ELF section.

To enable backwards compatibility, a special flag variable
(_IO_accept_foreign_vtables), protected by the pointer guard, avoids
process termination if libio stream object constructor functions have
been called earlier.  Such constructor functions are called by the GCC
2.95 libstdc++ library, and this mechanism ensures compatibility with
old binaries.  Existing callers inside glibc of these functions are
adjusted to call the original functions, not the wrappers which enable
vtable compatiblity.

The compatibility mechanism is used to enable passing FILE * objects
across a static dlopen boundary, too.

30 files changed:
ChangeLog
Makerules
debug/obprintf_chk.c
debug/vdprintf_chk.c
debug/vsnprintf_chk.c
debug/vsprintf_chk.c
libio/Makefile
libio/fileops.c
libio/genops.c
libio/iofdopen.c
libio/iofopen.c
libio/iofopncook.c
libio/iopopen.c
libio/iovdprintf.c
libio/libioP.h
libio/memstream.c
libio/obprintf.c
libio/oldfileops.c
libio/oldiofdopen.c
libio/oldiofopen.c
libio/oldiopopen.c
libio/strops.c
libio/vsnprintf.c
libio/vswprintf.c
libio/vtables.c [new file with mode: 0644]
libio/wfileops.c
libio/wmemstream.c
libio/wstrops.c
stdio-common/vfprintf.c
stdlib/strfmon_l.c

index 4bfee947a30d326e65d200e8756aaec09382600a..ce5070fe3956246e733f357bca7cbd61fdaf83d2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,77 @@
+2016-06-23  Florian Weimer  <fweimer@redhat.com>
+
+       [BZ #20191]
+       Implement vtable verification in libio.
+       * Makerules (shlib.lds): Place __libc_IO_vtables section.
+       * debug/obprintf_chk.c (_IO_obstack_jumps): Define as vtable.
+       * debug/vdprintf_chk.c (__vdprintf_chk): Call
+       _IO_new_file_init_internal instead of _IO_file_init.
+       * debug/vsnprintf_chk.c (_IO_strn_jumps): Define as vtable.
+       * debug/vsprintf_chk.c (_IO_str_chk_jumps): Likewise.
+       * libio/Makefile (routines): Add vtables.
+       * libio/libioP.h (_IO_JUMPS_FUNC): Call IO_validate_vtable.
+       (_IO_init): Remove, not for internal use.
+       (_IO_init_internal): Declare, internal replacement for _IO_init.
+       (_IO_file_init): Remove, not for internal use.
+       (_IO_new_file_init): Remove, not for internal use.
+       (_IO_new_file_init_internal): Declare, internal replacement for
+       _IO_new_file_init.
+       (_IO_old_file_init): Remove, not for internal use.
+       (_IO_old_file_init_internal): Declare, internal replacement for
+       _IO_old_file_init.
+       (_IO_str_init_static, _IO_str_init_readonly): Remove, not for
+       internal use.
+       (__libc_IO_vtables, IO_accept_foreign_vtables, _IO_vtable_check):
+       Declare.
+       (libio_vtable): New macro.
+       (IO_set_accept_foreign_vtables, _IO_validate_vtable): New inline
+       functions.
+       * libio/fileops.c (_IO_new_file_init_internal): Rename from
+       _IO_new_file_init.
+       (_IO_new_file_init): New externally visible wrapper which disables
+       vtable verification.
+       (_IO_file_jumps, _IO_file_jumps_mmap, _IO_file_jumps_maybe_mmap):
+       Define as vtables.
+       * libio/genops.c (_IO_init_internal): Rename from _IO_init.
+       (_IO_init): New externally visible wrapper which disables
+       vtable verification.
+       * libio/iofdopen.c (_IO_new_fdopen): Call
+       _IO_new_file_init_internal instead of _IO_file_init.  Adjust
+       comment.
+       * libio/iofopen.c (__fopen_internal): Call
+       _IO_new_file_init_internal instead of _IO_file_init.
+       * libio/iofopncook.c (_IO_cookie_jumps, _IO_old_cookie_jumps):
+       Define as vtables.
+       (_IO_cookie_init): Call _IO_init_internal instead of _IO_init,
+       _IO_new_file_init_internal instead of _IO_file_init.
+       * libio/iopopen.c (_IO_new_popen): Likewise.
+       (_IO_proc_jumps): Define as vtable.
+       * libio/iovdprintf.c (_IO_vdprintf): Call
+       _IO_new_file_init_internal instead of _IO_file_init.
+       * libio/memstream.c (_IO_mem_jumps): Define as vtable.
+       (__open_memstream): Call _IO_init_internal instead of _IO_init.
+       * libio/obprintf.c (_IO_obstack_jumps): Define as vtable.
+       * libio/oldfileops.c (_IO_old_file_init_internal): Rename from
+       _IO_old_file_init.
+       (_IO_old_file_init): New externally visible wrapper which disables
+       vtable verification.
+       (_IO_old_file_jumps): Define as vtable.
+       * libio/oldiofdopen.c (_IO_old_fdopen): Call
+       _IO_old_file_init_internal instead of _IO_old_file_init.
+       * libio/oldiofopen.c (_IO_old_fopen): Likewise.
+       * libio/oldiopopen.c (_IO_old_popen): Likewise.
+       (_IO_old_proc_jumps): Define as vtable.
+       * libio/strops.c (_IO_str_jumps, _IO_strn_jumps, _IO_wstrn_jumps):
+       Define as vtables.
+       * libio/vtables.c: New file.
+       * libio/wfileops.c (_IO_wfile_jumps, _IO_wfile_jumps_mmap)
+       (_IO_wfile_jumps_maybe_mmap): Define as vtables.
+       * libio/wmemstream.c (_IO_wmem_jumps): Define as vtable.
+       * libio/wstrops.c (_IO_wstr_jumps): Likewise.
+       * stdio-common/vfprintf.c (_IO_helper_jumps): Likewise.
+       * stdlib/strfmon_l.c (__vstrfmon_l): Call _IO_init_internal
+       instead of _IO_init.
+
 2016-06-23  Florian Weimer  <fweimer@redhat.com>
 
        * test-skeleton.c (xrealloc): Support deallocation with n == 0.
index 53eabfaba82a127221089112b40a51dc5e6949ea..f1ecd4039376328bda1fa4d9e6366c47f54d8bac 100644 (file)
--- a/Makerules
+++ b/Makerules
@@ -562,6 +562,9 @@ $(common-objpfx)shlib.lds: $(common-objpfx)config.make $(..)Makerules
                 PROVIDE(__start___libc_thread_subfreeres = .);\
                 __libc_thread_subfreeres : { *(__libc_thread_subfreeres) }\
                 PROVIDE(__stop___libc_thread_subfreeres = .);\
+                PROVIDE(__start___libc_IO_vtables = .);\
+                __libc_IO_vtables : { *(__libc_IO_vtables) }\
+                PROVIDE(__stop___libc_IO_vtables = .);\
                 /DISCARD/ : { *(.gnu.glibc-stub.*) }@'
        test -s $@T
        mv -f $@T $@
index 8469b5f675c6c282590efc0c2223d74e58104350..09655ba7a36e7a6777dcf966a35504f36d8c91e2 100644 (file)
@@ -35,7 +35,7 @@ struct _IO_obstack_file
   struct obstack *obstack;
 };
 
-extern const struct _IO_jump_t _IO_obstack_jumps attribute_hidden;
+extern const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden;
 
 int
 __obstack_vprintf_chk (struct obstack *obstack, int flags, const char *format,
index 05d0bcd7e7df6808ef915a3ba5a4852e074bcfea..ce0ddb028c00343d7ef1f0dddff849c0e746bced 100644 (file)
@@ -39,7 +39,7 @@ __vdprintf_chk (int d, int flags, const char *format, 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
index cc559d2b1d750ec7334679819f242b6b734730ff..a6bb051234726bbed1e67458b055bf0360c0d59b 100644 (file)
@@ -20,7 +20,7 @@
 #include "../libio/libioP.h"
 #include "../libio/strfile.h"
 
-extern const struct _IO_jump_t _IO_strn_jumps attribute_hidden;
+extern const struct _IO_jump_t _IO_strn_jumps libio_vtable attribute_hidden;
 
 /* Write formatted output into S, according to the format
    string FORMAT, writing no more than MAXLEN characters.  */
index aa1587ce6f3f7aed44c7d02a46269236e0568d3b..02e7372849e1400e50213be591e97585f02f4b12 100644 (file)
@@ -32,7 +32,7 @@ _IO_str_chk_overflow (_IO_FILE *fp, int c)
 }
 
 
-static const struct _IO_jump_t _IO_str_chk_jumps =
+static const struct _IO_jump_t _IO_str_chk_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_str_finish),
index 4189bc4ad0dd670d5f0deed6da030d8c68827c21..12589f287513bf1901b745bd9a06ac175817d7a7 100644 (file)
@@ -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 \
index 8e83b1cd7b64f53ac2394f28f7e4b7186e1c116d..13157354272ff9ab1832d4a619a81f05898fcd69 100644 (file)
@@ -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),
index 5803cbf04fb0f861381f7031dd570c0cb3b33d6e..6234bf9046369b437b5f9327d948a673155066fe 100644 (file)
@@ -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)
index e00f337521a24070fbb7f7b84fa2d68528b26828..a4b67579421a3008cccce6d6f79277747502cea7 100644 (file)
@@ -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;
 
index 13e3910b636b391e1cc87a32971c96cd00fdadd8..855fe2fae5bdc28f39b20c355f2b3a1ff3007bd8 100644 (file)
@@ -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
index ceb444af7e2a73d87e19ceea12720d12c2904a9c..ae5df1707af3b6e78fa7a9e1ffa119d9927fea11 100644 (file)
@@ -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),
index 9ddde234949d9d71a41357f9305617ac32bb4e74..d85370c01f1c704e6111330ab8b9a38891064f7d 100644 (file)
@@ -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),
index 8ca55fccae2abe7d59b854b54c6d6ed298619503..d279e34e876730d0d7243e25b13f7128ead9dd60 100644 (file)
@@ -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
index 8706af2d901a51a272175f20e9f2d1888094bb7a..54dc35cdb652151ff7f1e392e892e9b24ddc47da 100644 (file)
@@ -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;
+}
index 7fa5245e723dfdd8a30598766c3edbf662871058..e20b9c22502005afc96a71f38ae67b6d49380692 100644 (file)
@@ -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;
index aa17b46da8989b31fabb5fa6109219e2a0f2d2fe..d7144e8e3923047d0a63be8d9ff7a497b17145b6 100644 (file)
@@ -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),
index 4f3bdfe4892a36a9312442ceb39780258f1a5a40..26b2bf5cf137ad781a9db1fb3bbc13c408d995f1 100644 (file)
@@ -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),
index 33406ff24077ef0f6b916bc84d9698ec93c25dc3..59fcfa4bf188caffd7f57a42a8419b23a4be9acc 100644 (file)
@@ -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
index cc7c34282cae90d24c622a580bfd7aa377d9e320..ce6a33d624c47faba504d8e0512544a8170b4229 100644 (file)
@@ -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
index ea75b4fb9007f08536117daf5542319b87e17173..bce74b1a5bd99444fe206ef04e4a611131d73825 100644 (file)
@@ -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),
index 0932d4c1b18a6a2c2df2559305ad4af04badf0a4..2ba3704dd233d8d812062d70552f0da2b6cc92e5 100644 (file)
@@ -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),
index f1063a17bec21d81911e09cb025d9b312a6427b2..ee7f75290f1336fc0df0363a797b3601f032563d 100644 (file)
@@ -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),
index b90441ad77ccf06235c7925f6c3dce00f44947cb..df7b719149b3bfec2f25a38ec94796af65d68036 100644 (file)
@@ -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 (file)
index 0000000..e364ea0
--- /dev/null
@@ -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");
+}
index df1fbdaf8b7b813788b958ffd79af8eed298870a..d88d08a2422d7c2faa05e373cacdb5b69da7a1a2 100644 (file)
@@ -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),
index 1bdbae9fe868388496d09aa6450c9a5e6a4878f6..bf2a50b5232b9d0ff1bac74604356fe543baab40 100644 (file)
@@ -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),
index 8d0e1cb331081935d2ffd6403e7247396e519f7d..09fa543f77932deacc82cd5625403111f977c536 100644 (file)
@@ -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),
index f24020a58599e16ca2f8d41836793bb8d42d3bfd..6e428e90444bbe4b9934528a2b08d5604be02769 100644 (file)
@@ -2240,7 +2240,7 @@ _IO_helper_overflow (_IO_FILE *s, int c)
 }
 
 #ifdef COMPILE_WPRINTF
-static const struct _IO_jump_t _IO_helper_jumps =
+static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT (finish, _IO_wdefault_finish),
@@ -2262,7 +2262,7 @@ static const struct _IO_jump_t _IO_helper_jumps =
   JUMP_INIT (stat, _IO_default_stat)
 };
 #else
-static const struct _IO_jump_t _IO_helper_jumps =
+static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT (finish, _IO_default_finish),
index 5851a5b94aac1c6d895a910e586fe7e053f3c866..39b51c017b7bac4960c156b032580cb0fdc647e6 100644 (file)
@@ -512,7 +512,7 @@ __vstrfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format,
 #ifdef _IO_MTSAFE_IO
       f._sbf._f._lock = NULL;
 #endif
-      _IO_init (&f._sbf._f, 0);
+      _IO_init_internal (&f._sbf._f, 0);
       _IO_JUMPS (&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