Skip to content

Avoiding races between hwnd use and window destruction #1

@raphlinus

Description

@raphlinus

In the Reddit discussion on this crate, the issue of avoiding use-after-free of a HWND came up. Currently, the crate doesn't do anything to try to prevent this - it gives you a HWND, and what you do with that afterwards is up to you.

There are several strategies for avoiding such races, each with advantages and disadvantages.

  • Do nothing. Windows has protection against stale hwnd we may simply define our interpretation of the soundness criterion on Windows such that calling a function (such as PostMessage) on a HWND that has recently been destroyed is not unsafe.

  • Defer window destruction. This is what pcwalton suggested in the thread. The idea is that actual destruction of the window wouldn't happen until after all references to the hwnd have been dropped. There are some tricky aspects, including the fact that DestroyWindow would have to be called from a window procedure handler because the drop of the last hwnd reference might happen in another thread, and that any naked use of DestroyWindow (including as the default action of WM_CLOSE) would be unsafe.

  • Use a weak reference to the HWND, with the expectation that the only long-lived strong reference would be dropped on window destruction. There could technically be a race if PostMessage were called with a HWND retrieved from the weak reference while the window is being destroyed, but the duration of the race would be very short, and perhaps we can count on the use-after-free protection to be completely effective in such cases.

  • Use Arc<Mutex<HWND>> to wrap the window handle, such that it's zeroed out in the WM_NCDESTROY handler. All uses of the HWND other than inside the window procedure (the main intended use case being cross-window or cross-thread PostWindow) would need to be done while holding the lock. The main risk here is deadlock; in particular, calling DestroyWindow would be likely to trigger deadlock. But this might be a reasonable restriction to put on clients, as most of the time you want to defer DestroyWindow anyway to reduce complications from reentrancy.

All of these can be done on top of the mechanisms provided by this crate, though some would benefit from having special lifetime-related logic, for example in the WM_NCDESTROY handler. It's not clear to me yet whether there's a "best" solution, and whether it should be provided by this crate. Discussion is welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions