From c3a26dff9abe4125daac5bd2e1ac8cfced1818ff Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Wed, 11 Jun 2014 23:41:04 +0400 Subject: [PATCH 1/2] Port to Boost.Core demangle() The port removes some conditional code from pretty_name() implementation. As a side effect, this improves portability (AFAICT, the previous version wouldn't do demangling on clang). Additionally, the commit fixes a possible buffer overrun if demangle() returns a string equal to cvr_saver_name or cvr_saver_name with trailing spaces. --- include/boost/type_index/stl_type_index.hpp | 58 +++------------------ 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/include/boost/type_index/stl_type_index.hpp b/include/boost/type_index/stl_type_index.hpp index b2821b6..6456321 100644 --- a/include/boost/type_index/stl_type_index.hpp +++ b/include/boost/type_index/stl_type_index.hpp @@ -29,6 +29,7 @@ #include #include // std::strcmp, std::strlen #include +#include #include #include #include @@ -38,17 +39,6 @@ #include #include - -#ifdef __GNUC__ -# include // abi::__cxa_demangle -#endif - -#if !defined(BOOST_MSVC) -# include -# include // std::free -# include // std::find, std::search -#endif - #if (defined(__EDG_VERSION__) && __EDG_VERSION__ < 245) \ || (defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION <= 744) # include @@ -133,60 +123,30 @@ inline const char* stl_type_index::name() const BOOST_NOEXCEPT { return data_->name(); } -namespace detail { - class free_at_scope_exit { - char* to_free_; - - public: - explicit free_at_scope_exit(char* to_free) BOOST_NOEXCEPT - : to_free_(to_free) - {} - - ~free_at_scope_exit() BOOST_NOEXCEPT { - std::free(to_free_); - } - }; -} - inline std::string stl_type_index::pretty_name() const { static const char cvr_saver_name[] = "boost::typeindex::detail::cvr_saver<"; -#if defined(_MSC_VER) - std::string ret = data_->name(); - std::string::size_type pos_beg = ret.find(cvr_saver_name); + // In case of MSVC demangle() is a no-op, and name() already returns demangled name. + // In case of GCC and Clang (on non-Windows systems) name() returns mangled name and demangle() undecorates it. + std::string ret = core::demangle(data_->name()); + std::string::size_type pos_beg = ret.find(cvr_saver_name, 0, sizeof(cvr_saver_name) - 1); if (pos_beg == std::string::npos) { return ret; } const char* begin = ret.c_str() + pos_beg + sizeof(cvr_saver_name) - 1; const char* end = ret.c_str() + ret.size() - 1; -#else - int status = 0; - char* demang = abi::__cxa_demangle(raw_name(), NULL, 0, &status); - detail::free_at_scope_exit scope(demang); - BOOST_ASSERT(!status); - const std::size_t length = std::strlen(demang); - const char* begin = std::search( - demang, demang + length, - cvr_saver_name, cvr_saver_name + sizeof(cvr_saver_name) - 1 - ); - if (begin == demang + length) { - return std::string(demang, demang + length); - } - begin += sizeof(cvr_saver_name) - 1; - const char* end = demang + length - 1; -#endif while (*begin == ' ') { // begin is zero terminated ++ begin; } - while (end != begin && *end != '>') { + while (end > begin && *end != '>') { -- end; } // we have cvr_saver_name somewhere at the start of the end - while (end != begin && *(end - 1) == ' ') { + while (end > begin && *(end - 1) == ' ') { -- end; } @@ -286,7 +246,7 @@ inline stl_type_index stl_type_index::type_id_with_cvr() BOOST_NOEXCEPT { template inline stl_type_index stl_type_index::type_id_runtime(const T& value) BOOST_NOEXCEPT { -#ifdef BOOST_NO_RTTI +#ifdef BOOST_NO_RTTI return value.boost_type_index_type_id_runtime_(); #else return typeid(value); @@ -295,6 +255,4 @@ inline stl_type_index stl_type_index::type_id_runtime(const T& value) BOOST_NOEX }} // namespace boost::typeindex - #endif // BOOST_TYPE_INDEX_STL_TYPE_INDEX_HPP - From 1633adfee479f9c4edaacafbe8c1130cfc0ff150 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Thu, 12 Jun 2014 22:23:48 +0400 Subject: [PATCH 2/2] Changed implementation to use the low level interface for demangling. Added a check for demangling errors, an exception is thrown in this case. The new implementation also does not allocate std::string until the parsing completes. --- include/boost/type_index/stl_type_index.hpp | 60 +++++++++++++-------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/include/boost/type_index/stl_type_index.hpp b/include/boost/type_index/stl_type_index.hpp index 6456321..8b29835 100644 --- a/include/boost/type_index/stl_type_index.hpp +++ b/include/boost/type_index/stl_type_index.hpp @@ -27,8 +27,10 @@ #endif #include -#include // std::strcmp, std::strlen +#include // std::strcmp, std::strlen, std::strstr +#include #include +#include #include #include #include @@ -125,34 +127,46 @@ inline const char* stl_type_index::name() const BOOST_NOEXCEPT { inline std::string stl_type_index::pretty_name() const { static const char cvr_saver_name[] = "boost::typeindex::detail::cvr_saver<"; + static BOOST_CONSTEXPR_OR_CONST std::string::size_type cvr_saver_name_len = sizeof(cvr_saver_name) - 1; // In case of MSVC demangle() is a no-op, and name() already returns demangled name. // In case of GCC and Clang (on non-Windows systems) name() returns mangled name and demangle() undecorates it. - std::string ret = core::demangle(data_->name()); - std::string::size_type pos_beg = ret.find(cvr_saver_name, 0, sizeof(cvr_saver_name) - 1); - if (pos_beg == std::string::npos) { - return ret; - } + core::scoped_demangled_name demangled_name(data_->name()); - const char* begin = ret.c_str() + pos_beg + sizeof(cvr_saver_name) - 1; - const char* end = ret.c_str() + ret.size() - 1; + const char* begin = demangled_name.get(); + if (!begin) + BOOST_THROW_EXCEPTION(std::runtime_error("Type name demangling failed")); - while (*begin == ' ') { // begin is zero terminated - ++ begin; - } + std::string::size_type len = std::strlen(begin); + const char* end = begin + len; - while (end > begin && *end != '>') { - -- end; - } + if (len > cvr_saver_name_len) { + const char* b = std::strstr(begin, cvr_saver_name); + if (b) { + b += cvr_saver_name_len; - // we have cvr_saver_name somewhere at the start of the end - while (end > begin && *(end - 1) == ' ') { - -- end; - } + // Trim leading spaces + while (*b == ' ') { // the string is zero terminated, we won't exceed the buffer size + ++ b; + } - if (begin >= end) { - // Some strange error in demangled name parsing - return begin; + // Skip the closing angle bracket + const char* e = end - 1; + while (e > b && *e != '>') { + -- e; + } + + // Trim trailing spaces + while (e > b && *(e - 1) == ' ') { + -- e; + } + + if (b < e) { + // Parsing seems to have succeeded, the type name is not empty + begin = b; + end = e; + } + } } return std::string(begin, end); @@ -162,9 +176,9 @@ inline std::string stl_type_index::pretty_name() const { inline std::size_t stl_type_index::hash_code() const BOOST_NOEXCEPT { #if _MSC_VER > 1600 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5 && defined(__GXX_EXPERIMENTAL_CXX0X__)) return data_->hash_code(); -#else +#else return boost::hash_range(raw_name(), raw_name() + std::strlen(raw_name())); -#endif +#endif }