From 5687881a1f362aa168a2cb2ab6796307323446da Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Mon, 24 Feb 2025 17:19:21 -0800 Subject: [PATCH] crash handling: remove catch-alls in main, crash hard This is because C++ exceptions are very tricky, and most notably, because a catch is run *after* things have been cleaned up, and we don't *want* them cleaned up if we want usable stack traces. $ nix nar Lix crashed. This is a bug. We would appreciate if you report it along with what caused it at https://git.lix.systems/lix-project/lix/issues with the following information included: Exception: std::runtime_error: lol Stack trace: 0# nix::getStackTrace[abi:cxx11]() in /home/jade/lix/lix4/outputs/out/lib/liblixutil.so 1# 0x00007D58FC909292 in /home/jade/lix/lix4/outputs/out/lib/liblixmain.so 2# 0x00007D58FC49220A in /nix/store/ybjcla5bhj8g1y84998pn4a2drfxybkv-gcc-13.3.0-lib/lib/libstdc++.so.6 3# 0x00007D58FC492275 in /nix/store/ybjcla5bhj8g1y84998pn4a2drfxybkv-gcc-13.3.0-lib/lib/libstdc++.so.6 4# 0x00007D58FC4924C7 in /nix/store/ybjcla5bhj8g1y84998pn4a2drfxybkv-gcc-13.3.0-lib/lib/libstdc++.so.6 5# 0x00005B9799B45036 in nix 6# 0x00005B9799B3697F in nix 7# 0x00005B9799B3924D in nix 8# nix::handleExceptions(std::__cxx11::basic_string, std::allocator > const&, std::function) in /home/jade/lix/lix4/outputs/out/lib/liblixmain.so 9# 0x00005B9799B38D2B in nix 10# 0x00007D58FC1E227E in /nix/store/wn7v2vhyyyi6clcyn0s9ixvl7d4d87ic-glibc-2.40-36/lib/libc.so.6 11# __libc_start_main in /nix/store/wn7v2vhyyyi6clcyn0s9ixvl7d4d87ic-glibc-2.40-36/lib/libc.so.6 12# 0x00005B9799A7CF05 in nix zsh: IOT instruction (core dumped) nix nar Sadly our symbolizer sucks. But that's, well, improvable. The core dump is at least much more useful now. Obviously with async things being full of catch-alls, we have the same problem. I think we might want to change those in another commit to only catch nix::BaseError subclasses and let the rest fall into a noexcept function so it will crash hard. The daemon also could use this treatment, but I would like to be able to send the client the crash message in phase 1 unwind. No idea how to achieve that; all the ways I have seen are heinous. Fixes: https://git.lix.systems/lix-project/lix/issues/698 Change-Id: I0b61f2e7a7003b92c39b1a0e90756979e5e59cb4 --- lix/libmain/shared.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lix/libmain/shared.cc b/lix/libmain/shared.cc index 4cb8728c9..5f21c13b3 100644 --- a/lix/libmain/shared.cc +++ b/lix/libmain/shared.cc @@ -328,13 +328,19 @@ int handleExceptions(const std::string & programName, std::function fun) } catch (const std::bad_alloc & e) { printError(error + "out of memory"); return 1; - } catch (const std::exception & e) { - // Random exceptions bubbling into main are cause for bug reports, crash - std::terminate(); - } catch (...) { - // Explicitly do not tolerate non-std exceptions escaping. - std::terminate(); } + // Deliberately do not catch random std exceptions! We have a nice + // std::terminate handler for those, and if we allow it to crash hard, it + // will produce better backtraces and more useful core dumps. + // + // We want to crash on those regardless, but omitting the handling is + // better than including it for that. + // + // If we catch them, we will land in terminate in phase 2 of unwind with + // all the frames between the throw and the catch already cleaned up, + // whereas if there is no handler (or it falls into a noexcept) it will + // terminate immediately at the end of phase 1 unwind while still having a + // stack and with no stack variables destroyed. return 0; }