Skip to content

GhosttyHost: guard the remaining native callbacks against exceptions crossing the boundary#444

Merged
deblasis merged 2 commits into
windowsfrom
fix/native-callback-boundary-guards
May 30, 2026
Merged

GhosttyHost: guard the remaining native callbacks against exceptions crossing the boundary#444
deblasis merged 2 commits into
windowsfrom
fix/native-callback-boundary-guards

Conversation

@deblasis
Copy link
Copy Markdown
Owner

Follow-up to the OnAction native-boundary guard. The other five callbacks libghostty invokes directly on its own thread (the delegates registered as function pointers in the ctor) had the same exposure: a managed exception unwinding across the ABI back into Zig is undefined behavior.

The synchronous bodies that can throw:

  • OnCloseSurface calls GCHandle.FromIntPtr, which throws on a bad/freed handle.
  • OnConfirmReadClipboard decodes a raw C string via Marshal.PtrToStringUTF8.
  • OnWriteClipboard walks native memory via the content marshaller.
  • OnReadClipboard and OnWakeup are lower risk but sit on the same boundary.

Each synchronous body is now wrapped in a try/catch that logs and absorbs (OnReadClipboard returns 0 = "not handled"). Guarding at the callback level also covers the clipboard bridge's synchronous decode, since that runs inline before the work is dispatched. The bridge's deferred dispatcher lambdas already self-guard on the UI thread, so ClipboardBridge.cs is untouched.

OnWakeup's body only hops to the dispatcher and is unlikely to throw, but it is guarded too so every registered callback has the same invariant rather than leaving one boundary uncovered.

deblasis added 2 commits May 30, 2026 16:40
…crossing the boundary

OnAction was hardened against managed exceptions unwinding across the
unmanaged ABI into Zig, but the other callbacks libghostty invokes directly
on its own thread had the same exposure:

- OnCloseSurface calls GCHandle.FromIntPtr, which throws on a bad handle.
- OnConfirmReadClipboard decodes a raw C string (Marshal.PtrToStringUTF8).
- OnWriteClipboard walks native memory via the content marshaller.
- OnReadClipboard and OnWakeup share the same boundary.

Wrap each synchronous body in a try/catch that logs and absorbs the
exception (OnReadClipboard returns 0 = "not handled"). The clipboard
bridge's deferred dispatcher work already self-guards on the UI thread, so
only the native-thread portion needed protecting here.
…afely

Review follow-up. Document that returning 0 ("not handled") cannot strand a
clipboard request, since HandleRead only throws on its synchronous prefix
before it takes on the completion obligation.
@deblasis deblasis marked this pull request as ready for review May 30, 2026 14:50
@deblasis deblasis merged commit 56b792e into windows May 30, 2026
98 checks passed
@deblasis deblasis deleted the fix/native-callback-boundary-guards branch May 30, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant