diff options
| author | Florian Weimer <fweimer@redhat.com> | 2016-06-23 20:01:40 +0200 |
|---|---|---|
| committer | Florian Weimer <fweimer@redhat.com> | 2016-06-23 20:01:52 +0200 |
| commit | db3476aff19b75c4fdefbe65fcd5f0a90588ba51 (patch) | |
| tree | 5cefd4971a0e6b5d5f9eacf6d3532c6aedc7b352 | |
| parent | 64ba17317dc9343f0958755ad04af71ec3da637b (diff) | |
| download | glibc-db3476aff19b75c4fdefbe65fcd5f0a90588ba51.tar.xz glibc-db3476aff19b75c4fdefbe65fcd5f0a90588ba51.zip | |
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.
| -rw-r--r-- | ChangeLog | 74 | ||||
| -rw-r--r-- | Makerules | 3 | ||||
| -rw-r--r-- | debug/obprintf_chk.c | 2 | ||||
| -rw-r--r-- | debug/vdprintf_chk.c | 2 | ||||
| -rw-r--r-- | debug/vsnprintf_chk.c | 2 | ||||
| -rw-r--r-- | debug/vsprintf_chk.c | 2 | ||||
| -rw-r--r-- | libio/Makefile | 2 | ||||
| -rw-r--r-- | libio/fileops.c | 18 | ||||
| -rw-r--r-- | libio/genops.c | 10 | ||||
| -rw-r--r-- | libio/iofdopen.c | 12 | ||||
| -rw-r--r-- | libio/iofopen.c | 2 | ||||
| -rw-r--r-- | libio/iofopncook.c | 8 | ||||
| -rw-r--r-- | libio/iopopen.c | 6 | ||||
| -rw-r--r-- | libio/iovdprintf.c | 2 | ||||
| -rw-r--r-- | libio/libioP.h | 76 | ||||
| -rw-r--r-- | libio/memstream.c | 4 | ||||
| -rw-r--r-- | libio/obprintf.c | 2 | ||||
| -rw-r--r-- | libio/oldfileops.c | 12 | ||||
| -rw-r--r-- | libio/oldiofdopen.c | 2 | ||||
| -rw-r--r-- | libio/oldiofopen.c | 2 | ||||
| -rw-r--r-- | libio/oldiopopen.c | 4 | ||||
| -rw-r--r-- | libio/strops.c | 2 | ||||
| -rw-r--r-- | libio/vsnprintf.c | 2 | ||||
| -rw-r--r-- | libio/vswprintf.c | 2 | ||||
| -rw-r--r-- | libio/vtables.c | 70 | ||||
| -rw-r--r-- | libio/wfileops.c | 6 | ||||
| -rw-r--r-- | libio/wmemstream.c | 2 | ||||
| -rw-r--r-- | libio/wstrops.c | 2 | ||||
| -rw-r--r-- | stdio-common/vfprintf.c | 4 | ||||
| -rw-r--r-- | stdlib/strfmon_l.c | 2 |
30 files changed, 279 insertions, 60 deletions
@@ -1,5 +1,79 @@ 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. 2016-06-23 Florian Weimer <fweimer@redhat.com> @@ -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); |
