AU plugin crashes Reaper in macOS on load - parameters related?

I am stuck identifying the cause of the issue below and need help. My plugin (latest Juce) runs on macOS (Ventura, Intel) in several hosts as VST3, AAX and AU. But the AU crashes Reaper immediately when it is loaded. The same AU runs without issues in Logic and Ableton and passes auval flawlessly. Debugging is hard as the crash seems to occur after the AU wrapper passes back to Reaper code.
Last sign of life is here in ComponentBase::AP_Open. Immediately after it it crashes.

/*!
	@file		AudioUnitSDK/ComponentBase.cpp
	@copyright	© 2000-2021 Apple Inc. All rights reserved.
*/

OSStatus ComponentBase::AP_Open(void* self, AudioComponentInstance compInstance)
{
	OSStatus result = noErr;
	const auto acpi = static_cast<AudioComponentPlugInInstance*>(self);
	try {
		const std::lock_guard guard{ InitializationMutex() };

		auto* const cb =
			static_cast<ComponentBase*>((*acpi->mConstruct)(&acpi->mInstanceStorage, compInstance));
		cb->DoPostConstructor(); // allows base class to do additional initialization
		// once the derived class is fully constructed
		result = noErr;
	}
	AUSDK_Catch(result)
	if (result != noErr) {
		delete acpi; // NOLINT
	}
	return result;
}

Here is the stack trace which does not give me any hints:

reaper (1) Queue : com.apple.main-thread (serial)
#0	0x0000000127c5eb23 in ausdk::ComponentBase::AP_Open(void*, ComponentInstanceRecord*) at /Users/setup/Library/Developer/VST/JUCE Projects/Vaporizer2/JUCE/modules/juce_audio_plugin_client/AU/AudioUnitSDK/ComponentBase.cpp:51
#1	0x00007ff80c6fa551 in APComponent::newInstance(unsigned int, bool, void (OpaqueAudioComponentInstance*, int) block_pointer) ()
#2	0x00007ff80c7f9e97 in instantiate(OpaqueAudioComponent*, unsigned int, bool, void (OpaqueAudioComponentInstance*, int) block_pointer) ()
#3	0x00007ff80c7fa2e7 in __AudioComponentInstanceNew_block_invoke ()
#4	0x00007ff80c67832b in Synchronously ()
#5	0x00007ff80c7fa0cf in AudioComponentInstanceNew ()
#6	0x0000000109502e4f in __OpenComponent(void*) ()
#7	0x000000010950147c in AU_Plugin::AU_Plugin(void*, char const*, void*, int*) ()
#8	0x0000000109353b7c in FxDsp::FxDsp(int, char const*, void*) ()
#9	0x000000010936b880 in FxChain::addSelection(HWND__*, bool, int) ()
#10	0x000000010939420c in addDialogProc(HWND__*, unsigned int, unsigned long, long) ()
#11	0x00000001094af9c6 in SwellDialogDefaultWindowProc(HWND__*, unsigned int, unsigned long, long) ()
#12	0x0000000109397176 in addDialogProc(HWND__*, unsigned int, unsigned long, long) ()
#13	0x00000001094af9c6 in SwellDialogDefaultWindowProc(HWND__*, unsigned int, unsigned long, long) ()
#14	0x00000001094ae100 in -[REAPERSwell_hwnd swellOnControlDoubleClick:] ()
#15	0x00007ff80de9542c in -[NSApplication(NSResponder) sendAction:to:from:] ()
#16	0x00007ff80de952b0 in -[NSControl sendAction:to:] ()
#17	0x00007ff80dfcc5dd in -[NSTableView _sendAction:to:row:column:] ()
#18	0x00007ff80dfcafb7 in -[NSTableView mouseDown:] ()
#19	0x00000001094c6fa9 in -[REAPERSwell_listview mouseUp:] ()
#20	0x00007ff80e6663e6 in _routeMouseUpEvent ()
#21	0x00007ff80de067eb in -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] ()
#22	0x00007ff80de06427 in -[NSWindow(NSEventRouting) sendEvent:] ()
#23	0x00007ff80de04e01 in -[NSApplication(NSEvent) sendEvent:] ()
#24	0x00000001092fe368 in -[REAPERapp sendEvent:] ()
#25	0x00007ff80e0bf74e in -[NSApplication _handleEvent:] ()
#26	0x00007ff80dc947ad in -[NSApplication run] ()
#27	0x00007ff80dc689a1 in NSApplicationMain ()
#28	0x0000000108fa8534 in start ()

What I found out in many debugging sessions is, that it seems to be related to parameters. When I clear the parameter array of the plugin in cb by setting the numUsed to 0 below, then the plugin loads and runs fine - but without any host parameters of course. As soon as I set it to 1 or higher it crashes.

juceParameters	juce::LegacyAudioParametersWrapper	
params	juce::Array<juce::AudioProcessorParameter *>	
values	juce::ArrayBase<juce::AudioProcessorParameter *, juce::DummyCriticalSection>	
elements	juce::HeapBlock<juce::AudioProcessorParameter *>	
numUsed	int	755

Do you have any recommendations? What in my parameters could cause the AU in Reaper to crash (and crash Reaper with it)?

Thanks!

Not sure what could be causing the problem, but it might be worth testing in the JUCE AudioPluginHost to see whether that’s able to load and run the plugin as expected.

You could also try building your plugin and the AudioPluginHost with address sanitizer enabled (add -fsanitize=address to you compiler flags in both projects), as this will tell you if there are problems with memory accesses in your code.

2 Likes

Thank you. I tried both. There are no issues that address sanitizer brings up. The plugin runs without problems in JUCE AudioPluginHost.
Is there anything else that I could check?

Hi reuk,

After several hours of debugging I finally found out, that everything works fine when I comment out the following statement in AudioUnitSDK/AUBase.h

	static void FillInParameterName(
		AudioUnitParameterInfo& ioInfo, CFStringRef inName, bool inShouldRelease)
	{
		ioInfo.cfNameString = inName;
		ioInfo.flags |= kAudioUnitParameterFlag_HasCFNameString;
		if (inShouldRelease) {
			ioInfo.flags |= kAudioUnitParameterFlag_CFNameRelease;
		}
		//CFStringGetCString(inName, &ioInfo.name[0], offsetof(AudioUnitParameterInfo, clumpID), kCFStringEncodingUTF8);
	}

The statement seems odd to me since in the description of struct AudioUnitParameterInfo it is written:

@struct			AudioUnitParameterInfo
	@var  			name
						UNUSED - set to zero - UTF8 encoded C string (originally).

So is this a bug in Apple’s SDK? And maybe Reaper still uses the name somehow? But how could it be that this only affects my plugin then and is not a broader issue for everyone?
Any ideas?

Thanks!

Does anyone have any idea why filling the “name” variable causes a crash in Reaper? Has no one observed anything similar?

This is how the struct is defined:

@file		AudioUnitProperties.h
@framework	AudioToolbox.framework
@copyright	(c) 2000-2018 Apple, Inc. All rights reserved.
struct AudioUnitParameterInfo {
	char						name[52];
	CFStringRef __nullable		unitName;
	UInt32						clumpID;
	CFStringRef __nullable		cfNameString;
	AudioUnitParameterUnit		unit;
	AudioUnitParameterValue		minValue;
	AudioUnitParameterValue		maxValue;
	AudioUnitParameterValue		defaultValue;
	AudioUnitParameterOptions	flags;
};
typedef struct AudioUnitParameterInfo AudioUnitParameterInfo;

The used

offsetof(AudioUnitParameterInfo, clumpID)

gives me 64 bytes in debugger, while I would have expected 52 * 1 byte + sizeof(CFStringRef) (=8) byte = 60 bytes. In any case, given Apple’s documentation (CFStringGetCString(_:_:_:_:) | Apple Developer Documentation) the CFStringGetCString will use a buffersize of 64 for a char[52] leading to the crash.

I saw a string related crash recently on plugin load in Pro Tools Dev for a debug build only. Does your crash happen in a release version? Does your crash happen in any other formats/hosts?

It only happens in Reaper and only with AudioUnits. I assume that it is related to the fact that my plugin has some really long parameter names. Not the IDs which are known to be problematic when longer than 31 chars for example in AAX. I am referring to the names. I will shorten them. But I believe that Apple’s code in AuBase.h of the AudioUnitSDK is flawed.

Just guessing out loud: 52 is an unusual number. Maybe the original size of name in the struct was 64, but then Apple shaved some bytes off of it to fit in some of the other fields (unitName, clumpID, cfNameString?) without increasing the sizeof of the struct.
And then they forgot to reduce accordingly the expected size of the name buffer in other parts of the code, leading to the 52 != 64 mismatch.

That’s perhaps something for @mfritze to consider?

1 Like

Yes. Exactly what I thought.

Thanks for reporting this issue. I’ve created a PR against the official repo:

In the meantime, I’ll patch the copy of this function used in JUCE.

1 Like