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

Reply via email to