Hey JUCE friends,
I recently had a go at running my plugin host app under the new Realtime Sanitizer. This sanitizer is set to come out as part of Clang 20, but I went ahead and compiled it from source to try it out: GitHub - realtime-sanitizer/rtsan: Central hub for RealtimeSanitizer and its associated tooling
It flagged to me an allocation that’s inside of VST3PluginInstance#processBlock.
Here’s the full stack trace:
==40309==ERROR: RealtimeSanitizer: unsafe-library-call
Intercepted call to real-time unsafe function `malloc` in real-time context!
#0 0x0001045dcaec in malloc rtsan_interceptors.cpp:442
#1 0x000196e47b54 in _malloc_type_malloc_outlined+0x60 (libsystem_malloc.dylib:arm64+0x1cb54)
#2 0x000196fdd368 in operator new(unsigned long)+0x30 (libc++abi.dylib:arm64+0x17368)
#3 0x000300016b84 in void* std::__1::__libcpp_operator_new[abi:ne180100]<unsigned long>(unsigned long) new:303
#4 0x000300016aec in std::__1::__libcpp_allocate[abi:ne180100](unsigned long, unsigned long) new:327
#5 0x00030002fd14 in std::__1::allocator<float*>::allocate[abi:ne180100](unsigned long) allocator.h:125
#6 0x00030002fbf0 in std::__1::__allocation_result<std::__1::allocator_traits<std::__1::allocator<float*>>::pointer> std::__1::__allocate_at_least[abi:ne180100]<std::__1::allocator<float*>>(std::__1::allocator<float*>&, unsigned long) allocate_at_least.h:55
#7 0x00030002fb30 in std::__1::__split_buffer<float*, std::__1::allocator<float*>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<float*>&) __split_buffer:343
#8 0x00030002f8ec in std::__1::__split_buffer<float*, std::__1::allocator<float*>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<float*>&) __split_buffer:339
#9 0x0003000427d0 in float** std::__1::vector<float*, std::__1::allocator<float*>>::__push_back_slow_path<float*>(float*&&) vector:1451
#10 0x000300042670 in std::__1::vector<float*, std::__1::allocator<float*>>::push_back[abi:ne180100](float*&&) vector:1479
#11 0x0003001600cc in void juce::HostBufferMapper::associateBufferTo<float>(Steinberg::Vst::AudioBusBuffers&, std::__1::vector<float*, std::__1::allocator<float*>>&, juce::AudioBuffer<float>&, juce::ChannelMapping const&, int) const juce_VST3Common.h:1164
#12 0x00030015ff70 in Steinberg::Vst::AudioBusBuffers* juce::HostBufferMapper::getVst3LayoutForJuceBuffer<float>(juce::AudioBuffer<float>&) juce_VST3Common.h:1136
#13 0x00030015e80c in void juce::VST3PluginInstance::associateWith<float>(Steinberg::Vst::ProcessData&, juce::AudioBuffer<float>&) juce_VST3PluginFormat.cpp:3574
#14 0x00030015e350 in void juce::VST3PluginInstance::processAudio<float>(juce::AudioBuffer<float>&, juce::MidiBuffer&, Steinberg::Vst::SymbolicSampleSizes, bool) juce_VST3PluginFormat.cpp:2775
#15 0x000300152034 in juce::VST3PluginInstance::processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) juce_VST3PluginFormat.cpp:2698
Given processBlock functions are always expected to be called from an audio/realtime thread, I find this quite surprising!
Could the JUCE team consider fixing this as a bug?
Thanks a lot,
Moe
