EditorWrapperComponent::resizeHostWindow() calls host.guiRequestResize()
unconditionally:
|
void resizeHostWindow() |
|
{ |
|
if (editor != nullptr) |
|
{ |
|
auto editorBounds = getSizeToContainChild().withPosition(0, 0); |
|
{ |
|
const juce::ScopedValueSetter<bool> resizingParentSetter(resizingParent, true); |
|
host.guiRequestResize((uint32_t)editorBounds.getWidth(), |
|
(uint32_t)editorBounds.getHeight()); |
|
} |
|
|
|
setBounds(editorBounds.withPosition(0, 0)); |
|
} |
|
} |
clap_host_gui is optional, and clap-helpers only asserts the predicate
before dereferencing:
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::guiRequestResize(uint32_t width, uint32_t height) const noexcept {
assert(canUseGui());
ensureMainThread("gui.request_resize");
return _hostGui->request_resize(_host, width, height);
}
assert is compiled out under NDEBUG, so in a release build a host without
clap_host_gui gets a null dereference rather than a no-op. This is exactly the
shape of #46 (paramsRescan() / clap_host_params), which is why every other
optional host extension in the wrapper is already guarded — canUseParams,
canUseState, canUseLatency, canUseTimerSupport, canUsePosixFdSupport,
canUseVoiceInfo, canUseNoteName, canUseRemoteControls, canUsePresetLoad,
canUseContextMenu. guiRequestResize appears to be the only one missed.
Symptom
EXC_BAD_ACCESS (SIGSEGV), KERN_INVALID_ADDRESS at 0x0000000000000008
ClapJuceWrapper::guiSetScale(double)
The faulting address is 8 because request_resize is the second member of
clap_host_gui.
The backtrace is misleading: everything inlines into one frame, so it names
guiSetScale and reads like a bug in the editor wrapper. It is not —
editorWrapper and editorWrapper->editor are both non-null, and both are
already null-checked. The null pointer is the host's. The real path is:
guiSetScale -> EditorWrapperComponent::setEditorScaleFactor
-> resizeHostWindow -> HostProxy::guiRequestResize
clap_plugin_gui::set_scale is not special — it is just the earliest call that
reaches resizeHostWindow(). childBoundsChanged() reaches the same line
whenever an editor adjusts its own bounds, so an editor that self-resizes
crashes the same way on such a host, with no set_scale involved.
Reproduction
A minimal CLAP host whose get_extension returns nullptr for everything
(legal — every CLAP extension is optional): create the plugin, gui->create,
then gui->set_scale(plugin, 1.0). Segfaults. Ordering is not a factor —
calling it after set_parent and show crashes too, just later.
Verified in both directions, on three separate release binaries (a JUCE 9
WebView-editor synth, and the same synth with a plain JUCE editor built against
JUCE 9 and JUCE 8 — all three crash identically):
- adding a
clap_host_gui to the host makes the crash disappear with the
wrapper unpatched, and the editor then attaches and sizes correctly;
- guarding the call makes it disappear with the host unchanged.
Real DAWs implement clap_host_gui — it is how a plugin asks to be resized —
so REAPER and Ableton Live never reach it. That is why this has gone unnoticed;
it is also why it is invisible to CI, which builds an effect and runs
clap-info without ever instantiating a window.
Fix
Guard the host call only. setBounds() should stay unconditional: a host that
declines, or never offers, a resize does not make the plugin's own view stale.
{
const juce::ScopedValueSetter<bool> resizingParentSetter(resizingParent, true);
if (host.canUseGui())
host.guiRequestResize((uint32_t)editorBounds.getWidth(),
(uint32_t)editorBounds.getHeight());
}
setBounds(editorBounds.withPosition(0, 0));
Inert for any host implementing clap_host_gui — i.e. for every host that could
previously reach this line without crashing.
EditorWrapperComponent::resizeHostWindow()callshost.guiRequestResize()unconditionally:
clap-juce-extensions/src/wrapper/clap-juce-wrapper.cpp
Lines 2118 to 2131 in 54b3c32
clap_host_guiis optional, and clap-helpers only asserts the predicatebefore dereferencing:
assertis compiled out underNDEBUG, so in a release build a host withoutclap_host_guigets a null dereference rather than a no-op. This is exactly theshape of #46 (
paramsRescan()/clap_host_params), which is why every otheroptional host extension in the wrapper is already guarded —
canUseParams,canUseState,canUseLatency,canUseTimerSupport,canUsePosixFdSupport,canUseVoiceInfo,canUseNoteName,canUseRemoteControls,canUsePresetLoad,canUseContextMenu.guiRequestResizeappears to be the only one missed.Symptom
The faulting address is 8 because
request_resizeis the second member ofclap_host_gui.The backtrace is misleading: everything inlines into one frame, so it names
guiSetScaleand reads like a bug in the editor wrapper. It is not —editorWrapperandeditorWrapper->editorare both non-null, and both arealready null-checked. The null pointer is the host's. The real path is:
clap_plugin_gui::set_scaleis not special — it is just the earliest call thatreaches
resizeHostWindow().childBoundsChanged()reaches the same linewhenever an editor adjusts its own bounds, so an editor that self-resizes
crashes the same way on such a host, with no
set_scaleinvolved.Reproduction
A minimal CLAP host whose
get_extensionreturnsnullptrfor everything(legal — every CLAP extension is optional): create the plugin,
gui->create,then
gui->set_scale(plugin, 1.0). Segfaults. Ordering is not a factor —calling it after
set_parentandshowcrashes too, just later.Verified in both directions, on three separate release binaries (a JUCE 9
WebView-editor synth, and the same synth with a plain JUCE editor built against
JUCE 9 and JUCE 8 — all three crash identically):
clap_host_guito the host makes the crash disappear with thewrapper unpatched, and the editor then attaches and sizes correctly;
Real DAWs implement
clap_host_gui— it is how a plugin asks to be resized —so REAPER and Ableton Live never reach it. That is why this has gone unnoticed;
it is also why it is invisible to CI, which builds an effect and runs
clap-infowithout ever instantiating a window.Fix
Guard the host call only.
setBounds()should stay unconditional: a host thatdeclines, or never offers, a resize does not make the plugin's own view stale.
{ const juce::ScopedValueSetter<bool> resizingParentSetter(resizingParent, true); if (host.canUseGui()) host.guiRequestResize((uint32_t)editorBounds.getWidth(), (uint32_t)editorBounds.getHeight()); } setBounds(editorBounds.withPosition(0, 0));Inert for any host implementing
clap_host_gui— i.e. for every host that couldpreviously reach this line without crashing.