Skip to content

[BUG] Segfault when using LTO and nested exception handlers #2073

@darcy-phipps

Description

@darcy-phipps

Description

LTO seems to mess up stack unwinding when exception handlers are nested across function calls. This causes a segfault while unwinding the stack. Without LTO, the stack is correctly unwound.

The following code reproduces the crash

#include <iostream>
#include <stdexcept>
#include <string>

std::string thrower() {
  throw std::out_of_range("nope");
}

std::string __attribute__((noinline)) wrapper() {
  try {
    return thrower();
  } catch (std::out_of_range&) {
    throw;
  } catch (...) {
    return "";
  }
}

int main() {
  try {
    auto s = wrapper();
    std::cout << "Wrapper returned: " << s << std::endl;
  } catch (std::out_of_range&) {
    std::cout << "Got a std::out_of_range" << std::endl;
  } catch (...) {
    std::cout << "Got some other exception" << std::endl;
  }

  return 0;
}

NOTE: The __attribute((noinline)) is required to keep the optimizer from inlining the wrapper which also makes the segfault go away. Having the nested handlers across function calls seems to be a requirement for this particular problem.

Building with ndk-27b, WITHOUT lto, and pushing to an emulator running Android 11

./android-ndk-r27b/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=x86_64-none-linux-android30 --sysroot=./android-ndk-r27b/toolchains/llvm/prebuilt/linux-x86_64/sysroot -g -O2 -std=gnu++14 -o atest_no_lto main.cpp
adb push atest_no_lto ./android-ndk-r27b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so /data/local/tmp
adb logcat -c
adb shell "LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/atest_no_lto"

Gives the output

Got a std::out_of_range

Building with ndk-27b, WITH lto, and pushing to an emulator running Android 11

./android-ndk-r27b/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=x86_64-none-linux-android30 --sysroot=./android-ndk-r27b/toolchains/llvm/prebuilt/linux-x86_64/sysroot -g -O2 -flto -std=gnu++14 -o atest_lto main.cpp
adb push atest_lto ./android-ndk-r27b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so /data/local/tmp
adb logcat -c
adb shell "LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/atest_lto"
adb logcat -d

Gives the output (and logcat)

Segmentation fault 
--------- beginning of crash
09-16 09:36:38.133  9973  9973 F libc    : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 9973 (atest_lto), pid 9973 (atest_lto)
--------- beginning of main
09-16 09:36:38.142  9976  9976 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone
09-16 09:36:38.143   299   299 I tombstoned: received crash request for pid 9973
09-16 09:36:38.143  9976  9976 I crash_dump64: performing dump of process 9973 (target tid = 9973)
09-16 09:36:38.143  9976  9976 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-16 09:36:38.143  9976  9976 F DEBUG   : Build fingerprint: 'google/sdk_gslim_x86_64/generic_x86_64:11/RSR1.210722.030/10147527:userdebug/dev-keys'
09-16 09:36:38.143  9976  9976 F DEBUG   : Revision: '0'
09-16 09:36:38.144  9976  9976 F DEBUG   : ABI: 'x86_64'
09-16 09:36:38.144  9976  9976 F DEBUG   : Timestamp: 2024-09-16 09:36:38-0400
09-16 09:36:38.144  9976  9976 F DEBUG   : pid: 9973, tid: 9973, name: atest_lto  >>> /data/local/tmp/atest_lto <<<
09-16 09:36:38.144  9976  9976 F DEBUG   : uid: 0
09-16 09:36:38.144  9976  9976 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
09-16 09:36:38.144  9976  9976 F DEBUG   : Cause: null pointer dereference
09-16 09:36:38.144  9976  9976 F DEBUG   :     rax 00007ffe572bbdf8  rbx 00007ffe572bb910  rcx 0000784a2d780510  rdx 0000000000000001
09-16 09:36:38.144  9976  9976 F DEBUG   :     r8  00007ffe572bbd00  r9  0000000000000028  r10 00005733a90621e8  r11 00005733a9062800
09-16 09:36:38.144  9976  9976 F DEBUG   :     r12 00007ffe572bba00  r13 0000784a2d780510  r14 00007ffe572bbd00  r15 0000000000000002
09-16 09:36:38.144  9976  9976 F DEBUG   :     rdi 00007ffe572bbd00  rsi 00007ffe572bb910
09-16 09:36:38.144  9976  9976 F DEBUG   :     rbp 0000000000000002  rsp 00007ffe572bb8f8  rip 0000000000000000
09-16 09:36:38.146  9976  9976 F DEBUG   : backtrace:
09-16 09:36:38.146  9976  9976 F DEBUG   :       #00 pc 0000000000000000  <unknown>
09-16 09:36:38.146  9976  9976 F DEBUG   :       #01 pc 0000000000125fc9  /data/local/tmp/libc++_shared.so (__unw_get_proc_info+9) (BuildId: 734ab2eaa203afbb89147d313e46146617bd1a2c)
09-16 09:36:38.146  9976  9976 F DEBUG   :       #02 pc 000000000000150f  [anon:scudo:primary]
09-16 09:36:38.146  9976  9976 F DEBUG   :       #03 pc 0000000000125dcb  /data/local/tmp/libc++_shared.so (_Unwind_GetLanguageSpecificData+11) (BuildId: 734ab2eaa203afbb89147d313e46146617bd1a2c)
09-16 09:36:38.149     0     0 D logd    : logdr: UID=0 GID=0 PID=9976 n tail=50 logMask=8 pid=9973 start=0ns timeout=0ns
09-16 09:36:38.150     0     0 D logd    : logdr: UID=0 GID=0 PID=9976 n tail=50 logMask=1 pid=9973 start=0ns timeout=0ns
09-16 09:36:38.150     0     0 D logd    : logdr: UID=0 GID=0 PID=9976 n tail=0 logMask=8 pid=9973 start=0ns timeout=0ns
09-16 09:36:38.151     0     0 D logd    : logdr: UID=0 GID=0 PID=9976 n tail=0 logMask=1 pid=9973 start=0ns timeout=0ns
09-16 09:36:38.153     0     0 I init    : Untracked pid 9976 exited with status 0
09-16 09:36:38.153     0     0 I init    : Untracked pid 9978 exited with status 0
--------- beginning of system
09-16 09:36:38.154   543   813 W NativeCrashListener: Couldn't find ProcessRecord for pid 9973
09-16 09:36:38.154   299   299 E tombstoned: Tombstone written to: /data/tombstones/tombstone_44

I'm attaching the full tombstone file in case it helps.
tombstone_44.zip

This also happens with ndk-26c and clang-17, which is where I first ran into it. I tried reproducing this problem directly on ubuntu 22.04 using clang-17 but was unsuccessful.

While digging around LLVM's bugtracker I found the following issue that may be relevant: llvm/llvm-project#56825

Affected versions

r26, r27

Canary version

No response

Host OS

Linux

Host OS version

Ubuntu 22.04

Affected ABIs

x86_64

Build system

ndk-build

Other build system

No response

minSdkVersion

n/a

Device API level

30

Metadata

Metadata

Labels

Type

No type

Projects

Status

Prebuilts submitted

Status

Merged

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions