[OpenWrt-Devel] Exceptions are broken in the uClibc++ - any throw causes memory corruption

Ivan Koldaev pixus.ru at gmail.com
Fri Feb 26 20:05:12 EST 2016


Hi community!


I believe 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 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
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
corruptions still should take place, yielding undefined behavior.


I would like someone experienced with OpenWRT, musl and uClibc++ to review
and verify my discoveries, because there is chance that I miss some magic
compile flag that makes OpenWRT to build binaries with other implementation
of "__cxxabiv1::__cxa_throw" or I miss something else.
This bug seems to exist for many years (since 2009), so I wonder why no one
noticed that try-catch corrupts memory and it completely does not work for
musl-based builds. My quick research shown that most of the OpenWRT
packages do not use C++ exceptions at all, that is probably why. Also musl
libc which makes this issue very visible, was adopted just recently, so
perhaps not much time passed yet.


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 at 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)
uClibc++ compiled with DODEBUG="y" (CONFIG_DEBUG does not affect uClibc++).
qemu-mips version 2.3.0 (Debian 1:2.3+dfsg-5ubuntu9.1)

OpenWRT diffconfig:
CONFIG_TARGET_malta=y
CONFIG_TARGET_malta_be=y
CONFIG_TARGET_malta_be_Default=y
CONFIG_DEBUG=y
CONFIG_KERNEL_PERF_EVENTS=y
CONFIG_KERNEL_PROC_PAGE_MONITOR=y
CONFIG_KERNEL_PROFILING=y
CONFIG_KERNEL_SLABINFO=y
CONFIG_KERNEL_SLUB_DEBUG=y
CONFIG_KERNEL_SLUB_DEBUG_ON=y
CONFIG_NO_STRIP=y
CONFIG_OPENSSL_WITH_EC=y
CONFIG_PACKAGE_ar=y
CONFIG_PACKAGE_binutils=y
CONFIG_PACKAGE_block-mount=y
CONFIG_PACKAGE_ethtool=y
CONFIG_PACKAGE_gdbserver=y
CONFIG_PACKAGE_kmod-crypto-hash=y
CONFIG_PACKAGE_kmod-fs-ext4=y
CONFIG_PACKAGE_kmod-lib-crc16=y
CONFIG_PACKAGE_libbfd=y
CONFIG_PACKAGE_libcap=y
CONFIG_PACKAGE_libncurses=y
CONFIG_PACKAGE_libnl=y
CONFIG_PACKAGE_libnl-core=y
CONFIG_PACKAGE_libnl-genl=y
CONFIG_PACKAGE_libnl-nf=y
CONFIG_PACKAGE_libnl-route=y
CONFIG_PACKAGE_libopcodes=y
CONFIG_PACKAGE_libopenssl=y
CONFIG_PACKAGE_libpcap=y
CONFIG_PACKAGE_libpcre=y
CONFIG_PACKAGE_libpthread=y
CONFIG_PACKAGE_librt=y
CONFIG_PACKAGE_objdump=y
CONFIG_PACKAGE_screen=y
CONFIG_PACKAGE_strace=y
CONFIG_PACKAGE_throw-catch-sigsegv=y
CONFIG_PACKAGE_uclibcxx=y
CONFIG_TARGET_EXT4_BLOCKSIZE=4096
CONFIG_TARGET_EXT4_BLOCKSIZE_4K=y
CONFIG_TARGET_EXT4_MAXINODE=6000
CONFIG_TARGET_EXT4_RESERVED_PCT=0
CONFIG_TARGET_ROOTFS_EXT4FS=y
CONFIG_TARGET_ROOTFS_PARTSIZE=256


-- 
Best regards,
Ivan Koldaev.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/openwrt-devel/attachments/20160226/e2a746fc/attachment.htm>
-------------- next part --------------
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list