mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-27 03:33:33 +08:00
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.
This commit is contained in:
parent
64ba17317d
commit
db3476aff1
74
ChangeLog
74
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.
|
||||
|
@ -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 $@
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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),
|
||||
|
@ -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 \
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
70
libio/vtables.c
Normal file
70
libio/vtables.c
Normal file
@ -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");
|
||||
}
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user