25

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)

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
Yuwei Xu
  • 433
  • 3
  • 5
  • 20
    Note that this question, and the curation actions that have been taken on it, [is being discussed on Meta](https://meta.stackoverflow.com/q/426189/10871073). – Adrian Mole Aug 27 '23 at 12:25
  • So much complaints and noise on meta, but it still lacks of the information about how the static library and the code are built and a compliable [mcve] ignoring the missing dependencies . Why not attempt to build them with the enabled address sanitizer? – 273K Aug 27 '23 at 20:10
  • 5
    `ci.get().category = "testCategory"; //OK` - here you use inline `get()` implemented in the header file, hence it is compiled in your code. `const_cast(ci.category()) = "testCategory"; //Crash, Access violation at 0xFFFFFFFFFFFFFFFF` - here you call `category()` that is not inline and implemented in the library. This is a clear sign of the ABI incompatibility. Maybe mixing 64 bit and 32 bit object files. – 273K Aug 27 '23 at 20:29
  • 1
    @273K Thanks for coming back hand helping, the ABI argument makes a lot of sene. I'm checking in this direction now. – Yuwei Xu Aug 28 '23 at 02:41
  • Are you using a debug build of the SDK with debug build of your code, and release for release? – Dan Mašek Aug 29 '23 at 14:14
  • 3
    @273K Mixing 32- and 64-bit object files/libraries would almost certainly cause a **linker** error with MSVC. Not a run-time issue. – Adrian Mole Aug 29 '23 at 14:36

0 Answers0