diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 33 | ||||
-rw-r--r-- | gcc/Makefile.in | 8 | ||||
-rw-r--r-- | gcc/aclocal.m4 | 10 | ||||
-rw-r--r-- | gcc/common.opt | 24 | ||||
-rw-r--r-- | gcc/config/gnu-user.h | 17 | ||||
-rw-r--r-- | gcc/cp/ChangeLog | 32 | ||||
-rw-r--r-- | gcc/cp/Make-lang.in | 10 | ||||
-rw-r--r-- | gcc/cp/class.c | 3 | ||||
-rw-r--r-- | gcc/cp/config-lang.in | 2 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 12 | ||||
-rw-r--r-- | gcc/cp/decl2.c | 39 | ||||
-rw-r--r-- | gcc/cp/init.c | 3 | ||||
-rw-r--r-- | gcc/cp/mangle.c | 30 | ||||
-rw-r--r-- | gcc/cp/vtable-class-hierarchy.c | 1353 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 53 | ||||
-rw-r--r-- | gcc/flag-types.h | 6 | ||||
-rw-r--r-- | gcc/gcc.c | 12 | ||||
-rw-r--r-- | gcc/output.h | 4 | ||||
-rw-r--r-- | gcc/passes.def | 1 | ||||
-rw-r--r-- | gcc/timevar.def | 1 | ||||
-rw-r--r-- | gcc/tree-pass.h | 1 | ||||
-rw-r--r-- | gcc/tree.h | 4 | ||||
-rw-r--r-- | gcc/varasm.c | 46 | ||||
-rw-r--r-- | gcc/vtable-verify.c | 793 | ||||
-rw-r--r-- | gcc/vtable-verify.h | 141 |
25 files changed, 2619 insertions, 19 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c62cb892163..f3ce757fd75 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,36 @@ +2013-08-06 Caroline Tice <cmtice@google.com> + + * gcc.c (VTABLE_VERIFICATION_SPEC): New definition. + (LINK_COMMAND_SPEC): Add VTABLE_VERIFICATION_SPEC. + * tree-pass.h: Add pass_vtable_verify. + * varasm.c (assemble_variable): Add code to properly set the comdat + section and name for the .vtable_map_vars section. + (assemble_vtyv_preinit_initializer): New function. + (default_sectin_type_flags): Make sure .vtable_map_vars section has + LINK_ONCE flag. + * output.h: Add function decl for assemble_vtv_preinit_initializer. + * vtable-verify.c: New file. + * vtable-verify.h: New file. + * flag-types.h (enum vtv_priority): Defintions for flag_vtable_verify + initialiation levels. + * timevar.def (TV_VTABLE_VERIFICATION): New definition. + * passes.def: Insert pass_vtable_verify. + * aclocal.m4: Reorder includes. + * doc/invoke.texi: Document the -fvtable-verify=, -fvtv-debug, and + -fvtv-counts options. + * config/gnu-user.h (GNU_USER_TARGET_STARTFILE_SPEC): Add vtv_start*.o, + as appropriate, if -fvtable-verify=... is used. + (GNU_USER_TARGET_ENDFILE_SPEC): Add vtv_end*.o as appropriate, if + -fvtable-verify=... is used. + * Makefile.in (OBJS): Add vtable-verify.o to list. + (vtable-verify.o): Add new build rule. + (GTFILES): Add vtable-verify.c to list. + * common.opt (fvtable-verify=): New flag. + (vtv_priority): Values for fvtable-verify= flag. + (fvtv-counts): New flag. + (fvtv-debug): New flag. + * tree.h (save_vtable_map_decl): New extern function decl. + 2013-08-07 David Malcolm <dmalcolm@redhat.com> * config/rl78/rl78.c (rl78_devirt_pass): Convert from a struct to... diff --git a/gcc/Makefile.in b/gcc/Makefile.in index afce540eed9..6ddc8534f84 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1473,6 +1473,7 @@ OBJS = \ varasm.o \ varpool.o \ vmsdbgout.o \ + vtable-verify.o \ web.o \ xcoffout.o \ $(out_object_file) \ @@ -2644,6 +2645,12 @@ tree-vectorizer.o: tree-vectorizer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(DUMPFILE_H) $(TM_H) $(GGC_H) $(TREE_H) $(TREE_FLOW_H) \ $(CFGLOOP_H) $(TREE_PASS_H) $(TREE_VECTORIZER_H) \ $(TREE_PRETTY_PRINT_H) +vtable-verify.o: vtable-verify.c vtable-verify.h $(CONFIG_H) \ + $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) cp/cp-tree.h $(TM_P_H) \ + $(BASIC_BLOCK_H) output.h $(TREE_FLOW_H) $(TREE_DUMP_H) $(TREE_PASS_H) \ + $(TIMEVAR_H) $(CFGLOOP_H) $(FLAGS_H) $(TREE_INLINE_H) $(SCEV_H) \ + $(DIAGNOSTIC_CORE_H) $(GIMPLE_PRETTY_PRINT_H) toplev.h langhooks.h \ + gt-vtable-verify.h tree-loop-distribution.o: tree-loop-distribution.c $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TREE_FLOW_H) $(CFGLOOP_H) $(TREE_DATA_REF_H) $(TREE_PASS_H) tree-parloops.o: tree-parloops.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ @@ -3817,6 +3824,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/lto-streamer.h \ $(srcdir)/target-globals.h \ $(srcdir)/ipa-inline.h \ + $(srcdir)/vtable-verify.c \ $(srcdir)/asan.c \ $(srcdir)/tsan.c \ @all_gtfiles@ diff --git a/gcc/aclocal.m4 b/gcc/aclocal.m4 index a992c3a96ed..1f3ce9d6935 100644 --- a/gcc/aclocal.m4 +++ b/gcc/aclocal.m4 @@ -99,11 +99,6 @@ m4_define([AC_PROG_CC], [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])]) ]) -m4_include([../libtool.m4]) -m4_include([../ltoptions.m4]) -m4_include([../ltsugar.m4]) -m4_include([../ltversion.m4]) -m4_include([../lt~obsolete.m4]) m4_include([../config/acx.m4]) m4_include([../config/codeset.m4]) m4_include([../config/dfp.m4]) @@ -119,4 +114,9 @@ m4_include([../config/picflag.m4]) m4_include([../config/progtest.m4]) m4_include([../config/stdint.m4]) m4_include([../config/warnings.m4]) +m4_include([../libtool.m4]) +m4_include([../ltoptions.m4]) +m4_include([../ltsugar.m4]) +m4_include([../ltversion.m4]) +m4_include([../lt~obsolete.m4]) m4_include([acinclude.m4]) diff --git a/gcc/common.opt b/gcc/common.opt index 4c7933e587c..90822801551 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2311,6 +2311,30 @@ Enum(symbol_visibility) String(hidden) Value(VISIBILITY_HIDDEN) EnumValue Enum(symbol_visibility) String(protected) Value(VISIBILITY_PROTECTED) +fvtable-verify= +Common Joined RejectNegative Enum(vtv_priority) Var(flag_vtable_verify) Init(VTV_NO_PRIORITY) +Validate vtable pointers before using them. + +Enum +Name(vtv_priority) Type(enum vtv_priority) UnknownError(unknown vtable verify initialization priority %qs) + +EnumValue +Enum(vtv_priority) String(none) Value(VTV_NO_PRIORITY) + +EnumValue +Enum(vtv_priority) String(std) Value(VTV_STANDARD_PRIORITY) + +EnumValue +Enum(vtv_priority) String(preinit) Value(VTV_PREINIT_PRIORITY) + +fvtv-counts +Common Var(flag_vtv_counts) +Output vtable verification counters. + +fvtv-debug +Common Var(flag_vtv_debug) +Output vtable verification pointer sets information. + fvpt Common Report Var(flag_value_profile_transformations) Optimization Use expression value profiles in optimizations diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h index bcdf0e6cc5a..2c48c18655a 100644 --- a/gcc/config/gnu-user.h +++ b/gcc/config/gnu-user.h @@ -39,15 +39,21 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see the GNU userspace magical crtbegin.o file (see crtstuff.c) which provides part of the support for getting C++ file-scope static object constructed before entering `main'. */ - + #if defined HAVE_LD_PIE #define GNU_USER_TARGET_STARTFILE_SPEC \ "%{!shared: %{pg|p|profile:gcrt1.o%s;pie:Scrt1.o%s;:crt1.o%s}} \ - crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \ + %{fvtable-verify=none:%s; \ + fvtable-verify=preinit:vtv_start_preinit.o%s; \ + fvtable-verify=std:vtv_start.o%s}" #else #define GNU_USER_TARGET_STARTFILE_SPEC \ "%{!shared: %{pg|p|profile:gcrt1.o%s;:crt1.o%s}} \ - crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \ + %{fvtable-verify=none:%s; \ + fvtable-verify=preinit:vtv_start_preinit.o%s; \ + fvtable-verify=std:vtv_start.o%s}" #endif #undef STARTFILE_SPEC #define STARTFILE_SPEC GNU_USER_TARGET_STARTFILE_SPEC @@ -59,7 +65,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see GNU userspace "finalizer" file, `crtn.o'. */ #define GNU_USER_TARGET_ENDFILE_SPEC \ - "%{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" + "%{fvtable-verify=none:%s; \ + fvtable-verify=preinit:vtv_end_preinit.o%s; \ + fvtable-verify=std:vtv_end.o%s} \ + %{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" #undef ENDFILE_SPEC #define ENDFILE_SPEC GNU_USER_TARGET_ENDFILE_SPEC diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 5083f7016cc..624af2b601c 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,35 @@ +2013-08-06 Caroline Tice <cmtice@google.com> + + * Make-lang.in (*CXX_AND_OBJCXX_OBJS): Add vtable-class-hierarchy.o to + list. + (vtable-class-hierarchy.o): Add build rule. + * cp-tree.h (vtv_start_verification_constructor_init_function): New + extern function decl. + (vtv_finish_verification_constructor_init_function): New extern + function decl. + (build_vtbl_address): New extern function decl. + (get_mangled_vtable_map_var_name): New extern function decl. + (vtv_compute_class_hierarchy_transitive_closure): New extern function + decl. + (vtv_generate_init_routine): New extern function decl. + (vtv_save_class_info): New extern function decl. + (vtv_recover_class_info): New extern function decl. + (vtv_build_vtable_verify_fndecl): New extern function decl. + * class.c (finish_struct_1): Add call to vtv_save_class_info if + flag_vtable_verify is true. + * config-lang.in: Add vtable-class-hierarchy.c to gtfiles list. + * vtable-class-hierarchy.c: New file. + * mangle.c (get_mangled_vtable_map_var_name): New function. + * decl2.c (start_objects): Update function comment. + (cp_write_global_declarations): Call vtv_recover_class_info, + vtv_compute_class_hierarchy_transitive_closure and + vtv_build_vtable_verify_fndecl, before calling + finalize_compilation_unit, and call vtv_generate_init_rount after, IFF + flag_vtable_verify is true. + (vtv_start_verification_constructor_init_function): New function. + (vtv_finish_verification_constructor_init_function): New function. + * init.c (build_vtbl_address): Remove static qualifier from function. + 2013-08-06 Jason Merrill <jason@redhat.com> PR c++/57825 diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index 2cb919a2172..2c1774f1e50 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -80,7 +80,8 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \ cp/typeck.o cp/cvt.o cp/except.o cp/friend.o cp/init.o cp/method.o \ cp/search.o cp/semantics.o cp/tree.o cp/repo.o cp/dump.o cp/optimize.o \ cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \ - cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.o $(CXX_C_OBJS) + cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.o \ + cp/vtable-class-hierarchy.o $(CXX_C_OBJS) # Language-specific object files for C++. CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS) @@ -341,7 +342,12 @@ cp/parser.o: cp/parser.c $(CXX_TREE_H) $(TM_H) $(DIAGNOSTIC_CORE_H) \ c-family/c-objc.h tree-pretty-print.h $(CXX_PARSER_H) $(TIMEVAR_H) cp/cp-gimplify.o: cp/cp-gimplify.c $(CXX_TREE_H) $(C_COMMON_H) \ $(TM_H) coretypes.h pointer-set.h tree-iterator.h $(SPLAY_TREE_H) - +cp/vtable-class-hierarchy.o: cp/vtable-class-hierarchy.c \ + $(TM_H) $(TIMEVAR_H) $(CXX_TREE_H) intl.h $(CXX_PARSER_H) cp/decl.h \ + $(FLAGS_H) $(DIAGNOSTIC_CORE_H) output.h $(CGRAPH_H) c-family/c-common.h \ + c-family/c-objc.h $(PLUGIN_H) \ + tree-iterator.h vtable-verify.h $(GIMPLE_H) \ + gt-cp-vtable-class-hierarchy.h cp/name-lookup.o: cp/name-lookup.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(CXX_TREE_H) $(TIMEVAR_H) gt-cp-name-lookup.h $(PARAMS_H) \ $(DIAGNOSTIC_CORE_H) $(FLAGS_H) debug.h pointer-set.h diff --git a/gcc/cp/class.c b/gcc/cp/class.c index f0c515269e2..2f08d5f0c7c 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -6485,6 +6485,9 @@ finish_struct_1 (tree t) maybe_suppress_debug_info (t); + if (flag_vtable_verify) + vtv_save_class_info (t); + dump_class_hierarchy (t); /* Finish debugging output for this type. */ diff --git a/gcc/cp/config-lang.in b/gcc/cp/config-lang.in index 1597bf97017..4ea9b4d9a2b 100644 --- a/gcc/cp/config-lang.in +++ b/gcc/cp/config-lang.in @@ -29,4 +29,4 @@ compilers="cc1plus\$(exeext)" target_libs="target-libstdc++-v3" -gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c" +gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c" diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e4363903c15..86727398fbc 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5298,6 +5298,8 @@ extern void note_vague_linkage_fn (tree); extern tree build_artificial_parm (tree, tree); extern bool possibly_inlined_p (tree); extern int parm_index (tree); +extern tree vtv_start_verification_constructor_init_function (void); +extern tree vtv_finish_verification_constructor_init_function (tree); /* in error.c */ extern void init_error (void); @@ -5388,6 +5390,7 @@ extern tree build_java_class_ref (tree); extern tree integral_constant_value (tree); extern tree decl_constant_value_safe (tree); extern int diagnose_uninitialized_cst_or_ref_member (tree, bool, bool); +extern tree build_vtbl_address (tree); /* in lex.c */ extern void cxx_dup_lang_specific_decl (tree); @@ -5617,7 +5620,6 @@ extern tree copied_binfo (tree, tree); extern tree original_binfo (tree, tree); extern int shared_member_p (tree); - /* The representation of a deferred access check. */ typedef struct GTY(()) deferred_access_check { @@ -6112,6 +6114,7 @@ extern tree mangle_tls_init_fn (tree); extern tree mangle_tls_wrapper_fn (tree); extern bool decl_tls_wrapper_p (tree); extern tree mangle_ref_init_variable (tree); +extern char * get_mangled_vtable_map_var_name (tree); /* in dump.c */ extern bool cp_dump_tree (void *, tree); @@ -6144,6 +6147,13 @@ extern bool cxx_omp_privatize_by_reference (const_tree); extern void suggest_alternatives_for (location_t, tree); extern tree strip_using_decl (tree); +/* in vtable-class-hierarchy.c */ +extern void vtv_compute_class_hierarchy_transitive_closure (void); +extern void vtv_generate_init_routine (void); +extern void vtv_save_class_info (tree); +extern void vtv_recover_class_info (void); +extern void vtv_build_vtable_verify_fndecl (void); + /* In cp/cp-array-notations.c */ extern tree expand_array_notation_exprs (tree); bool cilkplus_an_triplet_types_ok_p (location_t, tree, tree, tree, diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 1573cede899..d5d29127cfd 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -3039,7 +3039,8 @@ generate_tls_wrapper (tree fn) } /* Start the process of running a particular set of global constructors - or destructors. Subroutine of do_[cd]tors. */ + or destructors. Subroutine of do_[cd]tors. Also called from + vtv_start_verification_constructor_init_function. */ static tree start_objects (int method_type, int initp) @@ -4353,8 +4354,25 @@ cp_write_global_declarations (void) timevar_stop (TV_PHASE_DEFERRED); timevar_start (TV_PHASE_OPT_GEN); + if (flag_vtable_verify) + { + vtv_recover_class_info (); + vtv_compute_class_hierarchy_transitive_closure (); + vtv_build_vtable_verify_fndecl (); + } + finalize_compilation_unit (); + if (flag_vtable_verify) + { + /* Generate the special constructor initialization function that + calls __VLTRegisterPairs, and give it a very high + initialization priority. This must be done after + finalize_compilation_unit so that we have accurate + information about which vtable will actually be emitted. */ + vtv_generate_init_routine (); + } + timevar_stop (TV_PHASE_OPT_GEN); timevar_start (TV_PHASE_CHECK_DBGINFO); @@ -4731,4 +4749,23 @@ mark_used (tree decl) return mark_used (decl, tf_warning_or_error); } +tree +vtv_start_verification_constructor_init_function (void) +{ + return start_objects ('I', MAX_RESERVED_INIT_PRIORITY - 1); +} + +tree +vtv_finish_verification_constructor_init_function (tree function_body) +{ + tree fn; + + finish_compound_stmt (function_body); + fn = finish_function (0); + DECL_STATIC_CONSTRUCTOR (fn) = 1; + decl_init_priority_insert (fn, MAX_RESERVED_INIT_PRIORITY - 1); + + return fn; +} + #include "gt-cp-decl2.h" diff --git a/gcc/cp/init.c b/gcc/cp/init.c index f346d2fbff5..3ec32c580ac 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -43,7 +43,6 @@ static tree initializing_context (tree); static void expand_cleanup_for_base (tree, tree); static tree dfs_initialize_vtbl_ptrs (tree, void *); static tree build_field_list (tree, tree, int *); -static tree build_vtbl_address (tree); static int diagnose_uninitialized_cst_or_ref_member_1 (tree, tree, bool, bool); /* We are about to generate some complex initialization code. @@ -1098,7 +1097,7 @@ emit_mem_initializers (tree mem_inits) /* Returns the address of the vtable (i.e., the value that should be assigned to the vptr) for BINFO. */ -static tree +tree build_vtbl_address (tree binfo) { tree binfo_for = binfo; diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index 3cfca583cb0..8c11ba8d394 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -3888,4 +3888,34 @@ write_java_integer_type_codes (const tree type) gcc_unreachable (); } +/* Given a CLASS_TYPE, such as a record for std::bad_exception this + function generates a mangled name for the vtable map variable of + the class type. For example, if the class type is + "std::bad_exception", the mangled name for the class is + "St13bad_exception". This function would generate the name + "_ZN4_VTVISt13bad_exceptionE12__vtable_mapE", which unmangles as: + "_VTV<std::bad_exception>::__vtable_map". */ + + +char * +get_mangled_vtable_map_var_name (tree class_type) +{ + char *var_name = NULL; + const char *prefix = "_ZN4_VTVI"; + const char *postfix = "E12__vtable_mapE"; + + gcc_assert (TREE_CODE (class_type) == RECORD_TYPE); + + tree class_id = DECL_ASSEMBLER_NAME (TYPE_NAME (class_type)); + unsigned int len = strlen (IDENTIFIER_POINTER (class_id)) + + strlen (prefix) + + strlen (postfix) + 1; + + var_name = (char *) xmalloc (len); + + sprintf (var_name, "%s%s%s", prefix, IDENTIFIER_POINTER (class_id), postfix); + + return var_name; +} + #include "gt-cp-mangle.h" diff --git a/gcc/cp/vtable-class-hierarchy.c b/gcc/cp/vtable-class-hierarchy.c new file mode 100644 index 00000000000..479447aa7d0 --- /dev/null +++ b/gcc/cp/vtable-class-hierarchy.c @@ -0,0 +1,1353 @@ +/* Copyright (C) 2012-2013 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC 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 + General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers + before using them for virtual method dispatches. */ + +/* This file is part of the vtable security feature implementation. + The vtable security feature is designed to detect when a virtual + call is about to be made through an invalid vtable pointer + (possibly due to data corruption or malicious attacks). The + compiler finds every virtual call, and inserts a verification call + before the virtual call. The verification call takes the actual + vtable pointer value in the object through which the virtual call + is being made, and compares the vtable pointer against a set of all + valid vtable pointers that the object could contain (this set is + based on the declared type of the object). If the pointer is in + the valid set, execution is allowed to continue; otherwise the + program is halted. + + There are several pieces needed in order to make this work: 1. For + every virtual class in the program (i.e. a class that contains + virtual methods), we need to build the set of all possible valid + vtables that an object of that class could point to. This includes + vtables for any class(es) that inherit from the class under + consideration. 2. For every such data set we build up, we need a + way to find and reference the data set. This is complicated by the + fact that the real vtable addresses are not known until runtime, + when the program is loaded into memory, but we need to reference the + sets at compile time when we are inserting verification calls into + the program. 3. We need to find every virtual call in the program, + and insert the verification call (with the appropriate arguments) + before the virtual call. 4. We need some runtime library pieces: + the code to build up the data sets at runtime; the code to actually + perform the verification using the data sets; and some code to set + protections on the data sets, so they themselves do not become + hacker targets. + + To find and reference the set of valid vtable pointers for any given + virtual class, we create a special global varible for each virtual + class. We refer to this as the "vtable map variable" for that + class. The vtable map variable has the type "void *", and is + initialized by the compiler to NULL. At runtime when the set of + valid vtable pointers for a virtual class, e.g. class Foo, is built, + the vtable map variable for class Foo is made to point to the set. + During compile time, when the compiler is inserting verification + calls into the program, it passes the vtable map variable for the + appropriate class to the verification call, so that at runtime the + verification call can find the appropriate data set. + + The actual set of valid vtable pointers for a virtual class, + e.g. class Foo, cannot be built until runtime, when the vtables get + loaded into memory and their addresses are known. But the knowledge + about which vtables belong in which class' hierarchy is only known + at compile time. Therefore at compile time we collect class + hierarchy and vtable information about every virtual class, and we + generate calls to build up the data sets at runtime. To build the + data sets, we call one of the functions we add to the runtime + library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, + a vtable map variable and the address of a vtable. If the vtable + map variable is currently NULL, it creates a new data set (hash + table), makes the vtable map variable point to the new data set, and + inserts the vtable address into the data set. If the vtable map + variable is not NULL, it just inserts the vtable address into the + data set. In order to make sure that our data sets are built before + any verification calls happen, we create a special constructor + initialization function for each compilation unit, give it a very + high initialization priority, and insert all of our calls to + __VLTRegisterPair into our special constructor initialization + function. + + The vtable verification feature is controlled by the flag + '-fvtable-verify='. There are three flavors of this: + '-fvtable-verify=std', '-fvtable-verify=preinit', and + '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is + used, then our constructor initialization function gets put into the + preinit array. This is necessary if there are data sets that need + to be built very early in execution. If the constructor + initialization function gets put into the preinit array, the we also + add calls to __VLTChangePermission at the beginning and end of the + function. The call at the beginning sets the permissions on the + data sets and vtable map variables to read/write, and the one at the + end makes them read-only. If the '-fvtable-verify=std' option is + used, the constructor initialization functions are executed at their + normal time, and the __VLTChangePermission calls are handled + differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc). + The option '-fvtable-verify=none' turns off vtable verification. + + This file contains code to find and record the class hierarchies for + the virtual classes in a program, and all the vtables associated + with each such class; to generate the vtable map variables; and to + generate the constructor initialization function (with the calls to + __VLTRegisterPair, and __VLTChangePermission). The main data + structures used for collecting the class hierarchy data and + building/maintaining the vtable map variable data are defined in + gcc/vtable-verify.h, because they are used both here and in + gcc/vtable-verify.c. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "timevar.h" +#include "cpplib.h" +#include "tree.h" +#include "cp-tree.h" +#include "intl.h" +#include "c-family/c-pragma.h" +#include "decl.h" +#include "flags.h" +#include "diagnostic-core.h" +#include "output.h" +#include "target.h" +#include "cgraph.h" +#include "c-family/c-common.h" +#include "c-family/c-objc.h" +#include "plugin.h" +#include "tree-iterator.h" +#include "vtable-verify.h" +#include "gimple.h" +#include "bitmap.h" +#include "libiberty.h" + +#define MAX_SET_SIZE 5000 + +static int num_calls_to_regset = 0; +static int num_calls_to_regpair = 0; +static int current_set_size; + +/* Mark these specially since they need to be stored in precompiled + header IR. */ +static GTY (()) vec<tree, va_gc> *vlt_saved_class_info; +static GTY (()) tree vlt_register_pairs_fndecl = NULL_TREE; +static GTY (()) tree vlt_register_set_fndecl = NULL_TREE; + +struct work_node { + struct vtv_graph_node *node; + struct work_node *next; +}; + +struct vtbl_map_node *vtable_find_or_create_map_decl (tree); + +/* As part of vtable verification the compiler generates and inserts + calls to __VLTVerifyVtablePointer, which is in libstdc++. This + function builds and initializes the function decl that is used + in generating those function calls. + + In addition to __VLTVerifyVtablePointer there is also + __VLTVerifyVtablePointerDebug which can be used in place of + __VLTVerifyVtablePointer, and which takes extra parameters and + outputs extra information, to help debug problems. The debug + version of this function is generated and used if flag_vtv_debug is + true. + + The signatures for these functions are: + + void * __VLTVerifyVtablePointer (void **, void*); + void * __VLTVerifyVtablePointerDebug (void**, void *, char *, char *); +*/ + +void +vtv_build_vtable_verify_fndecl (void) +{ + tree func_type = NULL_TREE; + + if (verify_vtbl_ptr_fndecl != NULL_TREE + && TREE_CODE (verify_vtbl_ptr_fndecl) != ERROR_MARK) + return; + + if (flag_vtv_debug) + { + func_type = build_function_type_list (const_ptr_type_node, + build_pointer_type (ptr_type_node), + const_ptr_type_node, + const_string_type_node, + const_string_type_node, + NULL_TREE); + verify_vtbl_ptr_fndecl = + build_lang_decl (FUNCTION_DECL, + get_identifier ("__VLTVerifyVtablePointerDebug"), + func_type); + } + else + { + func_type = build_function_type_list (const_ptr_type_node, + build_pointer_type (ptr_type_node), + const_ptr_type_node, + NULL_TREE); + verify_vtbl_ptr_fndecl = + build_lang_decl (FUNCTION_DECL, + get_identifier ("__VLTVerifyVtablePointer"), + func_type); + } + + TREE_NOTHROW (verify_vtbl_ptr_fndecl) = 1; + DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl) + = tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl)); + DECL_PURE_P (verify_vtbl_ptr_fndecl) = 1; + TREE_PUBLIC (verify_vtbl_ptr_fndecl) = 1; + DECL_PRESERVE_P (verify_vtbl_ptr_fndecl) = 1; +} + +/* As part of vtable verification the compiler generates and inserts + calls to __VLTRegisterSet and __VLTRegisterPair, which are in + libsupc++. This function builds and initializes the function decls + that are used in generating those function calls. + + The signatures for these functions are: + + void __VLTRegisterSetDebug (void **, const void *, std::size_t, + size_t, void **); + + void __VLTRegisterSet (void **, const void *, std::size_t, + size_t, void **); + + void __VLTRegisterPairDebug (void **, const void *, size_t, + const void *, const char *, const char *); + + void __VLTRegisterPair (void **, const void *, size_t, const void *); +*/ + +static void +init_functions (void) +{ + tree register_set_type; + tree register_pairs_type; + + if (vlt_register_set_fndecl != NULL_TREE) + return; + + gcc_assert (vlt_register_pairs_fndecl == NULL_TREE); + gcc_assert (vlt_register_set_fndecl == NULL_TREE); + + /* Build function decl for __VLTRegisterSet*. */ + + register_set_type = build_function_type_list + (void_type_node, + build_pointer_type (ptr_type_node), + const_ptr_type_node, + size_type_node, + size_type_node, + build_pointer_type (ptr_type_node), + NULL_TREE); + + if (flag_vtv_debug) + vlt_register_set_fndecl = build_lang_decl + (FUNCTION_DECL, + get_identifier ("__VLTRegisterSetDebug"), + register_set_type); + else + vlt_register_set_fndecl = build_lang_decl + (FUNCTION_DECL, + get_identifier ("__VLTRegisterSet"), + register_set_type); + + + TREE_NOTHROW (vlt_register_set_fndecl) = 1; + DECL_ATTRIBUTES (vlt_register_set_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_register_set_fndecl)); + TREE_PUBLIC (vlt_register_set_fndecl) = 1; + DECL_PRESERVE_P (vlt_register_set_fndecl) = 1; + SET_DECL_LANGUAGE (vlt_register_set_fndecl, lang_cplusplus); + + /* Build function decl for __VLTRegisterPair*. */ + + if (flag_vtv_debug) + { + register_pairs_type = build_function_type_list (void_type_node, + build_pointer_type + (ptr_type_node), + const_ptr_type_node, + size_type_node, + const_ptr_type_node, + const_string_type_node, + const_string_type_node, + NULL_TREE); + + vlt_register_pairs_fndecl = build_lang_decl + (FUNCTION_DECL, + get_identifier ("__VLTRegisterPairDebug"), + register_pairs_type); + } + else + { + register_pairs_type = build_function_type_list (void_type_node, + build_pointer_type + (ptr_type_node), + const_ptr_type_node, + size_type_node, + const_ptr_type_node, + NULL_TREE); + + vlt_register_pairs_fndecl = build_lang_decl + (FUNCTION_DECL, + get_identifier ("__VLTRegisterPair"), + register_pairs_type); + } + + TREE_NOTHROW (vlt_register_pairs_fndecl) = 1; + DECL_ATTRIBUTES (vlt_register_pairs_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_register_pairs_fndecl)); + TREE_PUBLIC (vlt_register_pairs_fndecl) = 1; + DECL_PRESERVE_P (vlt_register_pairs_fndecl) = 1; + SET_DECL_LANGUAGE (vlt_register_pairs_fndecl, lang_cplusplus); + +} + +/* This is a helper function for + vtv_compute_class_hierarchy_transitive_closure. It adds a + vtv_graph_node to the WORKLIST, which is a linked list of + seen-but-not-yet-processed nodes. INSERTED is a bitmap, one bit + per node, to help make sure that we don't insert a node into the + worklist more than once. Each node represents a class somewhere in + our class hierarchy information. Every node in the graph gets added + to the worklist exactly once and removed from the worklist exactly + once (when all of its children have been processed). */ + +static void +add_to_worklist (struct work_node **worklist, struct vtv_graph_node *node, + sbitmap inserted) +{ + struct work_node *new_work_node; + + if (bitmap_bit_p (inserted, node->class_uid)) + return; + + new_work_node = XNEW (struct work_node); + new_work_node->next = *worklist; + new_work_node->node = node; + *worklist = new_work_node; + + bitmap_set_bit (inserted, node->class_uid); +} + +/* This is a helper function for + vtv_compute_class_hierarchy_transitive_closure. It goes through + the WORKLIST of class hierarchy nodes looking for a "leaf" node, + i.e. a node whose children in the hierarchy have all been + processed. When it finds the next leaf node, it removes it from + the linked list (WORKLIST) and returns the node. */ + +static struct vtv_graph_node * +find_and_remove_next_leaf_node (struct work_node **worklist) +{ + struct work_node *prev, *cur; + struct vtv_graph_node *ret_val = NULL; + + for (prev = NULL, cur = *worklist; cur; prev = cur, cur = cur->next) + { + if ((cur->node->children).length() == cur->node->num_processed_children) + { + if (prev == NULL) + (*worklist) = cur->next; + else + prev->next = cur->next; + + cur->next = NULL; + ret_val = cur->node; + free (cur); + return ret_val; + } + } + + return NULL; +} + +/* In our class hierarchy graph, each class node contains a bitmap, + with one bit for each class in the hierarchy. The bits are set for + classes that are descendants in the graph of the current node. + Initially the descendants bitmap is only set for immediate + descendants. This function traverses the class hierarchy graph, + bottom up, filling in the transitive closures for the descendants + as we rise up the graph. */ + +void +vtv_compute_class_hierarchy_transitive_closure (void) +{ + struct work_node *worklist = NULL; + sbitmap inserted = sbitmap_alloc (num_vtable_map_nodes); + unsigned i; + unsigned j; + + /* Note: Every node in the graph gets added to the worklist exactly + once and removed from the worklist exactly once (when all of its + children have been processed). Each node's children edges are + followed exactly once, and each node's parent edges are followed + exactly once. So this algorithm is roughly O(V + 2E), i.e. + O(E + V). */ + + /* Set-up: */ + /* Find all the "leaf" nodes in the graph, and add them to the worklist. */ + bitmap_clear (inserted); + for (j = 0; j < num_vtable_map_nodes; ++j) + { + struct vtbl_map_node *cur = vtbl_map_nodes_vec[j]; + if (cur->class_info + && ((cur->class_info->children).length() == 0) + && ! (bitmap_bit_p (inserted, cur->class_info->class_uid))) + add_to_worklist (&worklist, cur->class_info, inserted); + } + + /* Main work: pull next leaf node off work list, process it, add its + parents to the worklist, where a 'leaf' node is one that has no + children, or all of its children have been processed. */ + while (worklist) + { + struct vtv_graph_node *temp_node = + find_and_remove_next_leaf_node (&worklist); + + gcc_assert (temp_node != NULL); + temp_node->descendants = sbitmap_alloc (num_vtable_map_nodes); + bitmap_clear (temp_node->descendants); + bitmap_set_bit (temp_node->descendants, temp_node->class_uid); + for (i = 0; i < (temp_node->children).length(); ++i) + bitmap_ior (temp_node->descendants, temp_node->descendants, + temp_node->children[i]->descendants); + for (i = 0; i < (temp_node->parents).length(); ++i) + { + temp_node->parents[i]->num_processed_children = + temp_node->parents[i]->num_processed_children + 1; + if (!bitmap_bit_p (inserted, temp_node->parents[i]->class_uid)) + add_to_worklist (&worklist, temp_node->parents[i], inserted); + } + } +} + +/* Keep track of which pairs we have already created __VLTRegisterPair + calls for, to prevent creating duplicate calls within the same + compilation unit. VTABLE_DECL is the var decl for the vtable of + the (descendant) class that we are adding to our class hierarchy + data. VPTR_ADDRESS is an expression for calculating the correct + offset into the vtable (VTABLE_DECL). It is the actual vtable + pointer address that will be stored in our list of valid vtable + pointers for BASE_CLASS. BASE_CLASS is the record_type node for + the base class to whose hiearchy we want to add + VPTR_ADDRESS. (VTABLE_DECL should be the vtable for BASE_CLASS or + one of BASE_CLASS' descendents. */ + +static bool +check_and_record_registered_pairs (tree vtable_decl, tree vptr_address, + tree base_class) +{ + unsigned offset; + struct vtbl_map_node *base_vtable_map_node; + bool inserted_something = false; + + + if (TREE_CODE (vptr_address) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (vptr_address, 0)) == MEM_REF) + vptr_address = TREE_OPERAND (vptr_address, 0); + + if (TREE_OPERAND_LENGTH (vptr_address) > 1) + offset = TREE_INT_CST_LOW (TREE_OPERAND (vptr_address, 1)); + else + offset = 0; + + base_vtable_map_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (base_class)); + + inserted_something = vtbl_map_node_registration_insert + (base_vtable_map_node, + vtable_decl, + offset); + return !inserted_something; +} + +/* Given an IDENTIFIER_NODE, build and return a string literal based on it. */ + +static tree +build_string_from_id (tree identifier) +{ + int len; + + gcc_assert (TREE_CODE (identifier) == IDENTIFIER_NODE); + + len = IDENTIFIER_LENGTH (identifier); + return build_string_literal (len + 1, IDENTIFIER_POINTER (identifier)); +} + +/* A class may contain secondary vtables in it, for various reasons. + This function goes through the decl chain of a class record looking + for any fields that point to secondary vtables, and adding calls to + __VLTRegisterPair for the secondary vtable pointers. + + BASE_CLASS_DECL_ARG is an expression for the address of the vtable + map variable for the BASE_CLASS (whose hierarchy we are currently + updating). BASE_CLASS is the record_type node for the base class. + RECORD_TYPE is the record_type node for the descendant class that + we are possibly adding to BASE_CLASS's hierarchy. BODY is the + function body for the constructor init function to which we are + adding our calls to __VLTRegisterPair. */ + +static void +register_construction_vtables (tree base_class, tree record_type, + tree *vtable_ptr_array, int *num_args) +{ + tree vtbl_var_decl; + + if (TREE_CODE (record_type) != RECORD_TYPE) + return; + + vtbl_var_decl = CLASSTYPE_VTABLES (record_type); + + if (CLASSTYPE_VBASECLASSES (record_type)) + { + tree vtt_decl; + bool already_registered = false; + tree val_vtbl_decl = NULL_TREE; + + vtt_decl = DECL_CHAIN (vtbl_var_decl); + + /* Check to see if we have found a VTT. Add its data if appropriate. */ + if (vtt_decl) + { + tree values = DECL_INITIAL (vtt_decl); + if (TREE_ASM_WRITTEN (vtt_decl) + && values != NULL_TREE + && TREE_CODE (values) == CONSTRUCTOR + && TREE_CODE (TREE_TYPE (values)) == ARRAY_TYPE) + { + unsigned HOST_WIDE_INT cnt; + constructor_elt *ce; + + /* Loop through the initialization values for this + vtable to get all the correct vtable pointer + addresses that we need to add to our set of valid + vtable pointers for the current base class. This may + result in adding more than just the element assigned + to the primary vptr of the class, so we may end up + with more vtable pointers than are strictly + necessary. */ + + for (cnt = 0; + vec_safe_iterate (CONSTRUCTOR_ELTS (values), + cnt, &ce); + cnt++) + { + tree value = ce->value; + + /* Search for the ADDR_EXPR operand within the value. */ + + while (value + && TREE_OPERAND (value, 0) + && TREE_CODE (TREE_OPERAND (value, 0)) == ADDR_EXPR) + value = TREE_OPERAND (value, 0); + + /* The VAR_DECL for the vtable should be the first + argument of the ADDR_EXPR, which is the first + argument of value.*/ + + if (TREE_OPERAND (value, 0)) + val_vtbl_decl = TREE_OPERAND (value, 0); + + while (TREE_CODE (val_vtbl_decl) != VAR_DECL + && TREE_OPERAND (val_vtbl_decl, 0)) + val_vtbl_decl = TREE_OPERAND (val_vtbl_decl, 0); + + gcc_assert (TREE_CODE (val_vtbl_decl) == VAR_DECL); + + /* Check to see if we already have this vtable pointer in + our valid set for this base class. */ + + already_registered = check_and_record_registered_pairs + (val_vtbl_decl, + value, + base_class); + + if (already_registered) + continue; + + /* Add this vtable pointer to our set of valid + pointers for the base class. */ + + gcc_assert (*num_args < (MAX_SET_SIZE - 1)); + + vtable_ptr_array[*num_args] = value; + *num_args = *num_args + 1; + current_set_size++; + } + } + } + } +} + +/* This function iterates through all the vtables it can find from the + BINFO of a class, to make sure we have found ALL of the vtables + that an object of that class could point to. Generate calls to + __VLTRegisterPair for those vtable pointers that we find. + + BINFO is the tree_binfo node for the BASE_CLASS. BODY is the + function body for the constructor init function to which we are + adding calls to __VLTRegisterPair. ARG1 is an expression for the + address of the vtable map variable (for the BASE_CLASS), that will + point to the updated data set. BASE_CLASS is the record_type node + for the base class whose set of valid vtable pointers we are + updating. STR1 and STR2 are all debugging information, to be passed + as parameters to __VLTRegisterPairDebug. STR1 represents the name + of the vtable map variable to be updated by the call. Similarly, + STR2 represents the name of the class whose vtable pointer is being + added to the hierarchy. */ + +static void +register_other_binfo_vtables (tree binfo, tree base_class, + tree *vtable_ptr_array, int *num_args) +{ + unsigned ix; + tree base_binfo; + tree vtable_decl; + bool already_registered; + + if (binfo == NULL_TREE) + return; + + for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) + { + if ((!BINFO_PRIMARY_P (base_binfo) + || BINFO_VIRTUAL_P (base_binfo)) + && (vtable_decl = get_vtbl_decl_for_binfo (base_binfo))) + { + tree vtable_address = build_vtbl_address (base_binfo); + + already_registered = check_and_record_registered_pairs + (vtable_decl, + vtable_address, + base_class); + if (!already_registered) + { + gcc_assert (*num_args < (MAX_SET_SIZE - 1)); + + vtable_ptr_array[*num_args] = vtable_address; + *num_args = *num_args + 1; + current_set_size++; + } + } + + register_other_binfo_vtables (base_binfo, base_class, vtable_ptr_array, + num_args); + } +} + +/* The set of valid vtable pointers for any given class are stored in + a hash table. For reasons of efficiency, that hash table size is + always a power of two. In order to try to prevent re-sizing the + hash tables very often, we pass __VLTRegisterPair an initial guess + as to the number of entries the hashtable will eventually need + (rounded up to the nearest power of two). This function takes the + class information we have collected for a particular class, + CLASS_NODE, and calculates the hash table size guess. */ + +static int +guess_num_vtable_pointers (struct vtv_graph_node *class_node) +{ + tree vtbl; + int total_num_vtbls = 0; + int num_vtbls_power_of_two = 1; + unsigned i; + + for (i = 0; i < num_vtable_map_nodes; ++i) + if (bitmap_bit_p (class_node->descendants, i)) + { + tree class_type = vtbl_map_nodes_vec[i]->class_info->class_type; + for (vtbl = CLASSTYPE_VTABLES (class_type); vtbl; + vtbl = DECL_CHAIN (vtbl)) + { + total_num_vtbls++; + if (total_num_vtbls > num_vtbls_power_of_two) + num_vtbls_power_of_two <<= 1; + } + } + return num_vtbls_power_of_two; +} + +/* A simple hash function on strings */ +/* Be careful about changing this routine. The values generated will + be stored in the calls to InitSet. So, changing this routine may + cause a binary incompatibility. */ + +static uint32_t +vtv_string_hash (const char *in) +{ + const char *s = in; + uint32_t h = 0; + + gcc_assert (in != NULL); + for ( ; *s; ++s) + h = 5 * h + *s; + return h; +} + +static char * +get_log_file_name (const char *fname) +{ + const char *tmp_dir = concat (dump_dir_name, NULL); + char *full_name; + int dir_len; + int fname_len; + + dir_len = strlen (tmp_dir); + fname_len = strlen (fname); + + full_name = XNEWVEC (char, dir_len + fname_len + 1); + strcpy (full_name, tmp_dir); + strcpy (full_name + dir_len, fname); + + return full_name; +} + +static void +write_out_current_set_data (tree base_class, int set_size) +{ + static int class_data_log_fd = -1; + char buffer[1024]; + int bytes_written __attribute__ ((unused)); + char *file_name = get_log_file_name ("vtv_class_set_sizes.log"); + + if (class_data_log_fd == -1) + class_data_log_fd = open (file_name, + O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); + + if (class_data_log_fd == -1) + { + warning (0, "Unable to open log file 'vtv_class_set_sizes.log'"); + return; + } + + snprintf (buffer, sizeof (buffer), "%s %d\n", + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (base_class))), + set_size); + bytes_written = write (class_data_log_fd, buffer, strlen (buffer)); +} + +static tree +build_key_buffer_arg (tree base_ptr_var_decl) +{ + const int key_type_fixed_size = 8; + uint32_t len1 = IDENTIFIER_LENGTH (DECL_NAME (base_ptr_var_decl)); + uint32_t hash_value = vtv_string_hash (IDENTIFIER_POINTER + (DECL_NAME (base_ptr_var_decl))); + void *key_buffer = xmalloc (len1 + key_type_fixed_size); + uint32_t *value_ptr = (uint32_t *) key_buffer; + tree ret_value; + + /* Set the len and hash for the string. */ + *value_ptr = len1; + value_ptr++; + *value_ptr = hash_value; + + /* Now copy the string representation of the vtbl map name... */ + memcpy ((char *) key_buffer + key_type_fixed_size, + IDENTIFIER_POINTER (DECL_NAME (base_ptr_var_decl)), + len1); + + /* ... and build a string literal from it. This will make a copy + so the key_bufffer is not needed anymore after this. */ + ret_value = build_string_literal (len1 + key_type_fixed_size, + (char *) key_buffer); + free (key_buffer); + return ret_value; +} + +static void +insert_call_to_register_set (tree class_name, int num_args, + tree *vtbl_ptr_array, tree body, tree arg1, + tree arg2, tree size_hint_arg) +{ + tree call_expr; + char *array_arg_name = ACONCAT (("__vptr_array_", + IDENTIFIER_POINTER (class_name), NULL)); + tree array_arg_type = build_array_type_nelts (build_pointer_type + (build_pointer_type + (void_type_node)), + num_args); + tree array_arg = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (array_arg_name), + array_arg_type); + int k; + + vec<constructor_elt, va_gc> *array_elements; + vec_alloc (array_elements, num_args); + + tree initial = NULL_TREE; + tree arg3 = NULL_TREE; + + TREE_PUBLIC (array_arg) = 0; + DECL_EXTERNAL (array_arg) = 0; + TREE_STATIC (array_arg) = 1; + DECL_ARTIFICIAL (array_arg) = 0; + TREE_READONLY (array_arg) = 1; + DECL_IGNORED_P (array_arg) = 0; + DECL_PRESERVE_P (array_arg) = 0; + DECL_VISIBILITY (array_arg) = VISIBILITY_HIDDEN; + + for (k = 0; k < num_args; ++k) + { + CONSTRUCTOR_APPEND_ELT (array_elements, NULL_TREE, vtbl_ptr_array[k]); + } + + initial = build_constructor (TREE_TYPE (array_arg), array_elements); + + TREE_CONSTANT (initial) = 1; + TREE_STATIC (initial) = 1; + DECL_INITIAL (array_arg) = initial; + relayout_decl (array_arg); + varpool_finalize_decl (array_arg); + + arg3 = build1 (ADDR_EXPR, TYPE_POINTER_TO (TREE_TYPE (array_arg)), array_arg); + + TREE_TYPE (arg3) = build_pointer_type (TREE_TYPE (array_arg)); + + call_expr = build_call_expr (vlt_register_set_fndecl, 5, arg1, + arg2, /* set_symbol_key */ + size_hint_arg, build_int_cst (size_type_node, + num_args), + arg3); + append_to_statement_list (call_expr, &body); + num_calls_to_regset++; +} + +static void +insert_call_to_register_pair (tree vtable_address, int num_args, tree arg1, + tree arg2, tree size_hint_arg, tree str1, + tree str2, tree body) +{ + tree call_expr; + + if (num_args == 0) + vtable_address = build_int_cst (build_pointer_type (void_type_node), 0); + + if (flag_vtv_debug) + call_expr = build_call_expr (vlt_register_pairs_fndecl, 6, arg1, arg2, + size_hint_arg, vtable_address, str1, str2); + else + call_expr = build_call_expr (vlt_register_pairs_fndecl, 4, arg1, arg2, + size_hint_arg, vtable_address); + + append_to_statement_list (call_expr, &body); + num_calls_to_regpair++; +} + +static void +output_set_info (tree record_type, tree *vtbl_ptr_array, int array_size) +{ + static int vtv_debug_log_fd = -1; + char buffer[1024]; + int bytes_written __attribute__ ((unused)); + const char *class_name = + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (record_type))); + char *file_name = get_log_file_name ("vtv_set_ptr_data.log"); + + if (vtv_debug_log_fd == -1) + vtv_debug_log_fd = open (file_name, + O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); + if (vtv_debug_log_fd == -1) + { + warning (0, "Unable to open log file 'vtv_set_ptr_data.log'"); + return; + } + + for (int i = 0; i < array_size; ++i) + { + const char *vptr_name = "unknown"; + int vptr_offset = 0; + + if (TREE_CODE (vtbl_ptr_array[i]) == POINTER_PLUS_EXPR) + { + tree arg0 = TREE_OPERAND (vtbl_ptr_array[i], 0); + tree arg1 = TREE_OPERAND (vtbl_ptr_array[i], 1); + + if (TREE_CODE (arg0) == ADDR_EXPR) + arg0 = TREE_OPERAND (arg0, 0); + + if (TREE_CODE (arg0) == VAR_DECL) + vptr_name = IDENTIFIER_POINTER (DECL_NAME (arg0)); + + if (TREE_CODE (arg1) == INTEGER_CST) + vptr_offset = TREE_INT_CST_LOW (arg1); + } + + snprintf (buffer, sizeof (buffer), "%s %s %s + %d\n", + main_input_filename, class_name, vptr_name, vptr_offset); + bytes_written = write (vtv_debug_log_fd, buffer, strlen(buffer)); + } + +} + +/* This function goes through our internal class hierarchy & vtable + pointer data structure and outputs calls to __VLTRegisterPair for + every class-vptr pair (for those classes whose vtable would be + output in the current compilation unit). These calls get put into + our constructor initialization function. BODY is the function + body, so far, of our constructor initialization function, to which we + add the calls. */ + +static bool +register_all_pairs (tree body) +{ + bool registered_at_least_one = false; + tree vtbl_ptr_array[MAX_SET_SIZE]; + int num_vtable_args = 0; + unsigned j; + + for (j = 0; j < num_vtable_map_nodes; ++j) + { + struct vtbl_map_node *current = vtbl_map_nodes_vec[j]; + unsigned i = 0; + tree base_class = current->class_info->class_type; + tree base_ptr_var_decl = current->vtbl_map_decl; + tree arg1; + tree arg2; + tree new_type; + tree str1 = NULL_TREE; + tree str2 = NULL_TREE; + size_t size_hint; + tree size_hint_arg; + + gcc_assert (current->class_info != NULL); + + + if (flag_vtv_debug) + str1 = build_string_from_id (DECL_NAME (base_ptr_var_decl)); + + new_type = build_pointer_type (TREE_TYPE (base_ptr_var_decl)); + arg1 = build1 (ADDR_EXPR, new_type, base_ptr_var_decl); + + num_vtable_args = 0; + + for (i = 0; i < num_vtable_map_nodes; ++i) + if (bitmap_bit_p (current->class_info->descendants, i)) + { + struct vtbl_map_node *vtbl_class_node = vtbl_map_nodes_vec[i]; + tree class_type = vtbl_class_node->class_info->class_type; + + if (class_type + && (TREE_CODE (class_type) == RECORD_TYPE)) + { + bool already_registered; + + tree binfo = TYPE_BINFO (class_type); + tree vtable_decl; + bool vtable_should_be_output = false; + + vtable_decl = CLASSTYPE_VTABLES (class_type); + + /* Handle main vtable for this class. */ + + if (vtable_decl) + { + vtable_should_be_output = TREE_ASM_WRITTEN (vtable_decl); + str2 = build_string_from_id (DECL_NAME (vtable_decl)); + } + + if (vtable_decl && vtable_should_be_output) + { + tree vtable_address = build_vtbl_address (binfo); + + already_registered = check_and_record_registered_pairs + (vtable_decl, + vtable_address, + base_class); + + + if (!already_registered) + { + + vtbl_ptr_array[num_vtable_args++] = vtable_address; + + /* Find and handle any 'extra' vtables associated + with this class, via virtual inheritance. */ + register_construction_vtables (base_class, class_type, + vtbl_ptr_array, + &num_vtable_args); + + /* Find and handle any 'extra' vtables associated + with this class, via multiple inheritance. */ + register_other_binfo_vtables (binfo, base_class, + vtbl_ptr_array, + &num_vtable_args); + } + } + } + } + current_set_size = num_vtable_args; + + /* Sometimes we need to initialize the set symbol even if we are + not adding any vtable pointers to the set in the current + compilation unit. In that case, we need to initialize the + set to our best guess as to what the eventual size of the set + hash table will be (to prevent having to re-size the hash + table later). */ + + size_hint = guess_num_vtable_pointers (current->class_info); + + /* If we have added vtable pointers to the set in this + compilation unit, adjust the size hint for the set's hash + table appropriately. */ + if (num_vtable_args > 0) + while ((size_t) num_vtable_args > size_hint) + size_hint <<= 1; + size_hint_arg = build_int_cst (size_type_node, size_hint); + + /* Get the key-buffer argument. */ + arg2 = build_key_buffer_arg (base_ptr_var_decl); + + if (str2 == NULL_TREE) + str2 = build_string_literal (strlen ("unknown") + 1, + "unknown"); + + if (flag_vtv_debug) + output_set_info (current->class_info->class_type, + vtbl_ptr_array, num_vtable_args); + + if (num_vtable_args > 1) + { + insert_call_to_register_set (current->class_name, num_vtable_args, + vtbl_ptr_array, body, arg1, arg2, + size_hint_arg); + registered_at_least_one = true; + } + else if (num_vtable_args >= 0) + { + + if (num_vtable_args > 0 + || (current->is_used + || (current->registered.size() > 0))) + { + insert_call_to_register_pair (vtbl_ptr_array[0], num_vtable_args, + arg1, arg2, size_hint_arg, str1, + str2, body); + registered_at_least_one = true; + } + } + + if (flag_vtv_counts && current_set_size > 0) + write_out_current_set_data (base_class, current_set_size); + + } + + return registered_at_least_one; +} + +/* Given a tree containing a class type (CLASS_TYPE), this function + finds and returns the class hierarchy node for that class in our + data structure. */ + +static struct vtv_graph_node * +find_graph_node (tree class_type) +{ + struct vtbl_map_node *vtbl_node; + + vtbl_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (class_type)); + if (vtbl_node) + return vtbl_node->class_info; + + return NULL; +} + +/* Add base class/derived class pair to our internal class hierarchy + data structure. BASE_NODE is our vtv_graph_node that corresponds + to a base class. DERIVED_NODE is our vtv_graph_node that + corresponds to a class that is a descendant of the base class + (possibly the base class itself). */ + +static void +add_hierarchy_pair (struct vtv_graph_node *base_node, + struct vtv_graph_node *derived_node) +{ + (base_node->children).safe_push (derived_node); + (derived_node->parents).safe_push (base_node); +} + +/* This functions adds a new base class/derived class relationship to + our class hierarchy data structure. Both parameters are trees + representing the class types, i.e. RECORD_TYPE trees. + DERIVED_CLASS can be the same as BASE_CLASS. */ + +static void +update_class_hierarchy_information (tree base_class, + tree derived_class) +{ + struct vtv_graph_node *base_node = find_graph_node (base_class); + struct vtv_graph_node *derived_node = find_graph_node (derived_class); + + add_hierarchy_pair (base_node, derived_node); +} + + +static void +write_out_vtv_count_data (void) +{ + static int vtv_count_log_fd = -1; + char buffer[1024]; + int unused_vtbl_map_vars = 0; + int bytes_written __attribute__ ((unused)); + char *file_name = get_log_file_name ("vtv_count_data.log"); + + if (vtv_count_log_fd == -1) + vtv_count_log_fd = open (file_name, + O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); + if (vtv_count_log_fd == -1) + { + warning (0, "Unable to open log file 'vtv_count_data.log'"); + return; + } + + for (unsigned i = 0; i < num_vtable_map_nodes; ++i) + { + struct vtbl_map_node *current = vtbl_map_nodes_vec[i]; + if (!current->is_used + && current->registered.size() == 0) + unused_vtbl_map_vars++; + } + + snprintf (buffer, sizeof (buffer), "%s %d %d %d %d %d\n", + main_input_filename, total_num_virtual_calls, + total_num_verified_vcalls, num_calls_to_regset, + num_calls_to_regpair, unused_vtbl_map_vars); + + bytes_written = write (vtv_count_log_fd, buffer, strlen (buffer)); +} + +/* This function calls register_all_pairs, which actually generates + all the calls to __VLTRegisterPair (in the verification constructor + init function). It also generates the calls to + __VLTChangePermission, if the verification constructor init + function is going into the preinit array. INIT_ROUTINE_BODY is + the body of our constructior initialization function, to which we + add our function calls.*/ + +bool +vtv_register_class_hierarchy_information (tree init_routine_body) +{ + bool registered_something = false; + + init_functions (); + + if (num_vtable_map_nodes == 0) + return false; + + /* Add class hierarchy pairs to the vtable map data structure. */ + registered_something = register_all_pairs (init_routine_body); + + if (flag_vtv_counts) + write_out_vtv_count_data (); + + return registered_something; +} + + +/* Generate the special constructor function that calls + __VLTChangePermission and __VLTRegisterPairs, and give it a very + high initialization priority. */ + +void +vtv_generate_init_routine (void) +{ + tree init_routine_body; + bool vtable_classes_found = false; + + push_lang_context (lang_name_c); + + /* The priority for this init function (constructor) is carefully + chosen so that it will happen after the calls to unprotect the + memory used for vtable verification and before the memory is + protected again. */ + init_routine_body = vtv_start_verification_constructor_init_function (); + + vtable_classes_found = + vtv_register_class_hierarchy_information (init_routine_body); + + if (vtable_classes_found) + { + tree vtv_fndecl = + vtv_finish_verification_constructor_init_function (init_routine_body); + TREE_STATIC (vtv_fndecl) = 1; + TREE_USED (vtv_fndecl) = 1; + DECL_PRESERVE_P (vtv_fndecl) = 1; + if (flag_vtable_verify == VTV_PREINIT_PRIORITY) + { + DECL_STATIC_CONSTRUCTOR (vtv_fndecl) = 0; + assemble_vtv_preinit_initializer (vtv_fndecl); + } + + gimplify_function_tree (vtv_fndecl); + cgraph_add_new_function (vtv_fndecl, false); + + cgraph_process_new_functions (); + } + pop_lang_context (); +} + +/* This funtion takes a tree containing a class type (BASE_TYPE), and + it either finds the existing vtbl_map_node for that class in our + data structure, or it creates a new node and adds it to the data + structure if there is not one for the class already. As part of + this process it also creates the global vtable map variable for the + class. */ + +struct vtbl_map_node * +vtable_find_or_create_map_decl (tree base_type) +{ + char *var_name = NULL; + struct vtbl_map_node *vtable_map_node = NULL; + + /* Verify the type has an associated vtable. */ + if (!TYPE_BINFO (base_type) || !BINFO_VTABLE (TYPE_BINFO (base_type))) + return NULL; + + /* Create map lookup symbol for base class */ + var_name = get_mangled_vtable_map_var_name (base_type); + + /* We've already created the variable; just look it. */ + vtable_map_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (base_type)); + + if (!vtable_map_node || (vtable_map_node->vtbl_map_decl == NULL_TREE)) + { + /* If we haven't already created the *__vtable_map global + variable for this class, do so now, and add it to the + varpool, to make sure it gets saved and written out. */ + + tree var_decl = NULL; + tree var_type = build_pointer_type (void_type_node); + tree initial_value = integer_zero_node; + + var_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (var_name), var_type); + + DECL_EXTERNAL (var_decl) = 0; + TREE_STATIC (var_decl) = 1; + DECL_VISIBILITY (var_decl) = VISIBILITY_HIDDEN; + SET_DECL_ASSEMBLER_NAME (var_decl, get_identifier (var_name)); + DECL_ARTIFICIAL (var_decl) = 1; + /* We cannot mark this variable as read-only because we want to be + able to write to it at runtime. */ + TREE_READONLY (var_decl) = 0; + DECL_IGNORED_P (var_decl) = 1; + DECL_PRESERVE_P (var_decl) = 1; + + /* Put these mmap variables in thr .vtable_map_vars section, so + we can find and protect them. */ + + DECL_SECTION_NAME (var_decl) = build_string (strlen (".vtable_map_vars"), + ".vtable_map_vars"); + DECL_HAS_IMPLICIT_SECTION_NAME_P (var_decl) = true; + DECL_INITIAL (var_decl) = initial_value; + + comdat_linkage (var_decl); + + varpool_finalize_decl (var_decl); + if (!vtable_map_node) + vtable_map_node = + find_or_create_vtbl_map_node (TYPE_MAIN_VARIANT (base_type)); + if (vtable_map_node->vtbl_map_decl == NULL_TREE) + vtable_map_node->vtbl_map_decl = var_decl; + } + + gcc_assert (vtable_map_node); + return vtable_map_node; +} + +/* This function is used to build up our class hierarchy data for a + particular class. TYPE is the record_type tree node for the + class. */ + +static void +vtv_insert_single_class_info (tree type) +{ + if (flag_vtable_verify) + { + tree binfo = TYPE_BINFO (type); + tree base_binfo; + struct vtbl_map_node *own_map; + int i; + + /* First make sure to create the map for this record type. */ + own_map = vtable_find_or_create_map_decl (type); + if (own_map == NULL) + return; + + /* Go through the list of all base classes for the current + (derived) type, make sure the *__vtable_map global variable + for the base class exists, and add the base class/derived + class pair to the class hierarchy information we are + accumulating (for vtable pointer verification). */ + for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree tree_val = BINFO_TYPE (base_binfo); + struct vtbl_map_node *vtable_map_node = NULL; + + vtable_map_node = vtable_find_or_create_map_decl (tree_val); + + if (vtable_map_node != NULL) + update_class_hierarchy_information (tree_val, type); + } + } +} + +/* This function adds classes we are interested in to a list of + classes. RECORD is the record_type node for the class we are + adding to the list. */ + +void +vtv_save_class_info (tree record) +{ + if (!flag_vtable_verify || TREE_CODE (record) == UNION_TYPE) + return; + + if (!vlt_saved_class_info) + vec_alloc (vlt_saved_class_info, 10); + + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + + vec_safe_push (vlt_saved_class_info, record); +} + + +/* This function goes through the list of classes we saved and calls + vtv_insert_single_class_info on each one, to build up our class + hierarchy data structure. */ + +void +vtv_recover_class_info (void) +{ + tree current_class; + unsigned i; + + if (vlt_saved_class_info) + { + for (i = 0; i < vlt_saved_class_info->length(); ++i) + { + current_class = (*vlt_saved_class_info)[i]; + gcc_assert (TREE_CODE (current_class) == RECORD_TYPE); + vtv_insert_single_class_info (current_class); + } + } +} + +#include "gt-cp-vtable-class-hierarchy.h" diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 92cc25002a3..14955dd1be0 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -191,6 +191,8 @@ in the following sections. -ftemplate-depth=@var{n} @gol -fno-threadsafe-statics -fuse-cxa-atexit -fno-weak -nostdinc++ @gol -fno-default-inline -fvisibility-inlines-hidden @gol +-fvtable-verify=@var{std|preinit|none} @gol +-fvtv-counts -fvtv-debug @gol -fvisibility-ms-compat @gol -fext-numeric-literals @gol -Wabi -Wconversion-null -Wctor-dtor-privacy @gol @@ -318,6 +320,7 @@ Objective-C and Objective-C++ Dialects}. -fdump-tree-sra@r{[}-@var{n}@r{]} @gol -fdump-tree-forwprop@r{[}-@var{n}@r{]} @gol -fdump-tree-fre@r{[}-@var{n}@r{]} @gol +-fdump-tree-vtable-verify @gol -fdump-tree-vrp@r{[}-@var{n}@r{]} @gol -ftree-vectorizer-verbose=@var{n} @gol -fdump-tree-storeccp@r{[}-@var{n}@r{]} @gol @@ -2302,6 +2305,56 @@ and that pointers to function members defined in different shared objects may not compare equal. When this flag is given, it is a violation of the ODR to define types with the same name differently. +@item -fvtable-verify=@var{std|preinit|none} +@opindex fvtable-verify +Turn on (or off, if using @option{-fvtable-verify=none}) the security +feature that verifies at runtime, for every virtual call that is made, that +the vtable pointer through which the call is made is valid for the type of +the object, and has not been corrupted or overwritten. If an invalid vtable +pointer is detected (at runtime), an error is reported and execution of the +program is immediately halted. + +This option causes runtime data structures to be built, at program start up, +for verifying the vtable pointers. The options @code{std} and @code{preinit} +control the timing of when these data structures are built. In both cases the +data structures are built before execution reaches 'main'. The +@option{-fvtable-verify=std} causes these data structure to be built after the +shared libraries have been loaded and initialized. +@option{-fvtable-verify=preinit} causes them to be built before the shared +libraries have been loaded and initialized. + +If this option appears multiple times in the compiler line, with different +values specified, 'none' will take highest priority over both 'std' and +'preinit'; 'preinit' will take priority over 'std'. + +@item -fvtv-debug +@opindex (fvtv-debug) +Causes debug versions of the runtime functions for the vtable verification +feature to be called. This assumes the @option{-fvtable-verify=std} or +@option{-fvtable-verify=preinit} has been used. This flag will also cause the +compiler to keep track of which vtable pointers it found for each class, and +record that information in the file ``vtv_set_ptr_data.log'', in the dump +file directory on the user's machine. + +Note: This feature APPENDS data to the log file. If you want a fresh log +file, be sure to delete any existing one. + +@item -fvtv-counts +@opindex (fvtv-counts) +This is a debugging flag. When used in conjunction with +@option{-fvtable-verify=std} or @option{-fvtable-verify=preinit}, this +causes the compiler to keep track of the total number of virtual calls +it encountered and the number of verifications it inserted. It also +counts the number of calls to certain runtime library functions +that it inserts. This information, for each compilation unit, is written +to a file named ``vtv_count_data.log'', in the dump_file directory on +the user's machine. It also counts the size of the vtable pointer sets +for each class, and writes this information to ``vtv_class_set_sizes.log'' +in the same directory. + +Note: This feature APPENDS data to the log files. To get a fresh log +files, be sure to delete any existing ones. + @item -fno-weak @opindex fno-weak Do not use weak symbol support, even if it is provided by the linker. diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 4fc5d33348e..8eea9a67079 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -191,4 +191,10 @@ enum fp_contract_mode { FP_CONTRACT_FAST = 2 }; +/* flag_vtable_verify initialization levels. */ +enum vtv_priority { + VTV_NO_PRIORITY = 0, /* i.E. Do NOT do vtable verification. */ + VTV_STANDARD_PRIORITY = 1, + VTV_PREINIT_PRIORITY = 2 +}; #endif /* ! GCC_FLAG_TYPES_H */ diff --git a/gcc/gcc.c b/gcc/gcc.c index 6ef4e8a1b77..7c15cf3d176 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -722,6 +722,16 @@ proper position among the other output files. */ %{!pie:%{!shared:%e-fsanitize=thread linking must be done with -pie or -shared}}}}}" #endif +/* This is the spec to use, once the code for creating the vtable + verification runtime library, libvtv.so, has been created. Currently + the vtable verification runtime functions are in libstdc++, so we use + the spec just below this one. */ +#ifndef VTABLE_VERIFICATION_SPEC +#define VTABLE_VERIFICATION_SPEC "\ +%{!nostdlib:%{fvtable-verify=std: -lvtv -u_vtable_map_vars_start -u_vtable_map_vars_end}\ + %{fvtable-verify=preinit: -lvtv -u_vtable_map_vars_start -u_vtable_map_vars_end}}" +#endif + /* -u* was put back because both BSD and SysV seem to support it. */ /* %{static:} simply prevents an error message if the target machine doesn't handle -static. */ @@ -740,7 +750,7 @@ proper position among the other output files. */ %{flto} %{flto=*} %l " LINK_PIE_SPEC \ "%{fuse-ld=*:-fuse-ld=%*}\ %X %{o*} %{e*} %{N} %{n} %{r}\ - %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!nostartfiles:%S}}\ + %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!nostartfiles:%S}} " VTABLE_VERIFICATION_SPEC " \ %{static:} %{L*} %(mfwrap) %(link_libgcc) " SANITIZER_EARLY_SPEC " %o\ %{fopenmp|ftree-parallelize-loops=*:%:include(libgomp.spec)%(link_gomp)}\ %{fgnu-tm:%:include(libitm.spec)%(link_itm)}\ diff --git a/gcc/output.h b/gcc/output.h index cc48dfbed50..7b262566c2f 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -197,6 +197,10 @@ extern void assemble_end_function (tree, const char *); initial value (that will be done by the caller). */ extern void assemble_variable (tree, int, int, int); +/* Put the vtable verification constructor initialization function + into the preinit array. */ +extern void assemble_vtv_preinit_initializer (tree); + /* Compute the alignment of variable specified by DECL. DONT_OUTPUT_DATA is from assemble_variable. */ extern void align_variable (tree decl, bool dont_output_data); diff --git a/gcc/passes.def b/gcc/passes.def index 61cfd9e7e95..b289713368f 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -292,6 +292,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_tm_memopt); NEXT_PASS (pass_tm_edges); POP_INSERT_PASSES () + NEXT_PASS (pass_vtable_verify); NEXT_PASS (pass_lower_vector); NEXT_PASS (pass_lower_complex_O0); NEXT_PASS (pass_asan_O0); diff --git a/gcc/timevar.def b/gcc/timevar.def index 44f0eac52c7..8ab9b99eadf 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -255,6 +255,7 @@ DEFTIMEVAR (TV_TREE_UNINIT , "uninit var analysis") DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization") DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution") DEFTIMEVAR (TV_GIMPLE_SLSR , "straight-line strength reduction") +DEFTIMEVAR (TV_VTABLE_VERIFICATION , "vtable verification") /* Everything else in rest_of_compilation not included above. */ DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 41d5d92c8a9..787a49b7c41 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -439,6 +439,7 @@ extern gimple_opt_pass *make_pass_tm_edges (gcc::context *ctxt); extern gimple_opt_pass *make_pass_split_functions (gcc::context *ctxt); extern gimple_opt_pass *make_pass_feedback_split_functions (gcc::context *ctxt); extern gimple_opt_pass *make_pass_strength_reduction (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_vtable_verify (gcc::context *ctxt); /* IPA Passes */ extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt); diff --git a/gcc/tree.h b/gcc/tree.h index 0058a4b17bf..94f112f43d7 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -6496,6 +6496,9 @@ is_lang_specific (tree t) /* In gimple-low.c. */ extern bool block_may_fallthru (const_tree); +/* In vtable-verify.c. */ +extern void save_vtable_map_decl (tree); + /* Functional interface to the builtin functions. */ @@ -6586,7 +6589,6 @@ builtin_decl_implicit_p (enum built_in_function fncode) && builtin_info.implicit_p[uns_fncode]); } - /* For anonymous aggregate types, we need some sort of name to hold on to. In practice, this should not appear, but it should not be harmful if it does. */ diff --git a/gcc/varasm.c b/gcc/varasm.c index 8efd98e0020..69ec26a5e6b 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -2107,7 +2107,31 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, assemble_noswitch_variable (decl, name, sect, align); else { - switch_to_section (sect); + /* The following bit of code ensures that vtable_map + variables are not only in the comdat section, but that + each variable has its own unique comdat name. If this + code is removed, the variables end up in the same section + with a single comdat name. + + FIXME: resolve_unique_section needs to deal better with + decls with both DECL_SECTION_NAME and DECL_ONE_ONLY. Once + that is fixed, this if-else statement can be replaced with + a single call to "switch_to_section (sect)". */ + if (sect->named.name + && (strcmp (sect->named.name, ".vtable_map_vars") == 0)) + { +#if defined (OBJECT_FORMAT_ELF) + targetm.asm_out.named_section (sect->named.name, + sect->named.common.flags + | SECTION_LINKONCE, + DECL_NAME (decl)); + in_section = sect; +#else + switch_to_section (sect); +#endif + } + else + switch_to_section (sect); if (align > BITS_PER_UNIT) ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); assemble_variable_contents (decl, name, dont_output_data); @@ -2120,6 +2144,23 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, } } + +/* Given a function declaration (FN_DECL), this function assembles the + function into the .preinit_array section. */ + +void +assemble_vtv_preinit_initializer (tree fn_decl) +{ + section *sect; + unsigned flags = SECTION_WRITE; + rtx symbol = XEXP (DECL_RTL (fn_decl), 0); + + flags |= SECTION_NOTYPE; + sect = get_section (".preinit_array", flags, fn_decl); + switch_to_section (sect); + assemble_addr_to_section (symbol, sect); +} + /* Return 1 if type TYPE contains any pointers. */ static int @@ -6046,6 +6087,9 @@ default_section_type_flags (tree decl, const char *name, int reloc) if (decl && DECL_P (decl) && DECL_ONE_ONLY (decl)) flags |= SECTION_LINKONCE; + if (strcmp (name, ".vtable_map_vars") == 0) + flags |= SECTION_LINKONCE; + if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) flags |= SECTION_TLS | SECTION_WRITE; diff --git a/gcc/vtable-verify.c b/gcc/vtable-verify.c new file mode 100644 index 00000000000..b6c5bc3bce4 --- /dev/null +++ b/gcc/vtable-verify.c @@ -0,0 +1,793 @@ +/* Copyright (C) 2013 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC 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 General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers + before using them for virtual method dispatches. */ + +/* This file is part of the vtable security feature implementation. + The vtable security feature is designed to detect when a virtual + call is about to be made through an invalid vtable pointer + (possibly due to data corruption or malicious attacks). The + compiler finds every virtual call, and inserts a verification call + before the virtual call. The verification call takes the actual + vtable pointer value in the object through which the virtual call + is being made, and compares the vtable pointer against a set of all + valid vtable pointers that the object could contain (this set is + based on the declared type of the object). If the pointer is in + the valid set, execution is allowed to continue; otherwise the + program is halted. + + There are several pieces needed in order to make this work: 1. For + every virtual class in the program (i.e. a class that contains + virtual methods), we need to build the set of all possible valid + vtables that an object of that class could point to. This includes + vtables for any class(es) that inherit from the class under + consideration. 2. For every such data set we build up, we need a + way to find and reference the data set. This is complicated by the + fact that the real vtable addresses are not known until runtime, + when the program is loaded into memory, but we need to reference the + sets at compile time when we are inserting verification calls into + the program. 3. We need to find every virtual call in the program, + and insert the verification call (with the appropriate arguments) + before the virtual call. 4. We need some runtime library pieces: + the code to build up the data sets at runtime; the code to actually + perform the verification using the data sets; and some code to set + protections on the data sets, so they themselves do not become + hacker targets. + + To find and reference the set of valid vtable pointers for any given + virtual class, we create a special global variable for each virtual + class. We refer to this as the "vtable map variable" for that + class. The vtable map variable has the type "void *", and is + initialized by the compiler to NULL. At runtime when the set of + valid vtable pointers for a virtual class, e.g. class Foo, is built, + the vtable map variable for class Foo is made to point to the set. + During compile time, when the compiler is inserting verification + calls into the program, it passes the vtable map variable for the + appropriate class to the verification call, so that at runtime the + verification call can find the appropriate data set. + + The actual set of valid vtable pointers for a virtual class, + e.g. class Foo, cannot be built until runtime, when the vtables get + loaded into memory and their addresses are known. But the knowledge + about which vtables belong in which class' hierarchy is only known + at compile time. Therefore at compile time we collect class + hierarchy and vtable information about every virtual class, and we + generate calls to build up the data sets at runtime. To build the + data sets, we call one of the functions we add to the runtime + library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, + a vtable map variable and the address of a vtable. If the vtable + map variable is currently NULL, it creates a new data set (hash + table), makes the vtable map variable point to the new data set, and + inserts the vtable address into the data set. If the vtable map + variable is not NULL, it just inserts the vtable address into the + data set. In order to make sure that our data sets are built before + any verification calls happen, we create a special constructor + initialization function for each compilation unit, give it a very + high initialization priority, and insert all of our calls to + __VLTRegisterPair into our special constructor initialization + function. + + The vtable verification feature is controlled by the flag + '-fvtable-verify='. There are three flavors of this: + '-fvtable-verify=std', '-fvtable-verify=preinit', and + '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is + used, then our constructor initialization function gets put into the + preinit array. This is necessary if there are data sets that need + to be built very early in execution. If the constructor + initialization function gets put into the preinit array, the we also + add calls to __VLTChangePermission at the beginning and end of the + function. The call at the beginning sets the permissions on the + data sets and vtable map variables to read/write, and the one at the + end makes them read-only. If the '-fvtable-verify=std' option is + used, the constructor initialization functions are executed at their + normal time, and the __VLTChangePermission calls are handled + differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc). + The option '-fvtable-verify=none' turns off vtable verification. + + This file contains code for the tree pass that goes through all the + statements in each basic block, looking for virtual calls, and + inserting a call to __VLTVerifyVtablePointer (with appropriate + arguments) before each one. It also contains the hash table + functions for the data structures used for collecting the class + hierarchy data and building/maintaining the vtable map variable data + are defined in gcc/vtable-verify.h. These data structures are + shared with the code in the C++ front end that collects the class + hierarchy & vtable information and generates the vtable map + variables (see cp/vtable-class-hierarchy.c). This tree pass should + run just before the gimple is converted to RTL. + + Some implementation details for this pass: + + To find all of the virtual calls, we iterate through all the + gimple statements in each basic block, looking for any call + statement with the code "OBJ_TYPE_REF". Once we have found the + virtual call, we need to find the vtable pointer through which the + call is being made, and the type of the object containing the + pointer (to find the appropriate vtable map variable). We then use + these to build a call to __VLTVerifyVtablePointer, passing the + vtable map variable, and the vtable pointer. We insert the + verification call just after the gimple statement that gets the + vtable pointer out of the object, and we update the next + statement to depend on the result returned from + __VLTVerifyVtablePointer (the vtable pointer value), to ensure + subsequent compiler phases don't remove or reorder the call (it's no + good to have the verification occur after the virtual call, for + example). To find the vtable pointer being used (and the type of + the object) we search backwards through the def_stmts chain from the + virtual call (see verify_bb_vtables for more details). */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "basic-block.h" +#include "tree-flow.h" +#include "tree-pass.h" +#include "cfgloop.h" + +#include "vtable-verify.h" + +unsigned num_vtable_map_nodes = 0; +int total_num_virtual_calls = 0; +int total_num_verified_vcalls = 0; + +extern GTY(()) tree verify_vtbl_ptr_fndecl; +tree verify_vtbl_ptr_fndecl = NULL_TREE; + +/* Keep track of whether or not any virtual call were verified. */ +static bool any_verification_calls_generated = false; + +unsigned int vtable_verify_main (void); + + +/* The following few functions are for the vtbl pointer hash table + in the 'registered' field of the struct vtable_map_node. The hash + table keeps track of which vtable pointers have been used in + calls to __VLTRegisterPair with that particular vtable map variable. */ + +/* This function checks to see if a particular VTABLE_DECL and OFFSET are + already in the 'registered' hash table for NODE. */ + +bool +vtbl_map_node_registration_find (struct vtbl_map_node *node, + tree vtable_decl, + unsigned offset) +{ + struct vtable_registration key; + struct vtable_registration **slot; + + gcc_assert (node && node->registered.is_created()); + + key.vtable_decl = vtable_decl; + slot = (struct vtable_registration **) node->registered.find_slot (&key, + NO_INSERT); + + if (slot && (*slot)) + { + unsigned i; + for (i = 0; i < ((*slot)->offsets).length(); ++i) + if ((*slot)->offsets[i] == offset) + return true; + } + + return false; +} + +/* This function inserts VTABLE_DECL and OFFSET into the 'registered' + hash table for NODE. It returns a boolean indicating whether or not + it actually inserted anything. */ + +bool +vtbl_map_node_registration_insert (struct vtbl_map_node *node, + tree vtable_decl, + unsigned offset) +{ + struct vtable_registration key; + struct vtable_registration **slot; + bool inserted_something = false; + + if (!node || !node->registered.is_created()) + return false; + + key.vtable_decl = vtable_decl; + slot = (struct vtable_registration **) node->registered.find_slot (&key, + INSERT); + + if (! *slot) + { + struct vtable_registration *node; + node = XNEW (struct vtable_registration); + node->vtable_decl = vtable_decl; + + (node->offsets).create (10); + (node->offsets).safe_push (offset); + *slot = node; + inserted_something = true; + } + else + { + /* We found the vtable_decl slot; we need to see if it already + contains the offset. If not, we need to add the offset. */ + unsigned i; + bool found = false; + for (i = 0; i < ((*slot)->offsets).length() && !found; ++i) + if ((*slot)->offsets[i] == offset) + found = true; + + if (!found) + { + ((*slot)->offsets).safe_push (offset); + inserted_something = true; + } + } + return inserted_something; +} + +/* Hashtable functions for vtable_registration hashtables. */ + +inline hashval_t +registration_hasher::hash (const value_type *p) +{ + const struct vtable_registration *n = (const struct vtable_registration *) p; + return (hashval_t) (DECL_UID (n->vtable_decl)); +} + +inline bool +registration_hasher::equal (const value_type *p1, const compare_type *p2) +{ + const struct vtable_registration *n1 = + (const struct vtable_registration *) p1; + const struct vtable_registration *n2 = + (const struct vtable_registration *) p2; + return (DECL_UID (n1->vtable_decl) == DECL_UID (n2->vtable_decl)); +} + +/* End of hashtable functions for "registered" hashtables. */ + + + +/* Hashtable definition and functions for vtbl_map_hash. */ + +struct vtbl_map_hasher : typed_noop_remove <struct vtbl_map_node> +{ + typedef struct vtbl_map_node value_type; + typedef struct vtbl_map_node compare_type; + static inline hashval_t hash (const value_type *); + static inline bool equal (const value_type *, const compare_type *); +}; + +/* Returns a hash code for P. */ + +inline hashval_t +vtbl_map_hasher::hash (const value_type *p) +{ + const struct vtbl_map_node n = *((const struct vtbl_map_node *) p); + return (hashval_t) IDENTIFIER_HASH_VALUE (n.class_name); +} + +/* Returns nonzero if P1 and P2 are equal. */ + +inline bool +vtbl_map_hasher::equal (const value_type *p1, const compare_type *p2) +{ + const struct vtbl_map_node n1 = *((const struct vtbl_map_node *) p1); + const struct vtbl_map_node n2 = *((const struct vtbl_map_node *) p2); + return (IDENTIFIER_HASH_VALUE (n1.class_name) == + IDENTIFIER_HASH_VALUE (n2.class_name)); +} + +/* Here are the two structures into which we insert vtable map nodes. + We use two data structures because of the vastly different ways we need + to find the nodes for various tasks (see comments in vtable-verify.h + for more details. */ + +typedef hash_table <vtbl_map_hasher> vtbl_map_table_type; +typedef vtbl_map_table_type::iterator vtbl_map_iterator_type; + +/* Vtable map variable nodes stored in a hash table. */ +static vtbl_map_table_type vtbl_map_hash; + +/* Vtable map variable nodes stored in a vector. */ +vec<struct vtbl_map_node *> vtbl_map_nodes_vec; + +/* Return vtbl_map node for CLASS_NAME without creating a new one. */ + +struct vtbl_map_node * +vtbl_map_get_node (tree class_type) +{ + struct vtbl_map_node key; + struct vtbl_map_node **slot; + + tree class_type_decl; + tree class_name; + unsigned int type_quals; + + if (!vtbl_map_hash.is_created()) + return NULL; + + gcc_assert (TREE_CODE (class_type) == RECORD_TYPE); + + + /* Find the TYPE_DECL for the class. */ + class_type_decl = TYPE_NAME (class_type); + + /* Verify that there aren't any qualifiers on the type. */ + type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl)); + gcc_assert (type_quals == TYPE_UNQUALIFIED); + + /* Get the mangled name for the unqualified type. */ + gcc_assert (HAS_DECL_ASSEMBLER_NAME_P (class_type_decl)); + class_name = DECL_ASSEMBLER_NAME (class_type_decl); + + key.class_name = class_name; + slot = (struct vtbl_map_node **) vtbl_map_hash.find_slot (&key, + NO_INSERT); + if (!slot) + return NULL; + return *slot; +} + +/* Return vtbl_map node assigned to BASE_CLASS_TYPE. Create new one + when needed. */ + +struct vtbl_map_node * +find_or_create_vtbl_map_node (tree base_class_type) +{ + struct vtbl_map_node key; + struct vtbl_map_node *node; + struct vtbl_map_node **slot; + tree class_type_decl; + unsigned int type_quals; + + if (!vtbl_map_hash.is_created()) + vtbl_map_hash.create (10); + + /* Find the TYPE_DECL for the class. */ + class_type_decl = TYPE_NAME (base_class_type); + + /* Verify that there aren't any type qualifiers on type. */ + type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl)); + gcc_assert (type_quals == TYPE_UNQUALIFIED); + + gcc_assert (HAS_DECL_ASSEMBLER_NAME_P (class_type_decl)); + key.class_name = DECL_ASSEMBLER_NAME (class_type_decl); + slot = (struct vtbl_map_node **) vtbl_map_hash.find_slot (&key, + INSERT); + + if (*slot) + return *slot; + + node = XNEW (struct vtbl_map_node); + node->vtbl_map_decl = NULL_TREE; + node->class_name = key.class_name; + node->uid = num_vtable_map_nodes++; + + node->class_info = XNEW (struct vtv_graph_node); + node->class_info->class_type = base_class_type; + node->class_info->class_uid = node->uid; + node->class_info->num_processed_children = 0; + + (node->class_info->parents).create (4); + (node->class_info->children).create (4); + + node->registered.create (16); + + node->is_used = false; + + vtbl_map_nodes_vec.safe_push (node); + gcc_assert (vtbl_map_nodes_vec[node->uid] == node); + + *slot = node; + return node; +} + +/* End of hashtable functions for vtable_map variables hash table. */ + +/* Given a gimple STMT, this function checks to see if the statement + is an assignment, the rhs of which is getting the vtable pointer + value out of an object. (i.e. it's the value we need to verify + because its the vtable pointer that will be used for a virtual + call). */ + +static bool +is_vtable_assignment_stmt (gimple stmt) +{ + + if (gimple_code (stmt) != GIMPLE_ASSIGN) + return false; + else + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + + if (TREE_CODE (lhs) != SSA_NAME) + return false; + + if (TREE_CODE (rhs) != COMPONENT_REF) + return false; + + if (! (TREE_OPERAND (rhs, 1)) + || (TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL)) + return false; + + if (! DECL_VIRTUAL_P (TREE_OPERAND (rhs, 1))) + return false; + } + + return true; +} + +/* This function attempts to recover the declared class of an object + that is used in making a virtual call. We try to get the type from + the type cast in the gimple assignment statement that extracts the + vtable pointer from the object (DEF_STMT). The gimple statement + usually looks something like this: + + D.2201_4 = MEM[(struct Event *)this_1(D)]._vptr.Event */ + +static tree +extract_object_class_type (tree rhs) +{ + tree result = NULL_TREE; + + /* Try to find and extract the type cast from that stmt. */ + if (TREE_CODE (rhs) == COMPONENT_REF) + { + tree op0 = TREE_OPERAND (rhs, 0); + tree op1 = TREE_OPERAND (rhs, 1); + + if (TREE_CODE (op1) == FIELD_DECL + && DECL_VIRTUAL_P (op1)) + { + if (TREE_CODE (op0) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (op0, 0)) == MEM_REF + && TREE_CODE (TREE_TYPE (TREE_OPERAND (op0, 0)))== RECORD_TYPE) + result = TREE_TYPE (TREE_OPERAND (op0, 0)); + else + result = TREE_TYPE (op0); + } + else if (TREE_CODE (op0) == COMPONENT_REF) + { + result = extract_object_class_type (op0); + if (result == NULL_TREE + && TREE_CODE (op1) == COMPONENT_REF) + result = extract_object_class_type (op1); + } + } + + return result; +} + +/* This function traces forward through the def-use chain of an SSA + variable to see if it ever gets used in a virtual function call. It + returns a boolean indicating whether or not it found a virtual call in + the use chain. */ + +static bool +var_is_used_for_virtual_call_p (tree lhs, int *mem_ref_depth) +{ + imm_use_iterator imm_iter; + bool found_vcall = false; + use_operand_p use_p; + + if (TREE_CODE (lhs) != SSA_NAME) + return false; + + if (*mem_ref_depth > 2) + return false; + + /* Iterate through the immediate uses of the current variable. If + it's a virtual function call, we're done. Otherwise, if there's + an LHS for the use stmt, add the ssa var to the work list + (assuming it's not already in the list and is not a variable + we've already examined. */ + + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, lhs) + { + gimple stmt2 = USE_STMT (use_p); + + if (gimple_code (stmt2) == GIMPLE_CALL) + { + tree fncall = gimple_call_fn (stmt2); + if (TREE_CODE (fncall) == OBJ_TYPE_REF) + found_vcall = true; + else + return false; + } + else if (gimple_code (stmt2) == GIMPLE_PHI) + { + found_vcall = var_is_used_for_virtual_call_p + (gimple_phi_result (stmt2), + mem_ref_depth); + } + else if (gimple_code (stmt2) == GIMPLE_ASSIGN) + { + tree rhs = gimple_assign_rhs1 (stmt2); + if (TREE_CODE (rhs) == ADDR_EXPR + || TREE_CODE (rhs) == MEM_REF) + *mem_ref_depth = *mem_ref_depth + 1; + + if (TREE_CODE (rhs) == COMPONENT_REF) + { + while (TREE_CODE (TREE_OPERAND (rhs, 0)) == COMPONENT_REF) + rhs = TREE_OPERAND (rhs, 0); + + if (TREE_CODE (TREE_OPERAND (rhs, 0)) == ADDR_EXPR + || TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF) + *mem_ref_depth = *mem_ref_depth + 1; + } + + if (*mem_ref_depth < 3) + found_vcall = var_is_used_for_virtual_call_p + (gimple_assign_lhs (stmt2), + mem_ref_depth); + } + + else + break; + + if (found_vcall) + return true; + } + + return false; +} + +/* Search through all the statements in a basic block (BB), searching + for virtual method calls. For each virtual method dispatch, find + the vptr value used, and the statically declared type of the + object; retrieve the vtable map variable for the type of the + object; generate a call to __VLTVerifyVtablePointer; and insert the + generated call into the basic block, after the point where the vptr + value is gotten out of the object and before the virtual method + dispatch. Make the virtual method dispatch depend on the return + value from the verification call, so that subsequent optimizations + cannot reorder the two calls. */ + +static void +verify_bb_vtables (basic_block bb) +{ + gimple_seq stmts; + gimple stmt = NULL; + gimple_stmt_iterator gsi_vtbl_assign; + gimple_stmt_iterator gsi_virtual_call; + + stmts = bb_seq (bb); + gsi_virtual_call = gsi_start (stmts); + for (; !gsi_end_p (gsi_virtual_call); gsi_next (&gsi_virtual_call)) + { + stmt = gsi_stmt (gsi_virtual_call); + + /* Count virtual calls. */ + if (gimple_code (stmt) == GIMPLE_CALL) + { + tree fncall = gimple_call_fn (stmt); + if (TREE_CODE (fncall) == OBJ_TYPE_REF) + total_num_virtual_calls++; + } + + if (is_vtable_assignment_stmt (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + tree vtbl_var_decl = NULL_TREE; + struct vtbl_map_node *vtable_map_node; + tree vtbl_decl = NULL_TREE; + gimple call_stmt; + const char *vtable_name = "<unknown>"; + tree tmp0; + bool found; + int mem_ref_depth = 0; + + /* Make sure this vptr field access is for a virtual call. */ + if (!var_is_used_for_virtual_call_p (lhs, &mem_ref_depth)) + continue; + + /* Now we have found the virtual method dispatch and + the preceding access of the _vptr.* field... Next + we need to find the statically declared type of + the object, so we can find and use the right + vtable map variable in the verification call. */ + tree class_type = extract_object_class_type + (gimple_assign_rhs1 (stmt)); + + gsi_vtbl_assign = gsi_for_stmt (stmt); + + if (class_type + && (TREE_CODE (class_type) == RECORD_TYPE) + && TYPE_BINFO (class_type)) + { + /* Get the vtable VAR_DECL for the type. */ + vtbl_var_decl = BINFO_VTABLE (TYPE_BINFO (class_type)); + + if (TREE_CODE (vtbl_var_decl) == POINTER_PLUS_EXPR) + vtbl_var_decl = TREE_OPERAND (TREE_OPERAND (vtbl_var_decl, 0), + 0); + + gcc_assert (vtbl_var_decl); + + vtbl_decl = vtbl_var_decl; + vtable_map_node = vtbl_map_get_node + (TYPE_MAIN_VARIANT (class_type)); + + gcc_assert (verify_vtbl_ptr_fndecl); + + /* Given the vtable pointer for the base class of the + object, build the call to __VLTVerifyVtablePointer to + verify that the object's vtable pointer (contained in + lhs) is in the set of valid vtable pointers for the + base class. */ + + if (vtable_map_node && vtable_map_node->vtbl_map_decl) + { + use_operand_p use_p; + ssa_op_iter iter; + + vtable_map_node->is_used = true; + vtbl_var_decl = vtable_map_node->vtbl_map_decl; + + if (TREE_CODE (vtbl_decl) == VAR_DECL) + vtable_name = IDENTIFIER_POINTER (DECL_NAME (vtbl_decl)); + + /* Call different routines if we are interested in + trace information to debug problems. */ + if (flag_vtv_debug) + { + int len1 = IDENTIFIER_LENGTH + (DECL_NAME (vtbl_var_decl)); + int len2 = strlen (vtable_name); + + call_stmt = gimple_build_call + (verify_vtbl_ptr_fndecl, 4, + build1 (ADDR_EXPR, + TYPE_POINTER_TO + (TREE_TYPE (vtbl_var_decl)), + vtbl_var_decl), + lhs, + build_string_literal + (len1 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (vtbl_var_decl))), + build_string_literal (len2 + 1, + vtable_name)); + } + else + call_stmt = gimple_build_call + (verify_vtbl_ptr_fndecl, 2, + build1 (ADDR_EXPR, + TYPE_POINTER_TO + (TREE_TYPE (vtbl_var_decl)), + vtbl_var_decl), + lhs); + + + /* Create a new SSA_NAME var to hold the call's + return value, and make the call_stmt use the + variable for that purpose. */ + tmp0 = make_temp_ssa_name (TREE_TYPE (lhs), NULL, "VTV"); + gimple_call_set_lhs (call_stmt, tmp0); + update_stmt (call_stmt); + + /* Find the next stmt, after the vptr assignment + statememt, which should use the result of the + vptr assignment statement value. */ + gsi_next (&gsi_vtbl_assign); + gimple next_stmt = gsi_stmt (gsi_vtbl_assign); + + if (!next_stmt) + return; + + /* Find any/all uses of 'lhs' in next_stmt, and + replace them with 'tmp0'. */ + found = false; + FOR_EACH_PHI_OR_STMT_USE (use_p, next_stmt, iter, + SSA_OP_ALL_USES) + { + tree op = USE_FROM_PTR (use_p); + if (op == lhs) + { + SET_USE (use_p, tmp0); + found = true; + } + } + update_stmt (next_stmt); + gcc_assert (found); + + /* Insert the new verification call just after the + statement that gets the vtable pointer out of the + object. */ + gsi_vtbl_assign = gsi_for_stmt (stmt); + gsi_insert_after (&gsi_vtbl_assign, call_stmt, + GSI_NEW_STMT); + + any_verification_calls_generated = true; + total_num_verified_vcalls++; + } + } + } + } +} + +/* Main function, called from pass->excute(). Loop through all the + basic blocks in the current function, passing them to + verify_bb_vtables, which searches for virtual calls, and inserts + calls to __VLTVerifyVtablePointer. */ + +unsigned int +vtable_verify_main (void) +{ + unsigned int ret = 1; + basic_block bb; + + FOR_ALL_BB (bb) + verify_bb_vtables (bb); + + return ret; +} + +/* Gate function for the pass. */ + +static bool +gate_tree_vtable_verify (void) +{ + return (flag_vtable_verify); +} + +/* Definition of this optimization pass. */ + +namespace { + +const pass_data pass_data_vtable_verify = +{ + GIMPLE_PASS, /* type */ + "vtable-verify", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_gate */ + true, /* has_execute */ + TV_VTABLE_VERIFICATION, /* tv_id */ + ( PROP_cfg | PROP_ssa ), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_update_ssa, /* todo_flags_finish */ +}; + +class pass_vtable_verify : public gimple_opt_pass +{ +public: + pass_vtable_verify(gcc::context *ctxt) + : gimple_opt_pass(pass_data_vtable_verify, ctxt) + {} + + /* opt_pass methods: */ + bool gate () { return gate_tree_vtable_verify (); } + unsigned int execute () { return vtable_verify_main (); } + +}; // class pass_vtable_verify + +} // anon namespace + +gimple_opt_pass * +make_pass_vtable_verify (gcc::context *ctxt) +{ + return new pass_vtable_verify (ctxt); +} + +#include "gt-vtable-verify.h" diff --git a/gcc/vtable-verify.h b/gcc/vtable-verify.h new file mode 100644 index 00000000000..7ac487bef52 --- /dev/null +++ b/gcc/vtable-verify.h @@ -0,0 +1,141 @@ +/* Copyright (C) 2013 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC 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 General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Virtual Table Pointer Security. */ + +#ifndef VTABLE_VERIFY_H +#define VTABLE_VERIFY_H + +#include "sbitmap.h" +#include "hash-table.h" + +/* The function decl used to create calls to __VLTVtableVerify. It must + be global because it needs to be initialized in the C++ front end, but + used in the middle end (in the vtable verification pass). */ + +extern tree verify_vtbl_ptr_fndecl; + +/* Global variable keeping track of how many vtable map variables we + have created. */ +extern unsigned num_vtable_map_nodes; + +/* Keep track of how many virtual calls we are actually verifying. */ +extern int total_num_virtual_calls; +extern int total_num_verified_vcalls; + +/* Each vtable map variable corresponds to a virtual class. Each + vtable map variable has a hash table associated with it, that keeps + track of the vtable pointers for which we have generated a call to + __VLTRegisterPair (with the current vtable map variable). This is + the hash table node that is used for each entry in this hash table + of vtable pointers. + + Sometimes there are multiple valid vtable pointer entries that use + the same vtable pointer decl with different offsets. Therefore, + for each vtable pointer in the hash table, there is also an array + of offsets used with that vtable. */ + +struct vtable_registration +{ + tree vtable_decl; /* The var decl of the vtable. */ + vec<unsigned> offsets; /* The offsets array. */ +}; + +struct registration_hasher : typed_noop_remove <struct vtable_registration> +{ + typedef struct vtable_registration value_type; + typedef struct vtable_registration compare_type; + static inline hashval_t hash (const value_type *); + static inline bool equal (const value_type *, const compare_type *); +}; + +typedef hash_table <registration_hasher> register_table_type; +typedef register_table_type::iterator registration_iterator_type; + +/* This struct is used to represent the class hierarchy information + that we need. Each vtable map variable has an associated class + hierarchy node (struct vtv_graph_node). Note: In this struct, + 'children' means immediate descendants in the class hierarchy; + 'descendant' means any descendant however many levels deep. */ + +struct vtv_graph_node { + tree class_type; /* The record_type of the class. */ + unsigned class_uid; /* A unique, monotonically + ascending id for class node. + Each vtable map node also has + an id. The class uid is the + same as the vtable map node id + for nodes corresponding to the + same class. */ + unsigned num_processed_children; /* # of children for whom we have + computed the class hierarchy + transitive closure. */ + vec<struct vtv_graph_node *> parents; /* Vector of parents in the graph. */ + vec<struct vtv_graph_node *> children; /* Vector of children in the graph.*/ + sbitmap descendants; /* Bitmap representing all this node's + descendants in the graph. */ +}; + +/* This is the node used for our hashtable of vtable map variable + information. When we create a vtable map variable (var decl) we + put it into one of these nodes; create a corresponding + vtv_graph_node for our class hierarchy info and store that in this + node; generate a unique (monotonically ascending) id for both the + vtbl_map_node and the vtv_graph_node; and insert the node into two + data structures (to make it easy to find in several different + ways): 1). A hash table ("vtbl_map_hash" in vtable-verify.c). + This gives us an easy way to check to see if we already have a node + for the vtable map variable or not; and 2). An array (vector) of + vtbl_map_nodes, where the array index corresponds to the unique id + of the vtbl_map_node, which gives us an easy way to use bitmaps to + represent and find the vtable map nodes. */ + +struct vtbl_map_node { + tree vtbl_map_decl; /* The var decl for the vtable map + variable. */ + tree class_name; /* The DECL_ASSEMBLER_NAME of the + class. */ + struct vtv_graph_node *class_info; /* Our class hierarchy info for the + class. */ + unsigned uid; /* The unique id for the vtable map + variable. */ + struct vtbl_map_node *next, *prev; /* Pointers for the linked list + structure. */ + register_table_type registered; /* Hashtable of vtable pointers for which + we have generated a _VLTRegisterPair + call with this vtable map variable. */ + bool is_used; /* Boolean indicating if we used this vtable map + variable in a call to __VLTVerifyVtablePointer. */ +}; + +/* Controls debugging for vtable verification. */ +extern bool vtv_debug; + +/* The global vector of vtbl_map_nodes. */ +extern vec<struct vtbl_map_node *> vtbl_map_nodes_vec; + +extern struct vtbl_map_node *vtbl_map_get_node (tree); +extern struct vtbl_map_node *find_or_create_vtbl_map_node (tree); +extern void vtbl_map_node_class_insert (struct vtbl_map_node *, unsigned); +extern bool vtbl_map_node_registration_find (struct vtbl_map_node *, + tree, unsigned); +extern bool vtbl_map_node_registration_insert (struct vtbl_map_node *, + tree, unsigned); + +#endif /* VTABLE_VERIFY_H */ |