Skip to content

fix(macos): replace raw MenuChild pointer with Rc#361

Open
divanshu-go wants to merge 1 commit into
tauri-apps:devfrom
divanshu-go:fix/macos-menu-item-use-after-free
Open

fix(macos): replace raw MenuChild pointer with Rc#361
divanshu-go wants to merge 1 commit into
tauri-apps:devfrom
divanshu-go:fix/macos-menu-item-use-after-free

Conversation

@divanshu-go

Copy link
Copy Markdown

The MudaMenuItem ObjC class stored a raw Cell<*const MenuChild> ivar pointing into the Rust heap. If the owning Menu was dropped or replaced while the native NSMenuItem was still visible (e.g. Screenpipe rebuilding the tray menu while it was open), clicking any stale item would dereference freed memory — manifesting as a panic in String::as_str or slice::from_raw_parts inside fire_menu_item_click.

Replace the ivar with RefCell<Option<Rc<RefCell<MenuChild>>>>. Each create_ns_item_for_* function now receives a cloned Rc of the owning MenuChild and stores it on the NSMenuItem. AppKit retains the NSMenuItem, so the Rc keeps the MenuChild alive for exactly as long as the native item exists — no lifetime mismatch possible.

Fixes: #328

…ee crash

The MudaMenuItem ObjC class stored a raw `Cell<*const MenuChild>` ivar
pointing into the Rust heap. If the owning `Menu` was dropped or replaced
while the native NSMenuItem was still visible (e.g. Screenpipe rebuilding
the tray menu while it was open), clicking any stale item would dereference
freed memory — manifesting as a panic in `String::as_str` or
`slice::from_raw_parts` inside `fire_menu_item_click`.

Replace the ivar with `RefCell<Option<Rc<RefCell<MenuChild>>>>`. Each
`create_ns_item_for_*` function now receives a cloned `Rc` of the owning
`MenuChild` and stores it on the NSMenuItem. AppKit retains the NSMenuItem,
so the `Rc` keeps the `MenuChild` alive for exactly as long as the native
item exists — no lifetime mismatch possible.

Fixes: tauri-apps#328
divanshu-go added a commit to divanshu-go/screenpipe that referenced this pull request Jun 4, 2026
muda's macOS backend stored a raw `Cell<*const MenuChild>` ivar on every
NSMenuItem. When Screenpipe rebuilt the tray menu while it was open,
the old MenuChild items were freed but AppKit kept the NSMenuItems alive.
A user click on the stale menu would dereference freed memory inside
`fire_menu_item_click` — an `extern "C"` nounwind callback — making
`catch_unwind` useless and aborting the process immediately.

Symptom:
  PANIC at alloc/src/vec/mod.rs:1599 — unsafe precondition violated:
  slice::from_raw_parts requires the pointer to be aligned and non-null

Fix (three layers):

1. Patch muda at source via [patch.crates-io]:
   divanshu-go/muda@fix/macos-menu-item-use-after-free replaces
   Cell<*const MenuChild> with RefCell<Option<Rc<RefCell<MenuChild>>>>.
   The Rc clone stored on NSMenuItem keeps MenuChild alive for exactly
   as long as AppKit retains the native item.
   Upstream PR: tauri-apps/muda#361
   Root cause:  tauri-apps/muda#328

2. Pending-menu pattern in tray.rs (macOS only):
   The background poller now queues (MenuState, TrayMenuData) into
   PENDING_TRAY_MENU instead of calling set_menu directly. A new
   on_tray_icon_event handler fires on MouseButtonState::Down — before
   AppKit opens the native menu — and installs the fresh menu then.
   Windows/Linux keep the existing immediate-update path.

3. Atomic icon update in safe_icon.rs:
   Both icon helpers now use set_icon_with_as_template(...) to set the
   image and template flag in one atomic call, closing a brief window
   where dark-mode tearing could occur.

Also bumps Tauri to 2.11.2 (Rust) / 2.11.0 (npm) and aligns
tauri-build, tauri-utils, @tauri-apps/cli to the same minor to resolve
the version-mismatch build warning.
louis030195 pushed a commit to screenpipe/screenpipe that referenced this pull request Jun 4, 2026
* fix(tray): eliminate macOS use-after-free crash on menu click

muda's macOS backend stored a raw `Cell<*const MenuChild>` ivar on every
NSMenuItem. When Screenpipe rebuilt the tray menu while it was open,
the old MenuChild items were freed but AppKit kept the NSMenuItems alive.
A user click on the stale menu would dereference freed memory inside
`fire_menu_item_click` — an `extern "C"` nounwind callback — making
`catch_unwind` useless and aborting the process immediately.

Symptom:
  PANIC at alloc/src/vec/mod.rs:1599 — unsafe precondition violated:
  slice::from_raw_parts requires the pointer to be aligned and non-null

Fix (three layers):

1. Patch muda at source via [patch.crates-io]:
   divanshu-go/muda@fix/macos-menu-item-use-after-free replaces
   Cell<*const MenuChild> with RefCell<Option<Rc<RefCell<MenuChild>>>>.
   The Rc clone stored on NSMenuItem keeps MenuChild alive for exactly
   as long as AppKit retains the native item.
   Upstream PR: tauri-apps/muda#361
   Root cause:  tauri-apps/muda#328

2. Pending-menu pattern in tray.rs (macOS only):
   The background poller now queues (MenuState, TrayMenuData) into
   PENDING_TRAY_MENU instead of calling set_menu directly. A new
   on_tray_icon_event handler fires on MouseButtonState::Down — before
   AppKit opens the native menu — and installs the fresh menu then.
   Windows/Linux keep the existing immediate-update path.

3. Atomic icon update in safe_icon.rs:
   Both icon helpers now use set_icon_with_as_template(...) to set the
   image and template flag in one atomic call, closing a brief window
   where dark-mode tearing could occur.

Also bumps Tauri to 2.11.2 (Rust) / 2.11.0 (npm) and aligns
tauri-build, tauri-utils, @tauri-apps/cli to the same minor to resolve
the version-mismatch build warning.

* build(tray): pin muda fork by commit instead of branch

The muda patch tracked a mutable branch, so a fresh `cargo update -p muda`
or from-scratch lock regen would silently follow the fork's branch HEAD.
Pin the exact commit (9dc5de5, unchanged) for reproducible builds, matching
the existing `tauri-helper` rev-pin convention. Resolved graph is identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Louis Beaumont <louis@screenpi.pe>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
divanshu-go added a commit to divanshu-go/screenpipe that referenced this pull request Jun 4, 2026
…pipe#3813)

* fix(tray): eliminate macOS use-after-free crash on menu click

muda's macOS backend stored a raw `Cell<*const MenuChild>` ivar on every
NSMenuItem. When Screenpipe rebuilt the tray menu while it was open,
the old MenuChild items were freed but AppKit kept the NSMenuItems alive.
A user click on the stale menu would dereference freed memory inside
`fire_menu_item_click` — an `extern "C"` nounwind callback — making
`catch_unwind` useless and aborting the process immediately.

Symptom:
  PANIC at alloc/src/vec/mod.rs:1599 — unsafe precondition violated:
  slice::from_raw_parts requires the pointer to be aligned and non-null

Fix (three layers):

1. Patch muda at source via [patch.crates-io]:
   divanshu-go/muda@fix/macos-menu-item-use-after-free replaces
   Cell<*const MenuChild> with RefCell<Option<Rc<RefCell<MenuChild>>>>.
   The Rc clone stored on NSMenuItem keeps MenuChild alive for exactly
   as long as AppKit retains the native item.
   Upstream PR: tauri-apps/muda#361
   Root cause:  tauri-apps/muda#328

2. Pending-menu pattern in tray.rs (macOS only):
   The background poller now queues (MenuState, TrayMenuData) into
   PENDING_TRAY_MENU instead of calling set_menu directly. A new
   on_tray_icon_event handler fires on MouseButtonState::Down — before
   AppKit opens the native menu — and installs the fresh menu then.
   Windows/Linux keep the existing immediate-update path.

3. Atomic icon update in safe_icon.rs:
   Both icon helpers now use set_icon_with_as_template(...) to set the
   image and template flag in one atomic call, closing a brief window
   where dark-mode tearing could occur.

Also bumps Tauri to 2.11.2 (Rust) / 2.11.0 (npm) and aligns
tauri-build, tauri-utils, @tauri-apps/cli to the same minor to resolve
the version-mismatch build warning.

* build(tray): pin muda fork by commit instead of branch

The muda patch tracked a mutable branch, so a fresh `cargo update -p muda`
or from-scratch lock regen would silently follow the fork's branch HEAD.
Pin the exact commit (9dc5de5, unchanged) for reproducible builds, matching
the existing `tauri-helper` rev-pin convention. Resolved graph is identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Louis Beaumont <louis@screenpi.pe>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

macOS: use-after-free crash when calling set_menu while menu is displayed

1 participant