Bug 8741

Summary: Any throw statement causes memory corruption
Product: uClibc++ Reporter: Ivan Kold <pixus.ru>
Component: Standard ComplianceAssignee: unassigned
Status: RESOLVED FIXED    
Severity: critical CC: pixus.ru, uclibc-cvs
Priority: P5    
Version: 0.2.5   
Target Milestone: ---   
Hardware: All   
OS: All   
Host: Target:
Build:
Attachments: Proposed patch v1

Description Ivan Kold 2016-03-03 20:32:43 UTC
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.


For your convenience, I have created OpenWRT package and feed for this test case:
package: https://github.com/CoolSpot/openwrt-test/throw-catch-sigsegv
feed: https://github.com/CoolSpot/openwrt-test-packages

### Installing the feed
cd ~/Documents/openwrt
echo "src-git openwrttest https://github.com/coolspot/openwrt-test-packages.git" >> ./feeds.conf
./scripts/feeds update openwrttest
./scripts/feeds install -a -p openwrttest

### Building the package
make menuconfig
# In the menuconfig select the package Test/sub-test/throw-catch-sigsegv to be built
make package/throw-catch-sigsegv/install
# Copy the package on the device (or VM)
scp ./bin/malta/packages/openwrttest/throw-catch-sigsegv_2016-02-25_malta_mips.ipk qemu:/tmp/
# on the device (or VM)
root@OpenWrt:~# opkg install /tmp/throw-catch-sigsegv_2016-02-25_malta_mips.ipk

I am using following setup for reproducing:
OpenWRT commit 4885087731e4ee9ac9823bd5cf3e777eecfd33d9 (Tue Feb 23 14:40:40 2016 +0000)

I will provide proposed patch soon.
Comment 1 Ivan Kold 2016-03-03 21:23:55 UTC
Created attachment 6376 [details]
Proposed patch v1

Attached patch adds definition of the struct __cxa_refcounted_exception,
functions __get_refcounted_exception_header_from_obj and __get_refcounted_exception_header_from_ue, changes functions __cxa_allocate_exception and __cxa_free_exception to allocate/free enough memory for __cxa_refcounted_exception instead of __cxa_exception.
Comment 2 Bernhard Reutner-Fischer 2018-09-30 18:50:18 UTC
2b58f77ee27c60842054fd0e5dd67f5d00e89eb8 some time ago should have fixed this.