<div dir="ltr"><div>Hi community!</div><div><br></div><div><br></div><div>I believe I have discovered that uClibc++ incorrectly implements C++ exception ABI, allocating not enough memory in the "__cxxabiv1::__cxa_allocate_exception":</div><div><br></div><div>/// code begin ///</div><div>retval = malloc (thrown_size + sizeof(__cxa_exception));</div><div>/// code end ///</div><div><br></div><div><br></div><div>uClibc++ allocates "thrown_size + sizeof(__cxa_exception)" while stdlibc++ allocates "thrown_size += sizeof (__cxa_refcounted_exception);" since 2009-01-07 (<a href="https://gcc.gnu.org/bugzilla/attachment.cgi?id=17047">https://gcc.gnu.org/bugzilla/attachment.cgi?id=17047</a>) .</div><div>The "__cxa_refcounted_exception" is wrapper around "__cxa_exception" and thus bigger than "__cxa_exception" alone.</div><div>That causes memory corruption (buffer overflow) inside "__cxxabiv1::__cxa_throw" which is implemented by GCC's libsupc++:</div><div><br></div><div>/// code begin (gcc-5.2.0/libstdc++-v3/libsupc++/eh_throw.cc:69) ///</div><div>__cxa_refcounted_exception *header</div><div> = __get_refcounted_exception_header_from_obj (obj);</div><div>header->referenceCount = 1;</div><div>/// code end ///</div><div><br></div><div><br></div><div>In the code above, the "header->referenceCount = 1" writes in memory preceding region allocated for both thrown exception object and exception structure.</div><div>The "obj" is pointer to memory allocated by "__cxxabiv1::__cxa_allocate_exception", the "__get_refcounted_exception_header_from_obj (obj)" is defined as:</div><div><br></div><div>/// code begin ///</div><div>static inline __cxa_refcounted_exception *</div><div>__get_refcounted_exception_header_from_obj (void *ptr)</div><div>{</div><div> return reinterpret_cast<__cxa_refcounted_exception *>(ptr) - 1;</div><div>}</div><div>/// code end ///</div><div><br></div><div><br></div><div>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.</div><div><br></div><div>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).</div><div>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.</div><div><br></div><div><br></div><div>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.</div><div>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.</div><div><br></div><div><br></div><div>For your convenience, I have created OpenWRT package and feed for this test case:</div><div>package: <a href="https://github.com/CoolSpot/openwrt-test/throw-catch-sigsegv">https://github.com/CoolSpot/openwrt-test/throw-catch-sigsegv</a></div><div>feed: <a href="https://github.com/CoolSpot/openwrt-test-packages">https://github.com/CoolSpot/openwrt-test-packages</a></div><div><br></div><div>### Installing the feed</div><div>cd ~/Documents/openwrt</div><div>echo "src-git openwrttest <a href="https://github.com/coolspot/openwrt-test-packages.git">https://github.com/coolspot/openwrt-test-packages.git</a>" >> ./feeds.conf</div><div>./scripts/feeds update openwrttest</div><div>./scripts/feeds install -a -p openwrttest</div><div><br></div><div>### Building the package</div><div>make menuconfig</div><div># In the menuconfig select the package Test/sub-test/throw-catch-sigsegv to be built</div><div>make package/throw-catch-sigsegv/install</div><div># Copy the package on the device (or VM)</div><div>scp ./bin/malta/packages/openwrttest/throw-catch-sigsegv_2016-02-25_malta_mips.ipk qemu:/tmp/</div><div># on the device (or VM)</div><div>root@OpenWrt:~# opkg install /tmp/throw-catch-sigsegv_2016-02-25_malta_mips.ipk</div><div><br></div><div>I am using following setup for reproducing:</div><div>OpenWRT commit 4885087731e4ee9ac9823bd5cf3e777eecfd33d9 (Tue Feb 23 14:40:40 2016 +0000)</div><div>uClibc++ compiled with DODEBUG="y" (CONFIG_DEBUG does not affect uClibc++).</div><div>qemu-mips version 2.3.0 (Debian 1:2.3+dfsg-5ubuntu9.1)</div><div><br></div><div>OpenWRT diffconfig:</div><div>CONFIG_TARGET_malta=y</div><div>CONFIG_TARGET_malta_be=y</div><div>CONFIG_TARGET_malta_be_Default=y</div><div>CONFIG_DEBUG=y</div><div>CONFIG_KERNEL_PERF_EVENTS=y</div><div>CONFIG_KERNEL_PROC_PAGE_MONITOR=y</div><div>CONFIG_KERNEL_PROFILING=y</div><div>CONFIG_KERNEL_SLABINFO=y</div><div>CONFIG_KERNEL_SLUB_DEBUG=y</div><div>CONFIG_KERNEL_SLUB_DEBUG_ON=y</div><div>CONFIG_NO_STRIP=y</div><div>CONFIG_OPENSSL_WITH_EC=y</div><div>CONFIG_PACKAGE_ar=y</div><div>CONFIG_PACKAGE_binutils=y</div><div>CONFIG_PACKAGE_block-mount=y</div><div>CONFIG_PACKAGE_ethtool=y</div><div>CONFIG_PACKAGE_gdbserver=y</div><div>CONFIG_PACKAGE_kmod-crypto-hash=y</div><div>CONFIG_PACKAGE_kmod-fs-ext4=y</div><div>CONFIG_PACKAGE_kmod-lib-crc16=y</div><div>CONFIG_PACKAGE_libbfd=y</div><div>CONFIG_PACKAGE_libcap=y</div><div>CONFIG_PACKAGE_libncurses=y</div><div>CONFIG_PACKAGE_libnl=y</div><div>CONFIG_PACKAGE_libnl-core=y</div><div>CONFIG_PACKAGE_libnl-genl=y</div><div>CONFIG_PACKAGE_libnl-nf=y</div><div>CONFIG_PACKAGE_libnl-route=y</div><div>CONFIG_PACKAGE_libopcodes=y</div><div>CONFIG_PACKAGE_libopenssl=y</div><div>CONFIG_PACKAGE_libpcap=y</div><div>CONFIG_PACKAGE_libpcre=y</div><div>CONFIG_PACKAGE_libpthread=y</div><div>CONFIG_PACKAGE_librt=y</div><div>CONFIG_PACKAGE_objdump=y</div><div>CONFIG_PACKAGE_screen=y</div><div>CONFIG_PACKAGE_strace=y</div><div>CONFIG_PACKAGE_throw-catch-sigsegv=y</div><div>CONFIG_PACKAGE_uclibcxx=y</div><div>CONFIG_TARGET_EXT4_BLOCKSIZE=4096</div><div>CONFIG_TARGET_EXT4_BLOCKSIZE_4K=y</div><div>CONFIG_TARGET_EXT4_MAXINODE=6000</div><div>CONFIG_TARGET_EXT4_RESERVED_PCT=0</div><div>CONFIG_TARGET_ROOTFS_EXT4FS=y</div><div>CONFIG_TARGET_ROOTFS_PARTSIZE=256</div><br clear="all"><div><br></div>-- <br><div class="gmail_signature">Best regards,<br>Ivan Koldaev.<br></div>
</div>