Patch for uClibc++ bug #8741 (https://bugs.busybox.net/show_bug.cgi?id=8741)
I have discovered that uClibc++ incorrectly implements C++ exception ABI, allocating not enough memory in the "__cxxabiv1::__cxa_allocate_exception": /// code begin /// retval = malloc (thrown_size + sizeof(__cxa_exception)); /// code end /// uClibc++ allocates "thrown_size + sizeof(__cxa_exception)" while stdlibc++ allocates "thrown_size += sizeof (__cxa_refcounted_exception);" since 2009-01-07 (https://gcc.gnu.org/bugzilla/attachment.cgi?id=17047) . The "__cxa_refcounted_exception" is wrapper around "__cxa_exception" and thus bigger than "__cxa_exception" alone. That causes memory corruption (buffer overflow) inside "__cxxabiv1::__cxa_throw" which is not implemented by uClibc++, but implemented by GCC's libsupc++: /// code begin (gcc-5.2.0/libstdc++-v3/libsupc++/eh_throw.cc:69) /// __cxa_refcounted_exception *header = __get_refcounted_exception_header_from_obj (obj); header->referenceCount = 1; /// code end /// In the code above, the "header->referenceCount = 1" writes in memory preceding region allocated for both thrown exception object and exception's structure. The "obj" is pointer to memory allocated by "__cxxabiv1::__cxa_allocate_exception", the "__get_refcounted_exception_header_from_obj (obj)" is defined as: /// code begin /// static inline __cxa_refcounted_exception * __get_refcounted_exception_header_from_obj (void *ptr) { return reinterpret_cast<__cxa_refcounted_exception *>(ptr) - 1; } /// code end /// Thus GCC's libsupc++ expects enough memory allocated preceding exception object to keep structure "__cxa_refcounted_exception", but uClibc++ allocates only "sizeof(__cxa_exception)" bytes before exception object. When binary is compiled for OpenWRT's musl libc standard library, the program crashes with SIGSEGV in the "free" call from "__cxxabiv1::__cxa_free_exception" because musl is very sensitive for memory corruption (musl's malloc stores meta-information about memory region in 8 bytes right before memory chunk itself). When compiled against glibc, the segmentation fault does not happen immediately in the "__cxxabiv1::__cxa_free_exception", but memory corruption still should take place, yielding undefined behavior. Signed-off-by: Ivan Kold <pixus...@gmail.com> --- include/unwind-cxx.h | 34 +++++++++++++++++++++++++++++++++- src/eh_alloc.cpp | 8 ++++---- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/include/unwind-cxx.h b/include/unwind-cxx.h index b773259..03e6428 100644 --- a/include/unwind-cxx.h +++ b/include/unwind-cxx.h @@ -1,5 +1,5 @@ // -*- C++ -*- Exception handling and frame unwind runtime interface routines. -// Copyright (C) 2001 Free Software Foundation, Inc. +// Copyright (C) 2001-2015 Free Software Foundation, Inc. // // This file is part of GCC. // @@ -13,6 +13,10 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. +// // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING. If not, write to // the Free Software Foundation, 59 Temple Place - Suite 330, @@ -40,6 +44,12 @@ #include <cstddef> #include "unwind.h" +// Original unwind-cxx.h also includes bits/atomic_word.h which is CPU-specific, +// but always defines _Atomic_word as typedef int . +// Only thing that differs is memory-barrier macroses. +typedef int _Atomic_word; + + #pragma GCC visibility push(default) namespace __cxxabiv1 @@ -79,6 +89,13 @@ struct __cxa_exception _Unwind_Exception unwindHeader; }; +struct __cxa_refcounted_exception +{ + // Manage this header. + _Atomic_word referenceCount; + // __cxa_exception must be last, and no padding can be after it. + __cxa_exception exc; +}; // A dependent C++ exception object consists of a header, which is a wrapper // around an unwind object header with additional C++ specific information, @@ -210,6 +227,21 @@ __get_exception_header_from_ue (_Unwind_Exception *exc) return reinterpret_cast<__cxa_exception *>(exc + 1) - 1; } +// Acquire the C++ refcounted exception header from the C++ object. +static inline __cxa_refcounted_exception * +__get_refcounted_exception_header_from_obj (void *ptr) +{ + return reinterpret_cast<__cxa_refcounted_exception *>(ptr) - 1; +} + +// Acquire the C++ refcounted exception header from the generic exception +// header. +static inline __cxa_refcounted_exception * +__get_refcounted_exception_header_from_ue (_Unwind_Exception *exc) +{ + return reinterpret_cast<__cxa_refcounted_exception *>(exc + 1) - 1; +} + } /* namespace __cxxabiv1 */ #pragma GCC visibility pop diff --git a/src/eh_alloc.cpp b/src/eh_alloc.cpp index 5098196..5feeae9 100644 --- a/src/eh_alloc.cpp +++ b/src/eh_alloc.cpp @@ -30,16 +30,16 @@ extern "C" void * __cxa_allocate_exception(std::size_t thrown_size) throw(){ void *retval; //The sizeof crap is required by Itanium ABI because we need to provide space for //accounting information which is implementaion (gcc) specified - retval = malloc (thrown_size + sizeof(__cxa_exception)); + retval = malloc (thrown_size + sizeof(__cxa_refcounted_exception)); if (0 == retval){ std::terminate(); } - memset (retval, 0, sizeof(__cxa_exception)); - return (void *)((unsigned char *)retval + sizeof(__cxa_exception)); + memset (retval, 0, sizeof(__cxa_refcounted_exception)); + return (void *)((unsigned char *)retval + sizeof(__cxa_refcounted_exception)); } extern "C" void __cxa_free_exception(void *vptr) throw(){ - free( (char *)(vptr) - sizeof(__cxa_exception) ); + free( (char *)(vptr) - sizeof(__cxa_refcounted_exception) ); } -- 2.5.0 _______________________________________________ uClibc mailing list uClibc@uclibc.org http://lists.busybox.net/mailman/listinfo/uclibc