850d0926c7 fix(audio): close plugin editor windows before freeing their processors (pause UAF #56) (#57)
* fix(audio): close in-process plugin editor windows before freeing their processors (#56)

A `PluginEditorWindow` owns an `AudioProcessorEditor` bound to its slot's
processor, but nothing tore those windows down when the chain was freed. On
pause the renderer clears/reloads the chain (`clearChain` / `loadPreset`), which
destroys every slot processor — leaving any open editor pointing at freed
memory. Its next timer/paint callback then jumps through a dangling pointer:
the reported ACCESS_VIOLATION / DEP-execute at an unmapped address, seconds
after pausing (thread stack thick with `RB Final Leveler.vst3` editor-window
frames calling back into slopsmith_audio.node).

Fix: destroy the in-process editor windows BEFORE the processors they reference,
in all three teardown paths:
- ClearChain (JS thread) — close editors, then clear().
- LoadPresetWorker::Execute (libuv worker) — close editors, then clear() before
  rebuilding the chain.
- doShutdown — destroy editors first inside the existing message-thread lambda,
  before engine.reset().

editorWindows holds JUCE GUI objects, so teardown must happen on the message
thread. `closeAllPluginEditorWindows()` marshals via `dispatchOnMessageThread`
(post-and-wait) so the caller blocks until every editor is gone — guaranteeing
editors die before their processors. Callers already on the message thread
(doShutdown) use the inline `destroyAllPluginEditorWindowsOnMessageThread()` to
avoid a post-and-wait-on-self deadlock. On Linux/Windows the JUCE message thread
is a dedicated std::thread, so ClearChain (Node) and the worker never deadlock;
on macOS dispatch runs inline and in-process editors don't exist (sandboxed).

Native addon builds clean (Release).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* review: fix macOS worker-thread editor teardown; assert precondition; report post/timeout

Codex [P2]: closeAllPluginEditorWindows() delegated to dispatchOnMessageThread(),
which runs inline under JUCE_MAC — so LoadPresetWorker::Execute() (libuv worker)
could destroy JUCE DocumentWindow/AudioProcessorEditor objects off the message
thread on macOS. Now branch on the caller's actual thread: run inline only when
already on the message thread (else deadlock), otherwise post via
MessageManager::callAsync (drained by the JUCE thread on Linux/Windows and the
Node-main libuv timer on macOS) and wait. Correct on all platforms.

Copilot: report a refused post / 15s wait timeout via stderr instead of silently
assuming teardown completed (the previous "guarantee" wording overstated it);
add JUCE_ASSERT_MESSAGE_THREAD to the inline variant as a debug tripwire.

Native addon builds clean (Release).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* review(codex): don't callAsync+wait on an unpumped macOS MessageManager

Codex round 2 [P2]: my round-1 fix routed the off-message-thread teardown
through MessageManager::callAsync + WaitableEvent::wait on ALL platforms. On
macOS there is no separate message-thread pump (startJuceMessageThread's
JUCE_MAC branch only creates the manager; there is no dispatch loop), so a
callAsync+wait from LoadPresetWorker's libuv worker would stall the full 15s
timeout and then proceed with the editor still alive — the very UAF this targets.

Platform-split the off-thread path, matching loadVstSandboxAware()'s existing
JUCE_MAC handling:
- Already on the message thread → inline (doShutdown; ClearChain on macOS).
- Linux/Windows off-thread → post to the dedicated JUCE message thread + wait
  (with refused-post / timeout reporting).
- macOS off-thread → clear inline (the pre-existing macOS worker-thread
  limitation). editorWindows is empty on macOS in practice (in-process editors
  route to the sandbox child), and the editor/processor UAF this targets is
  Windows-specific, so no message-thread hop is needed there.

Native addon builds clean (Release).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* review(codex): tear down editors from LoadPreset (main thread), not the worker

Codex flagged a genuine dilemma in the previous approach: closing editor windows
from LoadPresetWorker::Execute (a libuv worker) is unsafe either way on macOS —
callAsync+wait stalls (no message-thread pump; the "libuv timer" the comment
promises was never implemented) AND clearing inline destroys JUCE GUI objects
off the message thread.

Resolve it by not tearing down from the worker at all: LoadPreset() (the N-API
entry, on the Node/main thread) now closes editors before queuing the
AsyncWorker. That is safe on every platform — macOS: main thread IS the message
thread (inline); Linux/Windows: post to the dedicated JUCE message thread and
wait — and still guarantees editors die before Execute() frees the chain's
processors. closeAllPluginEditorWindows() is consequently never called off a
worker thread, so its macOS special-case is gone and it reduces to the uniform
on-message-thread / post-and-wait form.

Native addon builds clean (Release).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:05:54 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00
2026-06-16 18:48:12 +02:00

Slopsmith Desktop

Standalone cross-platform desktop app that wraps Slopsmith with integrated VST hosting, amp modeling, audio I/O, and full plugin support.

Install

Prebuilt installers for the latest tagged release are published on the GitHub Releases page.

Platform Download Notes
Windows 10/11 (x64) Slopsmith.Setup.<version>.exe NSIS installer. On first run Windows SmartScreen may warn — click More info → Run anyway.
macOS 12+ (Apple Silicon) Slopsmith-<version>-arm64.dmg Signed & notarized. Intel Macs are not currently published — build from source.
Linux (x86_64) Slopsmith-<version>.AppImage chmod +x then run. Portable, no install step.
Debian / Ubuntu (x86_64) slopsmith-desktop_<version>_amd64.deb sudo apt install ./slopsmith-desktop_<version>_amd64.deb

First launch may take a minute or two while ML model caches populate in the app cache directory. Subsequent launches are fast.

There is currently no Homebrew, winget, Chocolatey, Scoop, Flatpak, or Snap distribution — download directly from Releases. The app does not yet ship an auto-updater; check Releases periodically for new versions.

S
Description
No description provided
Readme AGPL-3.0
2.8 MiB
Languages
C++ 52%
TypeScript 18.6%
JavaScript 17.9%
Shell 7.3%
CMake 2.4%
Other 1.8%