about summary refs log tree commit diff
path: root/libio/libioP.h
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2016-06-23 20:01:40 +0200
committerFlorian Weimer <fweimer@redhat.com>2016-06-23 20:01:52 +0200
commitdb3476aff19b75c4fdefbe65fcd5f0a90588ba51 (patch)
tree5cefd4971a0e6b5d5f9eacf6d3532c6aedc7b352 /libio/libioP.h
parent64ba17317dc9343f0958755ad04af71ec3da637b (diff)
downloadglibc-db3476aff19b75c4fdefbe65fcd5f0a90588ba51.tar.gz
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.
Diffstat (limited to 'libio/libioP.h')
-rw-r--r--libio/libioP.h76
1 files changed, 63 insertions, 13 deletions
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;
+}