Skip to content

fix(native): ignore terminal calls after Close - #805

Merged
omartelo merged 1 commit into
mainfrom
fix/native-terminal-use-after-close
Oct 17, 2026
Merged

omartelo merged 1 commit into
mainfrom
fix/native-terminal-use-after-close

Conversation

@omartelo

Copy link
Copy Markdown
Collaborator

Follow-up to #780, from the concern raised in #777: a light/dark switch during a reconnect could paint terminals that had just been freed.

Cause

terminals.reattach runs on the reconnect goroutine and calls Close on every terminal. Since #780, setColors snapshots the views under ts.mu and paints them after releasing it, so a SetColors can land on a terminal reattach freed a moment before. A frame holding the shown terminal across the swap (show then layout) had the same exposure, as did onOutput's Write. gioterm.Terminal documents its methods as safe from several goroutines, but Close left v.t dangling and the next call ran on freed libghostty memory.

Fix

  • Close marks the Terminal closed. Every method after it does nothing: it returns ErrClosed where it returns an error and its zero value otherwise. A second Close is a no-op.
  • A layout error ends the event loop, so window.content treats ErrClosed as a frame to redraw: the next frame's show picks up the reattached terminal.

Tests

  • TestClosedTerminalIgnoresCalls calls every method on a closed terminal. Before the fix, SetColors ran on the freed terminal and returned nil. After it, the test passes with -race -count=50.
  • native/: gofmt -l ., go vet ./..., go test ./... clean.
  • No test drives the ErrClosed branch in window.content. Not checked in a real window.

Refs #777

A reconnect frees every terminal from its own goroutine while the frame
goroutine may still paint one: setColors since #780 snapshots the views
and paints them after releasing the lock, and a frame can hold the shown
terminal across the swap. Those calls ran on freed libghostty memory.

Close now marks the Terminal closed and every method after it does
nothing, returning ErrClosed where it returns an error. The frame that
meets ErrClosed is redrawn instead of ending the event loop.

Refs #777
@omartelo
omartelo merged commit 49ae8ba into main Oct 17, 2026
10 checks passed
@omartelo
omartelo deleted the fix/native-terminal-use-after-close branch October 17, 2026 18:07
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