From 220feee3d96d15ad993456e45c3f844e2006eefc Mon Sep 17 00:00:00 2001 From: appkins Date: Tue, 22 Sep 2026 15:08:23 -0500 Subject: [PATCH] fix: stop interposing __cxa_throw, which broke every catch in Plex Interposing __cxa_throw puts a frame belonging to this library between every throw in Plex and the catch that was meant to handle it, and Plex does not survive that. The first exception the process throws is fatal whatever it was, which is why this surfaced as three unrelated looking crashes -- all of them exceptions Plex throws and handles perfectly well on its own: std::out_of_range: basic_string std::domain_error: Invalid uuid length UnauthorizedException: HTTP status code 401 One request reproduces it in a minute, with no Kubernetes and no cluster: GET /media/providers, no token plain Plex, own SQLite -> 401, keeps running shim + fresh PostgreSQL -> terminates, uncaught without this hook -> 401, keeps running A throw/catch test built against libstdc++ passes with the hook loaded, which is how it survived review: libstdc++ and libgcc are a matched pair and the forward is harmless between them. Plex is libc++ with LLVM's unwinder, and that is the pairing that breaks. The hook and the __cxa_throw backtrace that depends on it are kept behind an "exception-hook" feature, off by default, because the backtrace is what found the NULL column-type bug and is worth having for the next one. Turning it on means accepting that Plex will die on its first exception. --- rust/plex-pg-core/Cargo.toml | 3 +++ rust/plex-pg-core/src/runtime_linux.rs | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/rust/plex-pg-core/Cargo.toml b/rust/plex-pg-core/Cargo.toml index 208a1379..161f26e0 100644 --- a/rust/plex-pg-core/Cargo.toml +++ b/rust/plex-pg-core/Cargo.toml @@ -6,6 +6,9 @@ build = "build.rs" [features] interpose = [] +# Interposing __cxa_throw breaks every catch in Plex; see runtime_linux.rs. +# Kept for diagnosis only. +exception-hook = [] [lib] name = "plex_pg_core" diff --git a/rust/plex-pg-core/src/runtime_linux.rs b/rust/plex-pg-core/src/runtime_linux.rs index bc3aa47c..c819937a 100644 --- a/rust/plex-pg-core/src/runtime_linux.rs +++ b/rust/plex-pg-core/src/runtime_linux.rs @@ -156,7 +156,24 @@ fn setup_exception_catcher_if_enabled() { } } -#[no_mangle] +/// Interposing `__cxa_throw` puts a frame belonging to this library between +/// every `throw` in Plex and the `catch` that was meant to handle it, and +/// Plex does not survive that: an unauthenticated `GET /media/providers` +/// returns a clean 401 without the shim and terminates with the same +/// exception uncaught with it. +/// +/// libc++abi: terminating with uncaught exception of type +/// UnauthorizedException: HTTP status code 401 +/// +/// Plex throws and catches routinely, so the first throw of the process is +/// fatal whatever it was -- which is why this has surfaced as three unrelated +/// looking crashes (`std::out_of_range`, `std::domain_error: Invalid uuid +/// length`, and the one above), each of them an exception Plex handles +/// normally. +/// +/// Built behind a feature so the hook and the backtrace that depends on it can +/// be turned back on for diagnosis, off by default. +#[cfg_attr(feature = "exception-hook", no_mangle)] /// # Safety /// This is an ABI-level interposition hook for C++ exceptions. /// Callers must follow the platform C++ ABI for `__cxa_throw`.