Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Sources/GhosttyTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,12 @@ class GhosttyApp {
prefix: "cmux-shell-integration-override",
logLabel: "shell integration override (fallback)"
)
loadInlineGhosttyConfig(
"copy-on-select = clipboard",
into: fallbackConfig,
prefix: "cmux-copy-on-select",
logLabel: "copy-on-select override (fallback)"
)
usesHostLayerBackground = true
ghostty_config_finalize(fallbackConfig)
updateDefaultBackground(from: fallbackConfig, source: "initialize.fallbackConfig")
Expand Down Expand Up @@ -1887,6 +1893,17 @@ class GhosttyApp {
ghostty_config_load_recursive_files(config)
loadCmuxAppSupportGhosttyConfigIfNeeded(config)
loadCJKFontFallbackIfNeeded(config)
// Ghostty's default copy-on-select = true writes to a private
// named pasteboard (com.mitchellh.ghostty.selection) that is not
// the system clipboard. Override to "clipboard" so that selecting
// text copies to NSPasteboard.general and Cmd+V works as macOS
// users expect.
loadInlineGhosttyConfig(
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
"copy-on-select = clipboard",
into: config,
prefix: "cmux-copy-on-select",
logLabel: "copy-on-select override"
)
Comment on lines +1901 to +1906

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Override silently ignores user's copy-on-select setting

Because loadInlineGhosttyConfig is called after loadCmuxAppSupportGhosttyConfigIfNeeded (and ghostty_config_load_recursive_files), this inline config wins over any user-set value. A user who explicitly sets copy-on-select = false in their Ghostty config will have the preference silently reinstated to clipboard with no cmux-level knob to opt back out.

This is the same architectural pattern used for macos-background-from-layer (a cmux-internal flag), but copy-on-select is a standard, documented Ghostty option users may intentionally set. Worth considering whether a cmux settings key should gate this override, or at minimum adding a note in release notes / docs that the value is managed by cmux.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. A few notes on why this is an intentional forced override for now:

  1. Ghostty's default copy-on-select = true writes to a private named pasteboard (com.mitchellh.ghostty.selection), not the system clipboard. This means Cmd+V doesn't paste selections — only middle-click within Ghostty works. That's surprising behavior on macOS where users expect selected text to be available via Cmd+V.

  2. The UAF bug this PR fixes was in the copy-on-select = true code path. Until the ghostty fork fix lands, the default value is actively broken (intermittently copies only the first character).

  3. This mirrors the existing pattern for macos-background-from-layer — cmux overrides it because the Ghostty default doesn't match cmux's rendering model. Similarly, copy-on-select = clipboard is the only value that gives cmux users the expected macOS clipboard behavior.

Adding a cmux settings key to let users opt out (back to false or true) is reasonable as a follow-up if users request it. For now, shipping with clipboard as the forced default is the safest path.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ghostty's default copy-on-select = true writes to a private named pasteboard (com.mitchellh.ghostty.selection), not the system clipboard. Cmd+V doesn't paste selections — only middle-click within Ghostty works. That's surprising on macOS where people expect selected text in the system clipboard.

The UAF this PR fixes was also in this code path, so before the fix the default was actively broken too.

Same pattern as the macos-background-from-layer override — Ghostty's default doesn't match what cmux needs. Adding a cmux settings key to let users opt out is reasonable as a follow-up if anyone asks for it, but clipboard is the right default to ship with.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
let useHostLayerBackground = !hasConfiguredBackgroundImage(config)
usesHostLayerBackground = useHostLayerBackground
if !useHostLayerBackground {
Expand Down
12 changes: 12 additions & 0 deletions docs/ghostty-fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ When we change the fork, update this document and the parent submodule SHA.
Fork main has advanced beyond the March 30, 2026 rebase onto upstream `main`
at `3509ccf78` (`v1.3.1-457-g3509ccf78`).
Current cmux pinned fork head: `3b684a085` (`tip-1717-g3b684a085`).
Branch `fix-copy-on-select-uaf` (`8eaee4d95`) carries the section 9 fix below (manaflow-ai/ghostty#36).

### 1) macOS display link restart on display changes

Expand Down Expand Up @@ -123,6 +124,17 @@ tend to conflict together during rebases.
Fork main now carries the section 8 APC handling fix plus later upstream merges;
the current cmux pin is the head listed above.

### 9) Fix use-after-free in copy-on-select

- Commit: `8eaee4d95` (Fix use-after-free in copy-on-select)
- Branch: `fix-copy-on-select-uaf` (manaflow-ai/ghostty#36, pending merge)
- Files:
- `src/Surface.zig`
- Summary:
- Fixes a use-after-free in `setSelection()`. The old code called `Screen.select()` which freed tracked pins via `old.deinit()`, then compared the new selection against the freed `prev` pointer via `eql()`. Since freed memory usually retains old values, `eql()` returned true and `copySelectionToClipboards` was silently skipped — producing the one-character-paste symptom (cmux #2664).
- The fix evaluates `eql()` BEFORE `Screen.select()` frees the old pins.
- Additionally, copies the final selection to the clipboard directly on mouse-up (left-button release) to guarantee the clipboard is always up-to-date at the end of a drag selection.

## Upstreamed fork changes

### cursor-click-to-move respects OSC 133 click-to-move
Expand Down
2 changes: 1 addition & 1 deletion ghostty
Submodule ghostty updated 1 files
+33 −11 src/Surface.zig