The ubsan pass relies on rebuilding the cgraph edges in case it adds any new statements. Without adding the new callgraph edges the IPA inline analysis might be upset and respond with ICE. There are more ways how to fix this: we could schedule the pass_rebuild_cgraph_edges right after the ubsan pass, we could add TODO_rebuild_cgraph_edges, etc. But the cheapest approach seems to be doing the cgraph addition manually, which is what the following patch does.
Bootstrap-ubsan/regtest passed on x86_64-linux, ok for trunk? 2015-03-26 Marek Polacek <pola...@redhat.com> PR sanitizer/65583 * ubsan.c (ubsan_create_edge): New function. (instrument_bool_enum_load): Call it. (instrument_nonnull_arg): Likewise. (instrument_nonnull_return): Likewise. (instrument_object_size): Likewise. * g++.dg/ubsan/pr65583.C: New test. diff --git gcc/testsuite/g++.dg/ubsan/pr65583.C gcc/testsuite/g++.dg/ubsan/pr65583.C index e69de29..4e1149e 100644 --- gcc/testsuite/g++.dg/ubsan/pr65583.C +++ gcc/testsuite/g++.dg/ubsan/pr65583.C @@ -0,0 +1,140 @@ +// PR sanitizer/65583 +// { dg-do compile } +// { dg-options "-std=c++11 -fsanitize=undefined" } + +namespace std +{ + inline namespace __cxx11 + { + } + template < typename > class allocator; + template < class _CharT > struct char_traits; + namespace __cxx11 + { + template < typename _CharT, typename _Traits = + char_traits < _CharT >, typename _Alloc = + allocator < _CharT > >class basic_string; + typedef basic_string < char >string; + } +} +namespace std +{ + template < typename _Tp, _Tp __v > struct integral_constant + { + static constexpr _Tp value = __v; + }; + typedef integral_constant < bool, true > true_type; +} +namespace __gnu_cxx +{ + template < typename _Tp > class new_allocator + { + public: + typedef long unsigned size_type; + typedef _Tp value_type; + template < typename _Tp1 > struct rebind + { + typedef new_allocator < _Tp1 > other; + }; + }; +} +namespace std +{ + template < typename _Tp > using __allocator_base = + __gnu_cxx::new_allocator < _Tp >; + template < typename _Tp > class allocator:public __allocator_base < _Tp > + { + }; + template < typename _Alloc, typename _Tp > class __alloctr_rebind_helper + { + template < typename _Alloc2, typename _Tp2 > + static constexpr true_type _S_chk (typename _Alloc2::template rebind < + _Tp2 >::other *); + public: + using __type = decltype (_S_chk < _Alloc, _Tp > (nullptr)); + }; + template < typename _Alloc, typename _Tp, bool = + __alloctr_rebind_helper < _Alloc, + _Tp >::__type::value > struct __alloctr_rebind; + template < typename _Alloc, typename _Tp > struct __alloctr_rebind <_Alloc, + _Tp, true > + { + typedef typename _Alloc::template rebind < _Tp >::other __type; + }; + template < typename _Alloc > struct allocator_traits + { + typedef typename _Alloc::value_type value_type; + static value_type *_S_pointer_helper (...); + typedef decltype (_S_pointer_helper ((_Alloc *) 0)) __pointer; + typedef __pointer pointer; + template < typename _Tp > + static typename _Tp::size_type _S_size_type_helper (_Tp *); + typedef decltype (_S_size_type_helper ((_Alloc *) 0)) __size_type; + typedef __size_type size_type; + template < typename _Tp > using rebind_alloc = + typename __alloctr_rebind < _Alloc, _Tp >::__type; + }; +} +namespace __gnu_cxx +{ + template < typename _Alloc > struct __alloc_traits:std::allocator_traits < + _Alloc > + { + typedef std::allocator_traits < _Alloc > _Base_type; + template < typename _Tp > struct rebind + { + typedef typename _Base_type::template rebind_alloc < _Tp > other; + }; + }; +} +namespace std +{ + namespace __cxx11 + { + template < typename _CharT, typename _Traits, + typename _Alloc > class basic_string + { + typedef typename __gnu_cxx::__alloc_traits < _Alloc >::template rebind < + _CharT >::other _Char_alloc_type; + typedef __gnu_cxx::__alloc_traits < _Char_alloc_type > _Alloc_traits; + typedef _Char_alloc_type allocator_type; + typedef typename _Alloc_traits::size_type size_type; + typedef typename _Alloc_traits::pointer pointer; + struct _Alloc_hider:allocator_type + { + _Alloc_hider (pointer __dat, const _Alloc & __a) + { + } + }; + _Alloc_hider _M_dataplus; + union + { + size_type _M_allocated_capacity; + }; + pointer _M_local_data () + { + } + void _M_dispose () + { + _M_destroy (_M_allocated_capacity); + } + void _M_destroy (size_type __size) throw () + { + } + public: + basic_string (const _CharT * __s, const _Alloc & __a = _Alloc ()):_M_dataplus (_M_local_data (), + __a) + { + _M_dispose (); + } + }; + } + class FileHandle + { + std::string fname; + FileHandle (const char *fname); + }; + FileHandle::FileHandle (const char *fname):fname (fname) + { + } +} diff --git gcc/ubsan.c gcc/ubsan.c index 0e23d91..b9d9f30 100644 --- gcc/ubsan.c +++ gcc/ubsan.c @@ -686,6 +686,21 @@ is_ubsan_builtin_p (tree t) "__builtin___ubsan_", 18) == 0; } +/* Create a callgraph edge for statement STMT. */ + +static void +ubsan_create_edge (gimple stmt) +{ + gcall *call_stmt = dyn_cast <gcall *> (stmt); + basic_block bb = gimple_bb (stmt); + int freq = compute_call_stmt_bb_frequency (current_function_decl, bb); + cgraph_node *node = cgraph_node::get (current_function_decl); + tree decl = gimple_call_fndecl (call_stmt); + if (decl) + node->create_edge (cgraph_node::get_create (decl), call_stmt, bb->count, + freq); +} + /* Expand the UBSAN_BOUNDS special builtin function. */ bool @@ -1483,6 +1498,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi) } gimple_set_location (g, loc); gsi_insert_before (&gsi2, g, GSI_SAME_STMT); + ubsan_create_edge (g); *gsi = gsi_for_stmt (stmt); } @@ -1670,6 +1686,7 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi) } gimple_set_location (g, loc[0]); gsi_insert_before (gsi, g, GSI_SAME_STMT); + ubsan_create_edge (g); } *gsi = gsi_for_stmt (stmt); } @@ -1722,6 +1739,7 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi) } gimple_set_location (g, loc[0]); gsi_insert_before (gsi, g, GSI_SAME_STMT); + ubsan_create_edge (g); *gsi = gsi_for_stmt (stmt); } flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks; @@ -1818,6 +1836,7 @@ instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs) tree sizet; tree base_addr = base; + gimple bos_stmt = NULL; if (decl_p) base_addr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (base)), base); @@ -1834,6 +1853,17 @@ instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs) integer_zero_node); sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true, GSI_SAME_STMT); + /* If the call above didn't end up being an integer constant, go one + statement back and get the __builtin_object_size stmt. Save it, + we might need it later. */ + if (SSA_VAR_P (sizet)) + { + gsi_prev (gsi); + bos_stmt = gsi_stmt (*gsi); + + /* Move on to where we were. */ + gsi_next (gsi); + } } else return; @@ -1870,7 +1900,10 @@ instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs) } } - /* Nope. Emit the check. */ + if (bos_stmt && gimple_call_builtin_p (bos_stmt, BUILT_IN_OBJECT_SIZE)) + ubsan_create_edge (bos_stmt); + + /* We have to emit the check. */ t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true, GSI_SAME_STMT); ptr = force_gimple_operand_gsi (gsi, ptr, true, NULL_TREE, true, Marek