| commit db3476aff19b75c4fdefbe65fcd5f0a90588ba51 |
| Author: Florian Weimer <fweimer@redhat.com> |
| Date: Thu Jun 23 20:01:40 2016 +0200 |
| |
| libio: Implement vtable verification [BZ #20191] |
| |
| 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. |
| |
| diff -rupN a/Makerules b/Makerules |
| |
| |
| @@ -520,6 +520,9 @@ $(common-objpfx)shlib.lds: $(common-objp |
| 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 $@ |
| diff -rupN a/debug/obprintf_chk.c b/debug/obprintf_chk.c |
| |
| |
| @@ -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, |
| diff -rupN a/debug/vdprintf_chk.c b/debug/vdprintf_chk.c |
| |
| |
| @@ -39,7 +39,7 @@ __vdprintf_chk (int d, int flags, const |
| #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 -rupN a/debug/vsnprintf_chk.c b/debug/vsnprintf_chk.c |
| |
| |
| @@ -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. */ |
| diff -rupN a/debug/vsprintf_chk.c b/debug/vsprintf_chk.c |
| |
| |
| @@ -34,7 +34,7 @@ _IO_str_chk_overflow (fp, 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), |
| diff -rupN a/libio/Makefile b/libio/Makefile |
| |
| |
| @@ -44,7 +44,7 @@ routines := \ |
| __fbufsize __freading __fwriting __freadable __fwritable __flbf \ |
| __fpurge __fpending __fsetlocking \ |
| \ |
| - libc_fatal fmemopen |
| + libc_fatal fmemopen vtables |
| |
| include ../Makeconfig |
| |
| diff -rupN a/libio/fileops.c b/libio/fileops.c |
| |
| |
| @@ -139,7 +139,7 @@ extern struct __gconv_trans_data __libio |
| |
| |
| void |
| -_IO_new_file_init (fp) |
| +_IO_new_file_init_internal (fp) |
| struct _IO_FILE_plus *fp; |
| { |
| /* POSIX.1 allows another file handle to be used to change the position |
| @@ -151,7 +151,15 @@ _IO_new_file_init (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 (fp) |
| @@ -1586,7 +1594,7 @@ versioned_symbol (libc, _IO_new_file_wri |
| 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), |
| @@ -1611,7 +1619,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), |
| @@ -1635,7 +1643,7 @@ const struct _IO_jump_t _IO_file_jumps_m |
| 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 -rupN a/libio/genops.c b/libio/genops.c |
| |
| |
| @@ -595,13 +595,19 @@ _IO_default_doallocate (fp) |
| libc_hidden_def (_IO_default_doallocate) |
| |
| void |
| -_IO_init (fp, flags) |
| +_IO_init_internal (fp, flags) |
| _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 (fp, flags) |
| diff -rupN a/libio/iofdopen.c b/libio/iofdopen.c |
| |
| |
| @@ -158,15 +158,15 @@ _IO_new_fdopen (fd, 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 -rupN a/libio/iofopen.c b/libio/iofopen.c |
| |
| |
| @@ -83,7 +83,7 @@ __fopen_internal (filename, mode, 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 -rupN a/libio/iofopncook.c b/libio/iofopncook.c |
| |
| |
| @@ -116,7 +116,7 @@ _IO_cookie_seekoff (fp, offset, dir, mod |
| } |
| |
| |
| -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), |
| @@ -144,13 +144,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; |
| 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); |
| @@ -235,7 +235,7 @@ _IO_old_cookie_seek (fp, offset, 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 -rupN a/libio/iopopen.c b/libio/iopopen.c |
| |
| |
| @@ -292,9 +292,9 @@ _IO_new_popen (command, 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 |
| @@ -350,7 +350,7 @@ _IO_new_proc_close (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 -rupN a/libio/iovdprintf.c b/libio/iovdprintf.c |
| |
| |
| @@ -42,7 +42,7 @@ _IO_vdprintf (d, format, 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 -rupN a/libio/libioP.h b/libio/libioP.h |
| |
| |
| @@ -108,11 +108,12 @@ extern "C" { |
| |
| #if _IO_JUMPS_OFFSET |
| # define _IO_JUMPS_FUNC(THIS) \ |
| - (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS ((struct _IO_FILE_plus *) (THIS)) \ |
| - + (THIS)->_vtable_offset)) |
| + (IO_validate_vtable \ |
| + (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS ((struct _IO_FILE_plus *) (THIS)) \ |
| + + (THIS)->_vtable_offset))) |
| # define _IO_vtable_offset(THIS) (THIS)->_vtable_offset |
| #else |
| -# define _IO_JUMPS_FUNC(THIS) _IO_JUMPS ((struct _IO_FILE_plus *) (THIS)) |
| +# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS ((struct _IO_FILE_plus *) (THIS)))) |
| # define _IO_vtable_offset(THIS) 0 |
| #endif |
| #define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS) |
| @@ -361,8 +362,7 @@ extern void _IO_switch_to_main_get_area |
| 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; |
| @@ -570,8 +570,6 @@ extern int _IO_file_underflow_maybe_mmap |
| 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); |
| @@ -597,7 +595,8 @@ extern _IO_FILE* _IO_new_file_fopen (_IO |
| 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 *); |
| @@ -612,7 +611,8 @@ extern _IO_off64_t _IO_old_file_seekoff |
| 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); |
| @@ -656,10 +656,6 @@ extern void _IO_str_finish (_IO_FILE *, |
| |
| /* 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. */ |
| @@ -913,3 +909,57 @@ _IO_acquire_lock_clear_flags2_fct (_IO_F |
| | _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 -rupN a/libio/memstream.c b/libio/memstream.c |
| |
| |
| @@ -33,7 +33,7 @@ static int _IO_mem_sync (_IO_FILE* fp) _ |
| 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), |
| @@ -85,7 +85,7 @@ open_memstream (bufloc, sizeloc) |
| buf = calloc (1, _IO_BUFSIZ); |
| if (buf == NULL) |
| return NULL; |
| - _IO_init (&new_f->fp._sf._sbf._f, 0); |
| + _IO_init_internal (&new_f->fp._sf._sbf._f, 0); |
| _IO_JUMPS ((struct _IO_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 -rupN a/libio/obprintf.c b/libio/obprintf.c |
| |
| |
| @@ -91,7 +91,7 @@ _IO_obstack_xsputn (_IO_FILE *fp, const |
| |
| |
| /* 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 -rupN a/libio/oldfileops.c b/libio/oldfileops.c |
| |
| |
| @@ -113,7 +113,7 @@ extern int errno; |
| |
| void |
| attribute_compat_text_section |
| -_IO_old_file_init (fp) |
| +_IO_old_file_init_internal (fp) |
| struct _IO_FILE_plus *fp; |
| { |
| /* POSIX.1 allows another file handle to be used to change the position |
| @@ -138,6 +138,14 @@ _IO_old_file_init (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 (fp) |
| @@ -776,7 +784,7 @@ _IO_old_file_xsputn (f, data, 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 -rupN a/libio/oldiofdopen.c b/libio/oldiofdopen.c |
| |
| |
| @@ -114,7 +114,7 @@ _IO_old_fdopen (fd, mode) |
| #endif |
| _IO_old_init (&new_f->fp.file._file, 0); |
| _IO_JUMPS ((struct _IO_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 -rupN a/libio/oldiofopen.c b/libio/oldiofopen.c |
| |
| |
| @@ -53,7 +53,7 @@ _IO_old_fopen (filename, mode) |
| #endif |
| _IO_old_init (&new_f->fp.file._file, 0); |
| _IO_JUMPS ((struct _IO_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 -rupN a/libio/oldiopopen.c b/libio/oldiopopen.c |
| |
| |
| @@ -216,7 +216,7 @@ _IO_old_popen (command, mode) |
| fp = &new_f->fpx.file.file._file; |
| _IO_old_init (fp, 0); |
| _IO_JUMPS ((struct _IO_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 |
| @@ -273,7 +273,7 @@ _IO_old_proc_close (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 -rupN a/libio/strops.c b/libio/strops.c |
| |
| |
| @@ -345,7 +345,7 @@ _IO_str_finish (fp, 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 -rupN a/libio/vsnprintf.c b/libio/vsnprintf.c |
| |
| |
| @@ -66,7 +66,7 @@ _IO_strn_overflow (fp, 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 -rupN a/libio/vswprintf.c b/libio/vswprintf.c |
| |
| |
| @@ -65,7 +65,7 @@ _IO_wstrn_overflow (fp, 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 -rupN a/libio/vtables.c 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 -rupN a/libio/wfileops.c b/libio/wfileops.c |
| |
| |
| @@ -1035,7 +1035,7 @@ _IO_wfile_xsputn (f, data, 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), |
| @@ -1061,7 +1061,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), |
| @@ -1085,7 +1085,7 @@ const struct _IO_jump_t _IO_wfile_jumps_ |
| 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 -rupN a/libio/wmemstream.c b/libio/wmemstream.c |
| |
| |
| @@ -34,7 +34,7 @@ static int _IO_wmem_sync (_IO_FILE* fp) |
| 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 -rupN a/libio/wstrops.c b/libio/wstrops.c |
| |
| |
| @@ -347,7 +347,7 @@ _IO_wstr_finish (fp, 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), |
| diff -rupN a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c |
| |
| |
| @@ -2234,7 +2234,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), |
| @@ -2256,7 +2256,7 @@ static const struct _IO_jump_t _IO_helpe |
| 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), |
| diff -rupN a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c |
| |
| |
| @@ -515,7 +515,7 @@ __vstrfmon_l (char *s, size_t maxsize, _ |
| #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 |