I'm trying to use the VST Hosting utilities included in the SDK to load a plugin. The code is as shown:
#include "vst/v3/Vst3CommonIncludes.h"
int main()
{
std::string vst3_module_path = R"(C:\Program Files\Common Files\VST3\Kontakt.vst3)";
std::string error;
std::shared_ptr<Module> vst_module = Module::create(vst3_module_path, error);
std::vector<ClassInfo> class_infos = vst_module->getFactory().classInfos();;
assert(error.empty());
assert(class_infos.size());
ClassInfo plugin_info = class_infos[0]; //Crash
//... load the plugin and do more things
return 0;
}
where Vst3CommonIncludes.h
just includes all the VST SDK headers from pluginterfaces/vst
and public.sdk/source/vst
8
In my case, the SDK comes in source and cmake files to build them into a static library. So my code and SDK code share the same compiler.
VST SDK sources from Steinberg Media
My investigation showed that PluginFactory::classInfos()
returned corrupted data, trying to assign from them causes std::bad_alloc
since the size of std::string
is not valid.
Definition of PluginFactory::classInfos() in VST SDK:
PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept
{
auto count = classCount ();
Optional<FactoryInfo> factoryInfo;
ClassInfos classes;
classes.reserve (count);
auto f3 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory);
auto f2 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory2> (factory);
Steinberg::PClassInfo ci;
Steinberg::PClassInfo2 ci2;
Steinberg::PClassInfoW ci3;
for (uint32_t i = 0; i < count; ++i)
{
if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue)
//------------Unexpected behaviour here--------------------
classes.emplace_back (ci3); //--
//---------------------------------------------------------
else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue)
classes.emplace_back (ci2);
else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue)
classes.emplace_back (ci);
auto& classInfo = classes.back ();
if (classInfo.vendor ().empty ())
{
if (!factoryInfo)
factoryInfo = Optional<FactoryInfo> (info ());
classInfo.get ().vendor = factoryInfo->vendor ();
}
}
return classes;
}
After the in-place construction of the new ClassInfo element, ClassInfo::data::category
and other std::string
members (name, vendor, etc.) reads <NULL>
in debugger.
Stepping into the constructor of std::string
, I've found the this
pointer during construction of data.category
is NOT equal to &data.category
, and was offset by 4 bytes.
&data.category = 0x 0000 009b 546f ed14
this (std::string constructor scope) = 0x 0000 009b 546f ed18
//Actual address varies but the offset remains the same
Thus the string object became corrupted and later crashes the program.
Also, experimenting with ClassInfo
and its string members, I ran into this:
ClassInfo ci;
ci.get().category = "testCategory"; //OK
const_cast<string&>(ci.category()) = "testCategory"; //Crash, Access violation at 0xFFFFFFFFFFFFFFFF
I think it's highly related to the problem, but I couldn't come up with an explanation.
I also added
#if __cplusplus != 201703L
#error
#endif
to every relevant file, so I'm sure they share the same STL implementation, the problem will still occur.
I hoped to recreate a minimal scenario where VST SDK is not included, and with my own MimicClassInfo
that resembles the structure of original ClassInfo
in some aspects. The problem does not occur.
MSVC 14.37.32822 , using C++17 standard. VST SDK 3.7.8 build 34 (2023-05-15)
Another reason of such behaviour is different compiler flags (especially macros) used to build the C++ files.
For example if some class X
conditionally (based on some macro) defines one more additional data member, that affects the data members layout. If you compile two C++ files one with the macro and another one without that macro, your application may crash if those two C++ translation units work on the same object of class X
.
So your C++ code can be syntactically correct, but because of different compiler flags, linker may build buggy app.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With