diff --git a/ChangeLog b/ChangeLog index 4bfee947a3..ce5070fe39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,77 @@ +2016-06-23 Florian Weimer + + [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 * test-skeleton.c (xrealloc): Support deallocation with n == 0. diff --git a/Makerules b/Makerules index 53eabfaba8..f1ecd40393 100644 --- 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 $@ diff --git a/debug/obprintf_chk.c b/debug/obprintf_chk.c index 8469b5f675..09655ba7a3 100644 --- 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 --git a/debug/vdprintf_chk.c b/debug/vdprintf_chk.c index 05d0bcd7e7..ce0ddb028c 100644 --- a/debug/vdprintf_chk.c +++ b/debug/vdprintf_chk.c @@ -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 diff --git a/debug/vsnprintf_chk.c b/debug/vsnprintf_chk.c index cc559d2b1d..a6bb051234 100644 --- 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 --git a/debug/vsprintf_chk.c b/debug/vsprintf_chk.c index aa1587ce6f..02e7372849 100644 --- a/debug/vsprintf_chk.c +++ b/debug/vsprintf_chk.c @@ -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), 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 + . */ + +#include +#include +#include + +#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), diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c index f24020a585..6e428e9044 100644 --- a/stdio-common/vfprintf.c +++ b/stdio-common/vfprintf.c @@ -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), diff --git a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c index 5851a5b94a..39b51c017b 100644 --- a/stdlib/strfmon_l.c +++ b/stdlib/strfmon_l.c @@ -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