Is this unsound: extern "system" fn window_proc without catching unwinds?

Continuing the discussion from Passing callbacks to C: panic!:

Does this also hold in this case:

pub(crate) unsafe extern "system" fn window_proc(
    hwnd: HWND,
    msg: u32,
    w_param: WPARAM,
    l_param: LPARAM,
) -> LRESULT {
    /* … */

/* … */

    let wnd = WNDCLASSW {
        /* … */
        lpfnWndProc: Some(window_proc),
        /* … */
    if winuser::RegisterClassW(&wnd) == 0 {
        /* … */

See source of tray-item crate. Am I right that the implementation of tray-item is unsound in that matter?

Unwinding out of extern fn is an interesting edge case; it's currently unsound, but sometime in the future it will be changed to a guaranteed sound abort.

However, on a technicality, note that it's only really a "soundness" issue if you're calling downstream code that you don't control. If you're only calling upstream code which is "known" to not panic, it's "merely" a resiliency issue that a bug causing a panic will lead to potential UB.

In actuality, the current behavior is that Rust panics use the same mechanism as C++ exceptions, so the behavior will be the same as if C++ unwinds into the OS here. I think this is a consistent crash.

That's also what I understood from the other thread, but I wasn't sure if this also applies to a function that's invoked by Windows somehow (and which doesn't have any point to "unwind to", I guess?). Or maybe it does ultimately get invoked by Rust. I'm not sure if I correctly understand the mechanism here.

Anyway, I understand you that panicking here is unsound but should be changed in future to be sound.

Yes, I understand. In the given example, we have:

WININFO_STASH.with(|stash| {
    let stash = stash.borrow();
    let stash = stash.as_ref();
    if let Some(stash) = stash {
        let menu_id = winuser::GetMenuItemID(, w_param as i32) as i32;
        if menu_id != -1 {
            stash.tx.send(WindowsTrayEvent(menu_id as u32)).ok();

If I take it right, then stash.tx is a std::sync::mspc::Sender, which does potentially allocate when using the send method and thus can panic. So do I understand right that tray-item is unsound here (with current Rust)?

From the other thread (which was related to extern "C" and unrolling back to Rust code):

What I don't really understand is who or what will call the window_proc function, i.e. will it be from some Rust context and unwind "through" C/C++ frames back to Rust? But maybe it doesn't matter as it seems to be UB anyway for now.

What do you mean with "consistent crash"? A "clean" segmentation fault or abort?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.