Some thoughts on a less slippery catch_unwind

It's been a few weeks, but I've finished my survey of how crates currently use catch_unwind() in practice. To summarize:

Clean handling methods

26 crates:

  1. Move the payload into a provided field, pointer, global variable, or thread-local variable.
  2. Optionally, set some flag to inform the caller that a panic occurred.
  3. Later, on the caller's side of the boundary, take the payload and resume_unwind.

22 crates:

  1. If a panic occurred, optionally log a message, then abort or exit the process (or equivalent).

19 crates:

  1. Do some additional processing or cleaning up, then resume_unwind.

6 crates:

  1. Optionally do some additional processing.
  2. Directly return the thread::Result (or equivalent) to the user.

3 crates:

  1. Create a panic-on-drop guard before the payload is processed.
  2. Only forget the guard after the payload has been dropped.

2 crates:

  1. If a panic occurred, independently panic again, resulting in an abort if the payload panics.

Leaky handling methods*

69 crates (at least 24 unsound):

  1. Produce a value, set some flags, or call some functions based on whether or not a panic occurred.
  2. Optionally, use downcast or downcast_ref on the payload to extract the panic message, and either log it or embed it in a value.
  3. Return and implicitly drop the payload.

13 crates (at least 5 unsound):

  1. Use let _ = catch_unwind(...); or catch_unwind(...).ok(); in an attempt to ignore all panics, which implicitly drops the payload.

4 crates:

  1. Direct control flow based on whether or not a panic occurred.
  2. At any point, implicitly drop the payload.

1 crate:

  1. Move the payload into a provided field, pointer, global variable, or thread-local variable, to be processed later.
  2. Implicitly drop the prior payload in that place.

* These methods can be permissible when the panic comes from a controlled source, e.g., from a unit test.

Methodology

I only included uses of catch_unwind() in library crates published to crates.io. I did not include uses within test cases, but I did include uses within proc macros and testing support crates. Initially, I just searched for lang:rust /\bcatch_unwind\b/ on GitHub Code Search, and added the crates from the 100 results it returned. Then, I scraped the crates.io registry directly, and I added all crate versions where:

  • the version is the latest non-prerelease, non-yanked version of the crate as of 2022-07-12;
  • the version has at least 10,000 total downloads on crates.io; and
  • the source repo is publicly available, usually on GitHub or a GitLab instance.

Many of the crates are old and unmaintained, but some are still frequently downloaded.

Raw results

These are quite messy, since I didn't have any consistent coding process. I just looked for enough details to categorize the uses and to judge their soundness.

abi_stable v0.10.4 (abi_stable_crates/abi_stable at 0dcb0681cd9ef94978150b1a68f988d780fc9f61 · rodrimati1992/abi_stable_crates · GitHub):
abi_stable::external_types::parking_lot::once::Closure::call_with_closure(): match with ref, return RResult, implicit drop (impossible?)
abi_stable::external_types::parking_lot::once::Closure::run_call()/abi_stable::external_types::parking_lot::once::ROnce::call_once(): match with ref, move into field, cross boundary, resume_unwind
abi_stable::library::__call_root_module_loader(): unwrap_or, return Result (uncontrolled, unsound!)

abi_stable_shared v0.10.3 (abi_stable_crates/abi_stable_shared at ec2638d8c37329e6bb7d47a25bee7fba2b79010b · rodrimati1992/abi_stable_crates · GitHub):
abi_stable_shared::test_utils::must_panic(): match Err(e), return Result

allo-isolate v0.1.12 (GitHub - sunshine-protocol/allo-isolate at ee2a2cb0bc1dc8a5006ced8b3c9b93343f8c084a):
allo_isolate::Isolate::catch_unwind()/<allo_isolate::catch_unwind::CatchUnwind as Future>::poll(): return Result

alpm v2.2.1 (alpm.rs/alpm at alpm-v2.2.1 · archlinux/alpm.rs · GitHub):
alpm::cb::dlcb(): let _ (uncontrolled, unsound!)
alpm::cb::eventcb(): let _ (uncontrolled, unsound!)
alpm::cb::fetchcb(): unwrap_or, return c_int (uncontrolled, unsound!)
alpm::cb::logcb(): let _ (uncontrolled, unsound!)
alpm::cb::progresscb(): let _ (uncontrolled, unsound!)
alpm::cb::questioncb(): let _ (uncontrolled, unsound!)

async-global-executor v2.2.0 (GitHub - async-rs/async-global-executor at v2.2.0):
async_global_executor::threading::thread_main_loop(): if is_ok, break, implicit drop (uncontrolled!)
async_global_executor::threading::wait_for_local_executor_completion(): if is_ok, break, implicit drop (uncontrolled!)

async-io v1.7.0 (GitHub - smol-rs/async-io at v1.7.0):
async_io::src::reactor::ReactorLock::react(): ok (uncontrolled!)
async_io::src::reactor::Source::poll_ready(): ok (uncontrolled!)

bcc v0.0.32 (GitHub - rust-bpf/rust-bcc at v0.0.32):
bcc::ring_buf::callback::raw_callback(): let _ (uncontrolled, unsound!)
bcc::perf_event::callback::raw_callback(): let _ (uncontrolled, unsound!)

blocking v1.2.0 (GitHub - smol-rs/blocking at v1.2.0):
blocking::Executor::main_loop(): ok (uncontrolled!)

boring v2.0.0 (boring/boring at db6867b794303c7666c922c7f8ef5cf3d5f58396 · cloudflare/boring · GitHub):
boring::ssl::bio::bread()/boring::ssl::SslStream::check_panic(): match Err(err), move into field, cross boundary, resume_unwind
boring::ssl::bio::bwrite()/boring::ssl::SslStream::check_panic(): match Err(err), move into field, cross boundary, resume_unwind
boring::ssl::bio::ctrl()/boring::ssl::SslStream::check_panic(): match Err(err), move into field, cross boundary, resume_unwind
boring::util::invoke_passwd_cb()/<boring::util::CallbackState as Drop>::drop(): match Err(err), move into field, cross boundary, resume_unwind

brotli v3.3.4 (https://github.com/dropbox/rust-brotli/tree/3.3.4):
brotli::ffi::compressor::catch_panic()/brotli::ffi::compressor::BrotliEncoderCompress(): match Err(panic_err), call function with ref, implicit drop (controlled)
brotli::ffi::compressor::catch_panic()/brotli::ffi::compressor::BrotliEncoderCompressStream(): match Err(panic_err), call function with ref, implicit drop (controlled)
brotli::ffi::compressor::catch_panic()/brotli::ffi::compressor::BrotliEncoderSetCustomDictionary(): if let Err(panic_err), call function with ref, implicit drop (impossible)
brotli::ffi::compressor::catch_panic()/brotli::ffi::multicompress::BrotliEncoderCompressMulti(): match Err(panic_err), call function with ref, implicit drop (controlled)
brotli::ffi::compressor::catch_panic()/brotli::ffi::multicompress::BrotliEncoderCompressWorkPool(): match Err(panic_err), call function with ref, implicit drop (controlled)
brotli::ffi::compressor::catch_panic()/brotli::ffi::multicompress::BrotliEncoderDestroyWorkPool(): if let Err(panic_err), call function with ref, implicit drop (controlled)
brotli::ffi::compressor::catch_panic_cstate()/brotli::ffi::compressor::BrotliEncoderCreateInstance(): match Err(err), call function with ref, implicit drop (controlled)
brotli::ffi::multicompress::catch_panic_wstate()/brotli::ffi::multicompress::BrotliEncoderCreateWorkPool(): match Err(err), call function with ref, implicit drop (controlled)

brotli-decompressor v2.3.2 (GitHub - dropbox/rust-brotli-decompressor at 67d75006199b2d062f8e74706cb104add012a96e):
brotli_decompressor::ffi::catch_panic()/brotli_decompressor::ffi::BrotliDecoderDecompressStream(): match Err(mut readable_err), call function with ref, set indep. field, return BrotliDecoderResult, implicit drop (impossible)
brotli_decompressor::ffi::catch_panic_state()/brotli_decompressor::ffi::BrotliDecoderCreateInstance(): match Err(mut e), call function with ref, return *mut BrotliDecoderState, implicit drop (controlled)

cairo-rs v0.15.12 (gtk-rs-core/cairo at 0.15.12 · gtk-rs/gtk-rs-core · GitHub):
cairo_rs::image_surface_png::read_func()/cairo_rs::image_surface_png::ImageSurface::create_from_png(): match Err(payload), move into field, cross boundary, resume_unwind
cairo_rs::image_surface_png::write_func()/cairo_rs::image_surface_png::ImageSurface::write_to_png(): match Err(payload), move into field, cross boundary, resume_unwind
cairo_rs::stream::write_callback()/cairo_rs::stream::Surface::finish_output_stream(): match Err(payload), move into field, cross boundary, resume_unwind

civet v0.11.0 (GitHub - jtgeibel/rust-civet at v0.11.0) [old]:
civet::raw::raw_handler(): match Err(..), return i32 (uncontrolled, unsound!)

cool_asserts v2.0.3 (GitHub - Lucretiel/cool_asserts at 2.0.3):
cool_asserts::assert_panics::assert_panics!: match Err($panic), maybe call function with ref, implicit drop (controlled)

cpython v0.7.0 (GitHub - dgrunwald/rust-cpython at 0.7.0):
cpython::function::handle_callback(): match Err(err), create AbortOnDrop guard, implicit drop, forget guard

credibility v0.1.3 (GitHub - antifuchs/credibility at v0.1.3):
credibility::test_block::TestBlock::eval_aver()/<credibility::reporter::DefaultTestReporter as TestReporter>::averred(): if is_err, set indep. field, implicit drop (controlled)
credibility::test_block::TestBlock::eval_aver()/<credibility::selftest::TestTracker as TestReporter>::averred(): call indep. function, match Err(_), set indep. field, implicit drop (controlled)
credibility::test_block::TestBlock::eval_aver(): move into trait function

crossbeam-utils v0.8.10 (crossbeam/crossbeam-utils at crossbeam-utils-0.8.10 · crossbeam-rs/crossbeam · GitHub):
crossbeam_utils::thread::scope(): call indep. functions, match Err(err), resume_unwind

cryptobox-c v1.1.3 (GitHub - wireapp/cryptobox-c at v1.1.3) [old]:
cryptobox::catch_unwind(): match Err(_), return CBoxResult, implicit drop (controlled)

csound v0.1.8 (GitHub - neithanmo/csound-rs at v0.1.8):
csound::callbacks::Trampoline::catch(): match Err(_), exit

cucumber v0.13.0 (GitHub - cucumber-rs/cucumber at v0.13.0):
cucumber::runner::basic::Executor::run_before_hook()/cucumber::runner::basic::Executor::run_scenario()/cucumber::runner::basic::Executor::emit_failed_events(): match Err((panic_info, world)), move into ExecutionFailure, call function with ref, call indep. functions, err, if let Some(exec_err), match ExecutionFailure::BeforeHookPanicked { panic_info, meta, .. }, move into Cucumber, move into channel (uncontrolled?)

curl v0.4.43 (GitHub - alexcrichton/curl-rust at 0.4.43):
curl::panic::catch()/curl::panic::propagate(): match Err(e), move into global, cross boundary, resume_unwind

directwrite v0.1.4 (GitHub - Connicpu/directwrite-rs at 39c4dea396e833920cdd4eff20138944f067a7ff):
directwrite::inline_object::vtbl::draw(): match Err(_), return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::inline_object::vtbl::get_break_conditions(): match Err(_), return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::inline_object::vtbl::get_metrics(): match Err(_), return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::inline_object::vtbl::get_overhand_metrics(): match Err(_), return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::text_renderer::vtbl::comref::draw_glyph_run(): match _, return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::text_renderer::vtbl::comref::draw_inline_object(): match _, return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::text_renderer::vtbl::comref::draw_strikethrough(): match _, return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::text_renderer::vtbl::comref::draw_underline(): match _, return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::text_renderer::vtbl::comref::get_current_transform(): match Err(_), return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::text_renderer::vtbl::comref::get_pixels_per_dip(): match Err(_), return HRESULT, implicit drop (uncontrolled, unsound!)
directwrite::text_renderer::vtbl::comref::is_pixel_snapping_disabled(): match Err(_), return HRESULT, implicit drop (uncontrolled, unsound!)

easy-parallel v3.2.0 (GitHub - smol-rs/easy-parallel at v3.2.0):
easy_parallel::Parallel::finish(): call indep. functions, if let Some(err), resume_unwind

emacs v0.18.0 (GitHub - ubolonton/emacs-module-rs at a9b573c3ef443e9adf04cfc349ac7b3bc91b28e2):
<emacs::func::CallEnv as HandleCall>::handle_call()/emacs::env::Env::handle_panic(): match Err(error), attempt to downcast, call function with ref, implicit drop (uncontrolled, unwound! incidentally unsound!)
emacs::init::initialize(): match Err(e), call function with ref, return c_int, implicit drop (uncontrolled, unsound! incidentally unsound!)

ert v0.2.2 (GitHub - YushiOMOTE/ert at 0fbcee6b3c377d06aada4199d28f63445e255234):
ert::router::Via::new()/<ert::router::Via as Future>::poll(): expect

execution-context v0.1.0 (GitHub - mitsuhiko/rust-execution-context at 0.1.0) [old]:
execution_context::ctx::ExecutionContext::run(): set indep. field, match Err(err), resume_unwind

festive v0.2.2 (GitHub - estk/festive at c69ce3ab08196a678d632ada7425b497b5c7c0ba):
festive::fork::fork_impl(): match Err(_), exit

ffi-support v0.4.4 (GitHub - mozilla/ffi-support at v0.4.4):
ffi_support::call_with_result_impl()/<ffi_support::error::ExternError as From<Box<dyn Any + Send + 'static>>>::from(): match Err(e), call functions with ref, return ExternError, implicit drop (uncontrolled, unsound!)

ffi-toolkit v0.5.0 (rust-fil-ffi-toolkit/ffi-toolkit at ffi-toolkit-v0.5.0 · filecoin-project/rust-fil-ffi-toolkit · GitHub):
ffi_toolkit::catch_panic_response(): match Err(panic), call function with ref, call indep. functions, return *mut T, implicit drop (uncontrolled, unsound!)

ffms2 v0.2.0 (GitHub - rust-av/ffms2-rs at 52a43f3566dfa55784fe7fc5f41d2a2cf7ac0241):
ffms2::index::Indexer::ProgressCallback::IndexCallback(): if is_err, abort

fil-rustacuda v0.1.3 (GitHub - filecoin-project/fil-rustacuda at v0.1.3):
fil_rustacuda::stream::callback_wrapper(): let _ (uncontrolled, unsound!)

findshlibs v0.10.2 (GitHub - gimli-rs/findshlibs at 0.10.2):
findshlibs::linux::SharedLibrary::callback()/findshlibs::linux::SharedLibrary::each(): match Err(panicked), move into Option field, cross boundary, if let Some(panic), resume_unwind

flaky_test v0.1.0 (GitHub - denoland/flaky_test at v0.1.0):
flaky_test::flaky_test::#name(): if is_ok, else if i == 2, (unwrap_err, resume_unwind), else implicit drop (controlled)

frame-benchmarking v3.1.0 (substrate/frame/benchmarking at f6de92e1f353be3b88a575187a22a3827a823bf2 · paritytech/substrate · GitHub):
frame_benchmarking::impl_benchmark_test_suite!::benchmark_tests::test_benchmarks(): if let Err(err), call function with ref, set indep. field, implicit drop (controlled)

fruity v0.3.0 (GitHub - nvzqz/fruity at v0.3.0):
fruity::dispatch::queue::DispatchQueue::apply(): match Err(_error), abort
fruity::dispatch::queue::DispatchQueue::apply_auto(): match Err(_error), abort
fruity::dispatch::queue::DispatchQueue::spawn_async(): match Err(_error), abort
fruity::dispatch::queue::DispatchQueue::spawn_sync(): cross boundary, match Err(error), resume_unwind

fruity__bbqsrc v0.2.0 (GitHub - nvzqz/fruity at eadc4f576936c554ac2b9935679281230040c644):
fruity__bbqsrc::dispatch::queue::DispatchQueue::apply(): match Err(_error), abort
fruity__bbqsrc::dispatch::queue::DispatchQueue::apply_auto(): match Err(_error), abort
fruity__bbqsrc::dispatch::queue::DispatchQueue::spawn_async(): match Err(_error), abort
fruity__bbqsrc::dispatch::queue::DispatchQueue::spawn_sync(): cross boundary, match Err(error), resume_unwind

futures-cpupool v0.1.8 (futures-rs/futures-cpupool at futures-cpupool-0.1.8 · rust-lang/futures-rs · GitHub):
futures_cpupool::CpuPool::spawn()/<futures_cpupool::CpuFuture as Future>::poll(): resume_unwind

futures-executor-preview v0.2.2 (futures-rs/futures-executor at 0.2.2 · rust-lang/futures-rs · GitHub):
<futures_executor_preview::spawn::SpawnWithHandle as Future>::poll()/<futures_executor_preview::spawn::JoinHandle as Future>::poll(): cross boundary, match Err(e), resume_unwind

futures-lite v1.12.0 (GitHub - smol-rs/futures-lite at v1.12.0):
futures_lite::future::FutureExt::catch_unwind()/<futures_lite::future::CatchUnwind as Future>::poll(): return Result

futures-util v0.3.21 (futures-rs/futures-util at 0.2.2 · rust-lang/futures-rs · GitHub):
futures_util::future::future::FutureExt::catch_unwind()/<futures_util::future::future::catch_unwind::CatchUnwind as Future>::poll(): return Result
futures_util::future::future::FutureExt::remote_handle()/<futures_util::future::future::remote_handle::RemoteHandle as Future>::poll(): resume_unwind
futures_util::stream::stream::StreamExt::catch_unwind()/<futures_util::stream::stream::catch_unwind::CatchUnwind as Stream>::poll_next(): set indep. field, return Result

futures-util-preview v0.2.2 (futures-rs/futures-util at 0.3.21 · rust-lang/futures-rs · GitHub):
futures_util::future::FutureExt::catch_unwind()/<futures_util::future::catch_unwind::CatchUnwind as Future>::poll(): return Result
futures_util::stream::StreamExt::catch_unwind()/<futures_util::stream::catch_unwind::CatchUnwind as Stream>::poll_next(): return Result

galvanic-assert v0.8.7 (GitHub - mindsbackyard/galvanic-assert at 33855f4a2ef829ddd5652e7edbbf0249dddf77b6):
galvanic_assert::assert_that!($actual:expr, does not panic): if is_err, call indep. function, implicit drop (controlled)
galvanic_assert::assert_that!($actual:expr, panics): if is_ok, else implicit drop (controlled)
galvanic_assert::get_expectation_for!($actual:expr, does not panic): if is_err, else return Expectation, implicit drop (controlled)
galvanic_assert::get_expectation_for!($actual:expr, panics): if is_ok, else return Expectation, implicit drop (controlled)

galvanic-test v0.2.0 (GitHub - mindsbackyard/galvanic-test at v0.2.0):
galvanic_test::test!: if is_err, call indep. functions, implicit drop (controlled)

generator v0.7.0 (GitHub - Xudong-Huang/generator-rs at 0.7.0):
generator::gen_impl::gen_init()/generator::gen_impl::GeneratorImpl::resume_gen(): if let Err(cause), call function with ref, call indep. function, move into local, cross boundary, if let Some(err), resume_unwind

gio v0.15.12 (gtk-rs-core/gio at 0.15.12 · gtk-rs/gtk-rs-core · GitHub):
gio::read_input_stream::AnyReader::with_inner()/gio::read_input_stream::ReadInputStream::close_and_take(): match Err(panic), move into field, cross boundary, resume_unwind
gio::write_output_stream::AnyWriter::with_inner()/gio::write_output_stream::WriteOutputStream::close_and_take(): match Err(panic), move into field, cross boundary, resume_unwind

git2 v0.14.4 (GitHub - rust-lang/git2-rs at 0.14.4):
git2::panic::wrap()/git2::panic::check(): match Err(e), move into global Option, cross boundary, if let Some(err), resume_unwind

glow v0.11.2 (GitHub - grovesNL/glow at 3bf1f8acef58edaeda3275b53b3cc00d7615d7f0):
glow::native::raw_debug_message_callback(): ok (uncontrolled, unsound!)

gstreamer v0.18.7 (gstreamer · 0.18.7 · GStreamer / gstreamer-rs · GitLab):
gstreamer::subclass::error::panic_to_error!: match Err(err), call functions with ref, return $ret, implicit drop (uncontrolled, unsound!)
gstreamer::subclass::plugin_1_12::plugin_define!::plugin_desc::plugin_init_trampoline(): match Err(err), call functions with ref, return gboolean, implicit drop (uncontrolled, unsound!)
gstreamer::subclass::plugin_1_14::plugin_define!::plugin_desc::plugin_init_trampoline(): match Err(err), call functions with ref, return gboolean, implicit drop (uncontrolled, unsound!)

gstreamer-app v0.18.7 (gstreamer-app · 0.18.7 · GStreamer / gstreamer-rs · GitLab):
gstreamer_app::app_sink::trampoline_eos(): match Err(err), set indep. field, call function with ref, implicit drop (uncontrolled, unsound!)
gstreamer_app::app_sink::trampoline_new_event(): match Err(err), set indep. field, call function with ref, return gboolean, implicit drop (uncontrolled, unsound!)
gstreamer_app::app_sink::trampoline_new_preroll(): match Err(err), set indep. field, call function with ref, return GstFlowReturn, implicit drop (uncontrolled, unsound!)
gstreamer_app::app_sink::trampoline_new_sample(): match Err(err), set indep. field, call function with ref, return GstFlowReturn, implicit drop (uncontrolled, unsound!)
gstreamer_app::app_src::trampoline_enough_data(): match Err(err), set indep. field, call function with ref, implicit drop (uncontrolled, unsound!)
gstreamer_app::app_src::trampoline_need_data(): match Err(err), set indep. field, call function with ref, implicit drop (uncontrolled, unsound!)
gstreamer_app::app_src::trampoline_seek_data(): match Err(err), set indep. field, call function with ref, implicit drop (uncontrolled, unsound!)

guillotiere v0.6.2 (GitHub - nical/guillotiere at bf7e9464390485a96399f77e9b1bff3d442b8258):
guillotiere::recording::Recording::replay()/guillotiere::recording::Recording::find_reduced_testcase(): is_ok, implicit drop (controlled)

gurobi v0.3.4 (https://github.com/ubnt-intrepid/rust-gurobi/tree/v0.3.4) [old]:
gurobi::model::callback_wrapper(): match Err(e), return c_int, implicit drop (uncontrolled, unsound!)

hdf5 v0.8.1 (https://github.com/aldanor/hdf5-rust/tree/v0.8.1):
hdf5::error::ErrorStack::expand::callback(): unwrap_or, return herr_t (controlled)

helix v0.7.5 (https://github.com/tildeio/helix/tree/v0.7.5):
helix::macros::init::handle_exception!/helix::errors::Error::from_any(): map_err(|e|), attempt to downcast, unwrap_or_else(|any|), call function with ref, return Error, implicit drop (uncontrolled, unsound!)

honggfuzz v0.5.54 (https://github.com/rust-fuzz/honggfuzz-rs/tree/v0.5.54):
honggfuzz::fuzz(): is_err, implicit drop (uncontrolled!)

httpbis v0.9.1 (https://github.com/stepancheg/rust-http2/tree/v0.9.1):
httpbis::client_died_error_holder::SomethingDiedErrorHolder::wrap_future()/httpbis::misc::any_to_string(): if let Err(e), attempt to downcast, else implicit drop (uncontrolled!)

hyper v0.14.20 (https://github.com/hyperium/hyper/tree/v0.14.20):
hyper::ffi::macros::ffi_fn!::$name(): match Err(_), return default value, implicit drop (uncontrolled, unsound!)

imgui v0.8.2 (https://github.com/imgui-rs/imgui-rs/tree/v0.8.2/imgui):
imgui::clipboard::get_clipboard_text(): unwrap_or_else(|_|), call function, abort (incidentally unsound!)
imgui::clipboard::set_clipboard_text(): unwrap_or_else(|_|), call function, abort (incidentally unsound!)

interprocess v1.1.1 (https://github.com/kotauskas/interprocess/tree/1.1.1):
interprocess::os::unix::signal::signal_receiver(): unwrap_or_else(|_|), abort
interprocess::os::windows::signal::signal_receiver(): unwrap_or_else(|_|), abort

ladspa v0.3.4 (https://github.com/nwoeanhinnogaehr/ladspa.rs/tree/e8c899912225ec6b8c93146aa886c09c1846d766) [old]:
ladspa::fii::call_user_code!: match Err(_), call indep. function, return Option, implicit drop (uncontrolled, unsound!)

lambda_runtime v0.5.1 (https://github.com/awslabs/aws-lambda-rust-runtime/tree/v0.5.1-runtime/lambda-runtime):
lambda_runtime::Runtime::run(): match Err(err), call functions with ref, return Result, implicit drop (uncontrolled!)

libfuzzer-sys v0.4.3 (https://github.com/rust-fuzz/libfuzzer/tree/0.4.3):
libfuzzer_sys::test_input_wrap(): err, if is_some, abort (incidentally unsound!)

liblightning v0.0.2 (https://github.com/losfair/liblightning/tree/41056bb1cfd2b870ac8b62b9c64b1890a7be8650):
liblightning::co::CoState::co_initializer()/<liblightning::co::CoState as CommonCoState>::resume(): if let Err(e), move into field, cross boundary, resume_unwind
liblightning::scheduler::SharedSchedState::prepare_coroutine(): move into field, cross boundary, return Result

libpulse-binding v2.26.0 (https://github.com/jnqnfe/pulse-binding-rust/tree/v2.26.0/pulse-binding):
libpulse_binding::context::event_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::ext_device_manager::read_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::ext_device_restore::ext_subscribe_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::ext_device_restore::read_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::ext_stream_restore::read_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::ext_subscribe_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::ext_test_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::context_index_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_card_info_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_client_info_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_sample_info_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_server_info_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_sink_info_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_sink_input_info_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_source_info_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_source_output_info_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::get_stat_info_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::mod_info_list_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::introspect::send_message_to_object_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::notify_cb_proxy_multi(): let _ (uncontrolled, unsound!)
libpulse_binding::context::notify_cb_proxy_single(): let _ (uncontrolled, unsound!)
libpulse_binding::context::scache::play_sample_success_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::subscribe::cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::context::success_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::mainloop::api::once_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::mainloop::events::deferred::event_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::mainloop::events::io::event_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::mainloop::events::timer::event_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::mainloop::signal::signal_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::operation::notify_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::stream::event_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::stream::notify_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::stream::request_cb_proxy(): let _ (uncontrolled, unsound!)
libpulse_binding::stream::success_cb_proxy(): let _ (uncontrolled, unsound!)

libtest v0.0.1 (https://github.com/rust-lang/libtest/tree/72798cdede83eead387f32875d3ea77c85e29276/libtest):
libtest::bench::benchmark(): match Err(_), return TestResult, implicit drop (controlled)
libtest::run_test::run_test_inner()/libtest::calc_result(): call indep. functions, match, maybe call functions with ref, return TestResult, implicit drop (controlled)

lightproc v0.3.5 (https://github.com/bastion-rs/bastion/tree/lightproc-v0.3.5/src/lightproc):
lightproc::lightproc::LightProc::recoverable()/<lightproc::recoverable_handle::RecoverableHandle as Future>::poll(): match Err(_), call indep. functions, return Option, implicit drop (uncontrolled!)

lignin v0.1.0 (https://github.com/Tamschi/lignin/tree/v0.1.0):
lignin::callback_registry::callbacks_on::invoke(): match Err(panic), call indep. function, resume_unwind

mendes v0.0.64 (https://github.com/djc/mendes/tree/8766d9f6f18df902281cb9ad6235cd8fe9399ba5/mendes):
<mendes::hyper::ConnectionService as Service<Request<Body>>>::call()/mendes::hyper::panic_response(), match Err(e), maybe call functions with ref, return Result`, implicit drop (uncontrolled!)

mini-v8 v0.3.0 (https://github.com/SkylerLipthay/mini-v8/tree/39a44d1d78c8293da7be83751da907f18a90eeb5) [tough to build]:
mini_v8::ffi::callback_wrapper(): match Err(err), call function with ref, abort
(contains another unsound function)

miniz_oxide_c_api v0.3.0 (https://github.com/Frommi/miniz_oxide/tree/cff503a88812b4c467d9859ca7603cc623815d6b):
miniz_oxide_c_api::mz_deflateInit2(): match Err(_), call indep. function, return c_int, implicit drop (controlled)
miniz_oxide_c_api::mz_inflateInit2(): match Err(_), call indep. function, return c_int, implicit drop (controlled)
miniz_oxide_c_api::oxidize!::$mz_func(): match Err(_), call indep. function, return c_int, implicit drop (controlled)
miniz_oxide_c_api::tdef::tdefl_init(): match Err(_), call indep. function, return tdefl_status, implicit drop (controlled)

mlua v0.8.0 (https://github.com/khvzak/mlua/tree/v0.8.0):
mlua::lua::callback_error_ext()/mlua::util::pop_error(): match Err(p), move into field, cross boundary, resume_unwind
mlua::util::callback_error()/mlua::util::pop_error(): match Err(p), move into field, cross boundary, resume_unwind

mocktopus_macros v0.7.11 (https://github.com/CodeSandwich/Mocktopus/tree/aa93669b731074e47bf47628af1f299752f69901/macros):
mocktopus_macros::header_builder::FnHeaderBuilder::build(): match Err({unwind}), call indep. functions, resume_unwind

mozjs v0.10.1 (https://github.com/servo/rust-mozjs/tree/v0.10.1):
mozjs::panic::wrap_panic()/mozjs::panic::maybe_resume_unwind(): match Err(error), call indep. functions, move into global, implicit drop of prior value (uncontrolled!)

neon v0.10.1 (https://github.com/neon-bindings/neon/tree/0.10.1):
neon::context::internal::try_catch_glue()/neon::context::internal::ContextInternal::try_catch_internal(): match Err(err), move into pointer, cross boundary, resume_unwind
neon::types::error::convert_panics(): match Err(panic), call functions with ref, call indep. functions, return Result, implicit drop (uncontrolled, unsound!)

neon-runtime v0.10.1 (https://github.com/neon-bindings/neon/tree/0.10.1/crates/neon-runtime):
neon_runtime::napi::async_work::call_execute()/neon_runtime::napi::async_work::call_complete(), move into field, cross boundary, resume_unwind
neon_runtime::napi::no_panic::FailureBoundary::catch_failure()/neon_runtime::napi::error::fatal_error(): if let Err(panic), call function with ref, napi::fatal_error, unreachable!()

ntest v0.8.0 (https://github.com/becheran/ntest/tree/v0.8.0/ntest):
ntest::assert_panics(): if not is_err, panic!(), else implicit drop (controlled)

openssl v0.10.41 (https://github.com/sfackler/rust-openssl/tree/openssl-v0.10.41/openssl):
openssl::util::bio::bread()/openssl::ssl::SslStream::check_panic(): match Err(err), move into field, cross boundary, resume_unwind
openssl::util::bio::bwrite()/openssl::ssl::SslStream::check_panic(): match Err(err), move into field, cross boundary, resume_unwind
openssl::util::bio::ctrl()/openssl::ssl::SslStream::check_panic(): match Err(err), move into field, cross boundary, resume_unwind
openssl::util::invoke_passwd_cb()/<openssl::util::CallbackState as Drop>::drop(): match Err(err), move into field, cross boundary, resume_unwind

openvpn-plugin v0.4.1 (https://github.com/mullvad/openvpn-plugin-rs/tree/v0.4.1):
openvpn_plugin::openvpn_plugin_close(): if let Err(e), call function with ref, implicit drop (uncontrolled, unsound!)
openvpn_plugin::openvpn_plugin_func(): match Err(e), call function with ref, return c_int, implicit drop (uncontrolled, unsound!)
openvpn_plugin::openvpn_plugin_open(): match Err(e), call function with ref, return c_int, implicit drop (uncontrolled, unsound!)

paste v1.0.7 (https://github.com/dtolnay/paste/tree/1.0.7):
paste::pasted_to_tokens(): match Err(_), return Result, implicit drop (controlled)

paste-impl v0.1.18 (https://github.com/dtolnay/paste/tree/0.1.18/impl):
paste_impl::paste_segments(): match Err(_), return Result, implicit drop (controlled)

proc-macro-error v1.0.4 (https://gitlab.com/CreepySkeleton/proc-macro-error/-/tree/v1.0.4):
proc_macro_error::entry_point(): match Err(boxed), attempt to downcast, resume_unwind

proc-macro2 v1.0.40 (https://github.com/dtolnay/proc-macro2/tree/1.0.40):
proc_macro2::detection::initialize(): is_ok, implicit drop (controlled)
proc_macro2::wrapper::proc_macro_parse(): unwrap_or_else(|_|), return Result, implicit drop (controlled)

proc-macro2-diagnostics v0.9.1 (https://github.com/SergioBenitez/proc-macro2-diagnostics/tree/8a739598692e195858249fb3ad89370dca893371):
proc_macro2_diagnostics::nightly_works(): is_ok, implicit drop (controlled)

procspawn v0.10.1 (https://github.com/mitsuhiko/procspawn/tree/0.10.1):
procspawn::core::run_func(): match Err(panic), call function with ref, return Result, implicit drop (uncontrolled, sound)

proptest v1.0.0 (https://github.com/AltSysrq/proptest/tree/1.0.0/proptest):
proptest::test_runner::runner::call_test(): match Err(what), attempt to downcast, unwrap_or_else(|_|), return String, implicit drop (uncontrolled!)

pyo3 v0.16.5 (https://github.com/PyO3/pyo3/tree/v0.16.5):
pyo3::callback::handle_panic()/pyo3::callback::panic_result_into_callback_output()/pyo3::panic::PanicException::from_panic_payload(): match Err(payload), call functions by ref, implicit drop (uncontrolled, unsound?)
pyo3::impl_::pyclass::generate_pyclass_getattro_slot!::__wrap()/pyo3::callback::panic_result_into_callback_output()/pyo3::panic::PanicException::from_panic_payload(): match Err(payload), call functions by ref, implicit drop (controlled?)
pyo3::impl_::pymodule::ModuleDef::module_init()/pyo3::callback::panic_result_into_callback_output()/pyo3::panic::PanicException::from_panic_payload(): match Err(payload), call functions by ref, implicit drop (controlled?)
pyo3::types::function::drop_closure(): if let Err(err), call function with ref, implicit drop (uncontrolled, unsound!); let _ (controlled)

pyo3-macros-backend v0.16.5 (https://github.com/PyO3/pyo3/tree/v0.16.5/pyo3-macros-backend):
pyo3_macros_backend::method::FnSpec::get_wrapper_function::#ident()/pyo3::callback::panic_result_into_callback_output()/pyo3::panic::PanicException::from_panic_payload(): match Err(payload), call functions by ref, implicit drop (controlled?)
pyo3_macros_backend::pymethod::impl_py_getter_def::__wrap()/pyo3::callback::panic_result_into_callback_output()/pyo3::panic::PanicException::from_panic_payload(): match Err(payload), call functions by ref, implicit drop (controlled?)
pyo3_macros_backend::pymethod::impl_py_setter_def::__wrap()/pyo3::callback::panic_result_into_callback_output()/pyo3::panic::PanicException::from_panic_payload(): match Err(payload), call functions by ref, implicit drop (uncontrolled, unsound!)
pyo3_macros_backend::pymethod::impl_traverse_slot::__wrap_()/pyo3::callback::abort_on_traverse_panic(): match Err(_payload), call indep. function, abort
pyo3_macros_backend::pymethod::SlotDef::generate_type_slot::__wrap()/pyo3::callback::panic_result_into_callback_output()/pyo3::panic::PanicException::from_panic_payload(): match Err(payload), call functions by ref, implicit drop (controlled?)

quick-js v0.4.1 (https://github.com/theduke/quickjs-rs/tree/quick-js-v0.4.1):
quick_js::bindings::ContextWrapper::exec_callback(): match Err(_e), return indep. Result, implicit drop (uncontrolled, unsound!)

quickcheck v1.0.3 (https://github.com/BurntSushi/quickcheck/tree/1.0.3):
quickcheck::tester::safe(): map_err(|any_err|), call functions with ref, return String, implicit drop (uncontrolled!)
quickcheck::tester::TestResult::must_fail(): is_err, return TestResult, implicit drop (uncontrolled!)

rayon-core v1.9.3 (https://github.com/rayon-rs/rayon/tree/rayon-core-v1.9.3/rayon-core):
<rayon_core::job::StackJob as Job>::execute()/rayon_core::job::JobResult::into_return_value(): match Err(x), move into field, cross boundary, resume_unwind
rayon_core::join::join_context()/rayon_core::join::join_recover_from_panic(): match Err(err), call indep. function, resume_unwind
rayon_core::scope::ScopeBase::execute_job_closure()/rayon_core::scope::ScopeBase::job_panicked()/rayon_core::scope::ScopeBase::maybe_propagate_panic(): match Err(err), move into field, cross boundary, resume_unwind (incidentally unsound?)
rayon_core::spawn::spawn_job()/rayon_core::registry::Registry::handle_panic(): match Err(err), move into function (abort on panic) OR abort

riker v0.4.2 (https://github.com/riker-rs/riker/tree/2fbd243ce477839c15d74e22fceed10ee4a97f3b):
riker::kernel::kernel(): let _ (uncontrolled!)
riker::kernel::start_actor(): map_err(|_|), return CreateError, implicit drop (uncontrolled!)

rlua v0.19.2 (https://github.com/amethyst/rlua/tree/v0.19.2):
rlua::util::callback_error()/rlua::util::pop_error(): match Err(p), move into field, cross boundary, resume_unwind

rouille v3.5.0 (https://github.com/tomaka/rouille/tree/v3.5.0):
rouille::log::log(): match Err(payload), call indep. function, resume_unwind
rouille::log::log_custom(): match Err(payload), call indep. function, resume_unwind rouille::Server::process(): match Err(_), return Response`, implicit drop (uncontrolled!)

rusb v0.9.1 (https://github.com/a1ien/rusb/tree/v0.9.1-rusb):
rusb::hotplug::hotplug_callback(): match Err(_), return c_int, implicit drop (uncontrolled, unsound!)

ruscii v0.3.2 (https://github.com/lemunozm/ruscii/tree/v0.3.2):
ruscii::app::App::run(): if let Err(_), call indep. functions, implicit drop (uncontrolled!)

rusqlite v0.27.0 (https://github.com/rusqlite/rusqlite/tree/v0.27.0):
rusqlite::Connection::busy_handler::busy_handler_callback(): if let Ok(true), else return c_int (uncontrolled, unsound!)
rusqlite::Connection::trace(): drop (uncontrolled, unsound!)
rusqlite::functions::call_boxed_final(): match Err(_), call function, return, implicit drop (uncontrolled, unsound!)
rusqlite::functions::call_boxed_inverse(): match Err(_), call function, return, implicit drop (uncontrolled, unsound!)
rusqlite::functions::call_boxed_step(): match Err(_), call function, return, implicit drop (uncontrolled, unsound!)
rusqlite::functions::call_boxed_value(): match Err(_), call function, return, implicit drop (uncontrolled, unsound!)
rusqlite::inner_connection::InnerConnection::authorizer::call_boxed_closure(): map_or_else(|_|), return c_int, implicit drop (uncontrolled, unsound!)
rusqlite::inner_connection::InnerConnection::collation_needed::collation_needed_callback(): is_err, return, implicit drop (uncontrolled, unsound!)
rusqlite::inner_connection::InnerConnection::commit_hook::call_boxed_closure(): drop (uncontrolled, unsound!)
rusqlite::inner_connection::InnerConnection::create_collation::call_boxed_closure(): match Err(_), return c_int, implicit drop (uncontrolled, unsound!)
rusqlite::inner_connection::InnerConnection::create_scalar_function::call_boxed_closure(): match Err(_), call function, return, implicit drop (uncontrolled, unsound!)
rusqlite::inner_connection::InnerConnection::progress_handler::call_boxed_closure(): if let Ok(true), else return c_int (uncontrolled, unsound!)
rusqlite::inner_connection::InnerConnection::rollback_hook::call_boxed_closure(): drop (uncontrolled, unsound!)
rusqlite::session::call_conflict(): if let Ok(action), else return c_int (uncontrolled, unsound!)
rusqlite::session::call_filter(): if let Ok(true), else return c_int (uncontrolled, unsound!)
rusqlite::session::Session::table_filter::call_boxed_closure(): if let Ok(true), else return c_int (uncontrolled, unsound!)
rusqlite::trace::config_log(): drop (uncontrolled, unsound!)
rusqlite::unlock_notify::unlock_notify_cb(): drop (impossible)

rustc-rayon v0.4.0 (https://github.com/rust-lang/rustc-rayon/tree/v0.4.0):
rustc_rayon::job::JobImpl::run_result()/rustc_rayon::job::JobImpl::into_result(): match Err(x), move into field, cross boundary, resume_unwind

rustc-rayon-core v0.4.1 (https://github.com/rust-lang/rustc-rayon/tree/e70e468c72fc6832f53ed3fbe88bd24d77f7ccb9/rayon-core):
<rustc_rayon_core::job::StackJob as Job>::execute()/rustc_rayon_core::job::JobResult::into_return_value(): match Err(x), move into field, cross boundary, resume_unwind
rustc_rayon_core::join::join_context()/rustc_rayon_core::join::join_recover_from_panic(): match Err(err), call indep. function, resume_unwind
rustc_rayon_core::scope::ScopeBase::execute_job_closure()/rustc_rayon_core::scope::ScopeBase::job_panicked()/rustc_rayon_core::scope::ScopeBase::maybe_propagate_panic(): match Err(err), move into field, cross boundary, resume_unwind (incidentally unsound?)
rustc_rayon_core::spawn::spawn_job()/rustc_rayon_core::registry::Registry::handle_panic(): match Err(err), move into function (abort on panic) OR abort
rustc_rayon_core::ThreadPoolBuilder::build_scoped(): call indep. function, match Err(err), resume_unwind
rustc_rayon_core::registry::main_loop()/rustc_rayon_core::registry::Registry::handle_panic(): create AbortIfPanic guard, match Err(err), move into trait function, forget guard

rustler v0.25.0 (https://github.com/rusterlium/rustler/tree/rustler-0.25.0/rustler):
rustler::thread::spawn(): match Err(err), call functions with ref, implicit drop (uncontrolled, unsound?)

rustler_codegen v0.25.0 (https://github.com/rusterlium/rustler/tree/rustler-0.25.0/rustler_codegen):
rustler_codegen::nif::transcoder_decorator::<#name as Nif>::RAW_FUNC::nif_func::wrapper()/rustler::codegen_runtime::handle_nif_result(): match Err(err), downcast, match Err(_), return NifReturned, implicit drop (uncontrolled, unsound!)

rustls-ffi v0.9.1 (https://github.com/rustls/rustls-ffi/tree/v0.9.1):
rustls_ffi::panic::ffi_panic_boundary!: match Err(_), return error, implicit drop (uncontrolled, unsound?)

rusty-fork v0.3.0 (https://github.com/AltSysrq/rusty-fork/tree/0.3.0):
rusty_fork::fork::fork_impl(): match Err(_), exit

rutie-serde v0.3.0 (https://github.com/deliveroo/rutie-serde/tree/0.3.0):
rutie_serde::panics::catch_and_raise(): match Err(_), call functions, call nonreturning function, unreachable!()

safe-proc-macro2 v1.0.36 (https://gitlab.com/leonhard-llc/safe-regex-rs/-/tree/safe-proc-macro2-v1.0.36/safe-proc-macro2):
safe_proc_macro2::detection::initialize(): is_ok, implicit drop (controlled)
safe_proc_macro2::wrapper::proc_macro_parse(): unwrap_or_else(|_|), return Result, implicit drop (controlled)

sc-executor v0.9.0 (https://github.com/paritytech/substrate/tree/v3.0.0/client/executor):
sc_executor::native_executor::with_externalities_safe(): map_err(|e|), call functions with ref, return Error, implicit drop (uncontrolled!)

sc-executor-wasmtime v0.9.0 (https://github.com/paritytech/substrate/tree/v3.0.0/client/executor/wasmtime):
sc_executor_wasmtime::imports::call_static()/sc_executor_wasmtime::imports::stringify_panic_payload(): match Err(err), attempt to downcast, implicit drop (uncontrolled!)

sc-service v0.9.0 (https://github.com/paritytech/substrate/tree/v3.0.0/client/service):
sc_service::task_manager::SpawnTaskHandle::spawn_inner(): match Err(payload), call indep. function, resume_unwind

scheduled-thread-pool v0.2.6 (https://github.com/sfackler/scheduled-thread-pool/tree/v0.2.6):
scheduled_thread_pool::Worker::run(): let _ (uncontrolled, sound)

security-framework v2.6.1 (https://github.com/kornelski/rust-security-framework/tree/v2.6.1/security-framework):
security_framework::secure_transport::read_func()/security_framework::secure_transport::SslStream::check_panic(): match Err(e), move into field, cross boundary, resume_unwind security_framework::secure_transport::write_func()/security_framework::secure_transport::SslStream::check_panic(): match Err(e), move into field, cross boundary, resume_unwind

sentry-core v0.27.0 (https://github.com/getsentry/sentry-rust/tree/0.27.0/sentry-core):
sentry_core::hub::Hub::run(): call indep. functions, match Err(err), resume_unwind

serial_test v0.8.0 (https://github.com/palfrey/serial_test/tree/v0.8.0/serial_test):
serial_test::parallel_code_lock::local_async_parallel_core(): call indep. function, if let Err(err), resume_unwind
serial_test::parallel_code_lock::local_async_parallel_core_with_return(): call indep. function, match Err(err), resume_unwind
serial_test::parallel_code_lock::local_parallel_core(): call indep. function, if let Err(err), resume_unwind
serial_test::parallel_code_lock::local_parallel_core_with_return(): call indep. function, match Err(err), resume_unwind
serial_test::parallel_file_lock::fs_async_parallel_core(): call indep. function, if let Err(err), resume_unwind
serial_test::parallel_file_lock::fs_async_parallel_core_with_return(): call indep. function, match Err(err), resume_unwind
serial_test::parallel_file_lock::fs_parallel_core(): call indep. function, if let Err(err), resume_unwind
serial_test::parallel_file_lock::fs_parallel_core_with_return(): call indep. function, match Err(err), resume_unwind

sled v0.34.7 (https://github.com/spacejam/sled/tree/v0.34.7):
sled::threadpool::spawn_new_thread(): set indep. global, if is_err, call function with ref, set indep. global, return, implicit drop (uncontrolled, sound)

smbc v0.1.0 (https://github.com/smbc-rs/smbc/tree/f12958560d6179b10eb0f91971ca0c13c8b01aa8):
smbc::smbc::SmbClient::auth_wrapper(): unwrap_or, return default values (uncontrolled, unsound!)

smol v1.2.5 (https://github.com/smol-rs/smol/tree/v1.2.5):
smol::spawn::spawn(): ok (uncontrolled!)

sp-state-machine (https://github.com/paritytech/substrate/tree/d14784fee8eddead26efc8617c512cc8775bfde5/primitives/state-machine):
sp_state_machine::testing::TestExternalities::execute_with_safe(): map_err(|e|), call function with ref, return String, implicit drop (controlled)

sp-tasks v3.0.0 (https://github.com/paritytech/substrate/tree/v3.0.0/primitives/tasks):
sp_tasks::inner::spawn(): match Err(panic), call function with ref, return, implicit drop (uncontrolled!)

ssi v0.4.0 (https://github.com/spruceid/ssi/tree/v0.4.0):
ssi::jws::verify_bytes_warnable(): map_err(|e|), return indep. Error, implicit drop (controlled)

stacker v0.1.14 (https://github.com/rust-lang/stacker/tree/stacker-0.1.14):
stacker::_grow(): err, cross boundary, call indep. function, if let Some(p), resume_unwind
stacker::fiber_proc()/stacker::_grow(): err, move into field, cross boundary, call indep. functions, if let Some(p), resume_unwind

static_init v1.0.2 (https://gitlab.com/okannen/static_init/-/tree/6f2b42eed0015f394998482f5b28eb18395f39d2):
static_init::generic_lazy::may_debug(): match Err(x), if is::<CyclicPanic>, panic!(), else resume_unwind

steamworks v0.9.0 (https://github.com/Noxime/steamworks-rs/tree/v0.9.0):
steamworks::networking_types::free_rust_message_buffer(): if let Err(e), call function with ref, implicit drop (impossible, incidentally unsound!)
steamworks::utils::c_warning_callback(): if let Err(err), call functions with ref, abort

take_mut v0.2.2 (https://github.com/Sgeo/take_mut/tree/v0.2.2):
take_mut::scoped::scope(): call indep. function, abort OR (match Err(p), resume_unwind)
take_mut::take(): unwrap_or_else(|_|), abort
take_mut::take_or_recover(): match Err(err), call indep. functions, resume_unwind; unwrap_or_else(|_|), abort

temp-env v0.2.0 (https://github.com/vmx/temp-env/tree/v0.2.0):
temp_env::with_vars(): match Err(err), call indep. functions, resume_unwind

test-context-macros v0.1.1 (https://github.com/markhildreth/test-context/tree/v0.1.1/macros):
test_context_macros::test_context(): call indep. function, match Err(err), resume_unwind

tester v0.9.0 (https://github.com/messense/rustc-test/tree/v0.9.0):
tester::bench::benchmark(): call indep. function, match Err(_), return TestResult, implicit drop (uncontrolled, sound)
tester::run_test_in_process(): call indep. functions, match Err(e), call function with ref, implicit drop (uncontrolled, sound)

thread-pool v0.1.1 (https://github.com/carllerche/thread-pool/tree/4f3599617fc14a7d83b4f936ceca99d7af98a6eb):
thread_pool::thread_pool::Worker::run(): let _ (uncontrolled!)

tokio v1.19.2 (https://github.com/tokio-rs/tokio/tree/tokio-1.19.2/tokio):
tokio::runtime::task::harness::cancel_task(): match Err(panic), move into field
tokio::runtime::task::harness::Harness::complete(): let _ (uncontrolled!)
tokio::runtime::task::harness::Harness::drop_join_handle_slow(): let _ (uncontrolled!)
tokio::runtime::task::harness::poll_future(): match Err(panic), move into Result, move into field; let _ (controlled?)
tokio::signal::reusable_box::ReusableBoxFuture::set_same_layout(): call indep. functions, match Err(payload), resume_unwind
tokio::sync::task::atomic_waker::AtomicWaker::do_register(): match Err(panic), move into Option, call functions with is_some, if let Some(panic), resume_unwind; let _ (uncontrolled!); let _ (uncontrolled!)
tokio::sync::watch::Sender::send_if_modified(): match Err(panicked), call indep. function, resume_unwind

tokio-threadpool v0.1.17 (https://github.com/tokio-rs/tokio/tree/tokio-threadpool-0.1.17/tokio-threadpool):
tokio_threadpool::task::Task::run(): match Err(_), call functions, return Run, implicit drop (uncontrolled!)

tokio-unix-ipc v0.2.0 (https://github.com/mitsuhiko/tokio-unix-ipc/tree/0.2.0):
tokio_unix_ipc::panic::catch_panic(): match Err(panic), call function with ref, return Result, implicit drop (uncontrolled!)

tower-http v0.3.4 (https://github.com/tower-rs/tower-http/tree/tower-http-0.3.4/tower-http):
<tower_http::catch_panic::CatchPanic as Service<Request<_>>>::call()/<tower_http::catch_panic::ResponseFuture as Future>::poll(): return Result

tracing-gstreamer v0.3.2 (https://github.com/standard-ai/tracing-gstreamer/tree/v0.3.2):
tracing_gstreamer::log::log_callback(): unwrap_or_else(|_e|), abort

trybuild v1.0.63 (https://github.com/dtolnay/trybuild/tree/1.0.63):
trybuild::diff::r#impl::Diff::compute(): ok, ? (controlled)

twinstar v0.4.0 (https://github.com/panicbit/twinstar/tree/v0.4.0):
twinstar::Server::serve_client(): unwrap_or_else(|_|), return Response, implicit drop (uncontrolled!)

two-rusty-forks v0.4.0 (https://github.com/inikulin/two-rusty-forks/tree/9346a869dab75ce6dad915b088231b4911ef1d99):
two_rusy_forks::fork::fork_impl(): match Err(_), exit

uppercut v0.4.0 (https://github.com/sergey-melnychuk/uppercut/tree/0.4):
uppercut::core::worker_loop(): if is_err, err, unwrap, move into user-supplied trait method, default implicit drop (uncontrolled!)

vapoursynth v0.3.0 (https://github.com/YaLTeR/vapoursynth-rs/tree/v0.3.0/vapoursynth):
vapoursynth::api::API::add_message_handler::c_callback(): if is_err, abort
vapoursynth::api::API::add_message_handler_trivial::c_callback(): if is_err, call indep. function, abort (incidentally unsound!)
vapoursynth::api::API::set_message_handler::c_callback(): if is_err, abort
vapoursynth::api::API::set_message_handler_trivial::c_callback(): if is_err, call indep. function, abort (incidentally unsound!)
vapoursynth::node::Node::get_frame_async::c_callback(): if is_err, abort
vapoursynth::plugins::ffi::create(): if is_err, abort
vapoursynth::plugins::ffi::export_vapoursynth_plugin!::VapourSynthPluginInit(): if is_err, abort
vapoursynth::plugins::ffi::free(): if is_err, abort
vapoursynth::plugins::ffi::get_frame(): match Err(_), abort
vapoursynth::plugins::ffi::init(): if is_err, call indep. functions, implicit drop (uncontrolled, unsound!); if is_err, abort

wasmer v2.3.0 (https://github.com/wasmerio/wasmer/tree/2.3.0/lib/api):
<wasmer_vm::VMDynamicFunctionContext as VMDynamicFunctionCall<_>>::func_wrapper()/wasmer_vm::trap::traphandlers::resume_panic()/wasmer_fm::trap::traphandlers::unwind_with()/wasmer_fm::trap::traphandlers::UnwindReason::to_trap(): match Err(panic), move into UnwindReason, cross boundary, resume_unwind
wasmer::js::externals::function::inner::impl_host_function!::HostFunction::function_body_pointer::func_wrapper(): match _, unimplemented!()
wasmer::sys::externals::function::inner::impl_host_function!::HostFunction::function_body_ptr()/wasmer_vm::trap::traphandlers::resume_panic()/wasmer_fm::trap::traphandlers::unwind_with()/wasmer_fm::trap::traphandlers::UnwindReason::to_trap(): match Err(panic), move into UnwindReason, cross boundary, resume_unwind
wasmer::sys::native::impl_native_traits!::NativeFunc::call(): map_err(|e|), call function with ref, return RuntimeError, implicit drop (uncontrolled, unsound?)

wasmer-runtime-core v0.17.1 (https://github.com/wasmerio/wasmer/tree/0.17.1/lib/runtime-core):
wasmer_runtime_core::typed_func::DynamicFunc::new::do_enter_host_polymorphic()/wasmer_runtime_core::tiering::run_tiering(): match Err(e), move into RuntimeError, cross boundary, if let Err(e), match _, return Result, implicit drop (uncontrolled!)
wasmer_runtime_core::typed_func::impl_traits!::HostFunction::to_raw::wrap()/wasmer_runtime_core::tiering::run_tiering(): match Err(err), move into RuntimeError, cross boundary, if let Err(e), match _, return Result, implicit drop (uncontrolled!)

wayland-client v0.29.4 (https://github.com/Smithay/wayland-rs/tree/39a24803925a6831959229e85faffdab39552a61/wayland-client):
wayland_client::native_lib::proxy::proxy_dispatcher(): match Err(_), call function, libc::abort

webview2 v0.1.4 (https://github.com/sopium/webview2/tree/v0.1.4):
webview2::callback!::<Impl as $name>::invoke(): match Err(_), call indep. function, abort (incidentally unsound!)

winit v0.26.1 (https://github.com/rust-windowing/winit/tree/v0.26.1):
winit::platform_impl::macos::event_loop::stop_app_on_panic()/winit::platform_impl::macos::event_loop::EventLoop::run_return(): match Err(e), move into Rc, call indep. functions, cross boundary, if let Some(panic), call indep. function, resume_unwind
winit::platform_impl::windows::event_loop::runner::EventLoopRunner::catch_unwind(): on condition call indep. function, return Option, implicit drop (uncontrolled, unsound!)

with_locals-proc_macros v0.3.0 (https://github.com/danielhenrymantilla/with_locals.rs/tree/v0.3.0/src/proc_macros):
with_locals_proc_macros::handle_let_bindings::handle_let_bindings(): if let Err(panic), (return Result, implicit drop) OR resume_unwind (controlled)
with_locals_proc_macros::wrap_statements_inside_closure_body::wrap_statements_inside_closure_body(): if let Err(panic), (return Result, implicit drop) OR resume_unwind (controlled)

wstp v0.2.2 (https://github.com/WolframResearch/wstp-rs/tree/v0.2.2):
wstp::wait::link_wait_callback_trampoline(): match Err(_), return i32, implicit drop (uncontrolled, unsound!)


TL;DR: catch_unwind() is a horrible interface for preventing unwinding. It works well when paired with resume_unwind() or when followed by abort(), but nearly every crate that handles it otherwise ends up leaking panics from dropping the payload. Something like drop_err_or_{forget, abort}() would be a safe and reasonable behavior (IMHO), but no currently widespread crate implements that logic.

Regarding UnwindResult, crates often move a value or access a mutable reference depending on whether a panic occurred. inspect_err() is insufficient to handle this, so into_result() would probably be more commonly used than I initially expected. Also, a very frequent pattern is to ignore the result and just to extract the payload for later. Therefore, it would likely need an err() method, with the same footgun warnings as into_result():

impl<T> UnwindResult<T> {
    // If there is a payload, return it; otherwise, return `None`. Be careful
    // when dropping it.
    pub fn err(self) -> Option<Box<dyn Any + Send>>;
}
9 Likes