The Problem:
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
Investigation Done:
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.
Additional Information:
std::string related experiment:
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.
C++ standard consistency:
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.
Reproducing Attempt:
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.
Compiler and SDK info:
MSVC 14.37.32822 , using C++17 standard. VST SDK 3.7.8 build 34 (2023-05-15)