Fixes #6626: Throw if Lv2 object CTORs fail (#6951)

On plugin instantiation failure, `Lv2Proc::m_valid` was being set to false. However, `Lv2Proc::run` did not evaluate `m_valid` and still called `lilv_instance_run`, which caused undefined behavior, including crashes.

This bug fixes this by not even create such zombie classes, and instead `throw`s right away. The throws are caught in `lmms_plugin_main`, as suggested in the PR discussion and as the VST3 approach.
This commit is contained in:
Johannes Lorenz
2024-02-18 15:56:45 +01:00
committed by GitHub
parent 99120f567d
commit 34ab5ff730
9 changed files with 37 additions and 69 deletions

View File

@@ -24,6 +24,7 @@
#include "Lv2Effect.h"
#include <QDebug>
#include "Lv2SubPluginFeatures.h"
@@ -109,9 +110,12 @@ extern "C"
PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data)
{
using KeyType = Plugin::Descriptor::SubPluginFeatures::Key;
auto eff = new Lv2Effect(_parent, static_cast<const KeyType*>(_data));
if (!eff->isValid()) { delete eff; eff = nullptr; }
return eff;
try {
return new Lv2Effect(_parent, static_cast<const KeyType*>(_data));
} catch (const std::runtime_error& e) {
qCritical() << e.what();
return nullptr;
}
}
}

View File

@@ -41,8 +41,6 @@ public:
initialization
*/
Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key* _key);
//! Must be checked after ctor or reload
bool isValid() const { return m_controls.isValid(); }
bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override;
EffectControls* controls() override { return &m_controls; }

View File

@@ -38,11 +38,8 @@ Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) :
EffectControls(effect),
Lv2ControlBase(this, uri)
{
if (isValid())
{
connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged,
this, [this](){onSampleRateChanged();});
}
connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged,
this, &Lv2FxControls::onSampleRateChanged);
}

View File

@@ -76,19 +76,16 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg,
Instrument(instrumentTrackArg, &lv2instrument_plugin_descriptor, key),
Lv2ControlBase(this, key->attributes["uri"])
{
if (Lv2ControlBase::isValid())
{
clearRunningNotes();
clearRunningNotes();
connect(instrumentTrack()->pitchRangeModel(), SIGNAL(dataChanged()),
this, SLOT(updatePitchRange()), Qt::DirectConnection);
connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged,
this, [this](){onSampleRateChanged();});
connect(instrumentTrack()->pitchRangeModel(), SIGNAL(dataChanged()),
this, SLOT(updatePitchRange()), Qt::DirectConnection);
connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged,
this, &Lv2Instrument::onSampleRateChanged);
// now we need a play-handle which cares for calling play()
auto iph = new InstrumentPlayHandle(this, instrumentTrackArg);
Engine::audioEngine()->addPlayHandle(iph);
}
// now we need a play-handle which cares for calling play()
auto iph = new InstrumentPlayHandle(this, instrumentTrackArg);
Engine::audioEngine()->addPlayHandle(iph);
}
@@ -134,11 +131,6 @@ void Lv2Instrument::onSampleRateChanged()
bool Lv2Instrument::isValid() const { return Lv2ControlBase::isValid(); }
void Lv2Instrument::saveSettings(QDomDocument &doc, QDomElement &that)
{
Lv2ControlBase::saveSettings(doc, that);
@@ -321,9 +313,12 @@ extern "C"
PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data)
{
using KeyType = Plugin::Descriptor::SubPluginFeatures::Key;
auto ins = new Lv2Instrument(static_cast<InstrumentTrack*>(_parent), static_cast<KeyType*>(_data));
if (!ins->isValid()) { delete ins; ins = nullptr; }
return ins;
try {
return new Lv2Instrument(static_cast<InstrumentTrack*>(_parent), static_cast<KeyType*>(_data));
} catch (const std::runtime_error& e) {
qCritical() << e.what();
return nullptr;
}
}
}

View File

@@ -61,8 +61,6 @@ public:
~Lv2Instrument() override;
void reload();
void onSampleRateChanged();
//! Must be checked after ctor or reload
bool isValid() const;
/*
load/save