Null-pointer crash on dispatching desktop mouse events


this user crash seems impossible to reason about. The program crashes (dereferencing a nullptr) while clicking an entry in a combobox. The stacktrace looks like this:

[thunk]:juce::ComponentListener::`vcall'{24,{flat}}' }'()	C++
juce::ListenerList<juce::ComponentListener,juce::Array<juce::ComponentListener *,juce::DummyCriticalSection,0> >::callChecked<juce::Component::BailOutChecker,juce::Component &>(const juce::Component::BailOutChecker & bailOutChecker={...}, void (juce::Component &) * callbackFunction=0x0f960f37, juce::Component & param1={...}) Line 178	C++
juce::Component::internalMouseUp(juce::MouseInputSource source={...}, juce::Point<float> relativePos={...}, juce::Time time={...}, const juce::ModifierKeys oldModifiers={...}) Line 2500	C++
juce::MouseInputSourceInternal::sendMouseUp(juce::Component & comp={...}, juce::Point<float> screenPos={...}, juce::Time time={...}, const juce::ModifierKeys oldMods={...}) Line 148	C++
juce::MouseInputSourceInternal::setButtons(juce::Point<float> screenPos={...}, juce::Time time={...}, const juce::ModifierKeys newButtonState={...}) Line 191	C++
juce::MouseInputSourceInternal::handleEvent(juce::ComponentPeer & newPeer={...}, juce::Point<float> positionWithinPeer={...}, juce::Time time={...}, const juce::ModifierKeys newMods={...}) Line 306	C++
juce::ComponentPeer::handleMouseEvent(int touchIndex=0, juce::Point<float> pos={...}, juce::ModifierKeys newMods={...}, __int64 time=1461152549852) Line 92	C++
juce::HWNDComponentPeer::doMouseUp(juce::Point<float> position={...}, const unsigned int wParam=0) Line 1775	C++
juce::HWNDComponentPeer::peerWindowProc(HWND__ * h=0x00010618, unsigned int message=514, unsigned int wParam=0, long lParam=3211451) Line 2440	C++

Note: This callstack is recreated from the situation (but it is completely identical), thus the parameters to the functions may not be representable.

Anyway, this is the expression crashing (at juce_Component: Component::internalMouseUp):

Desktop& desktop = Desktop::getInstance();
desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseUp, me);

Inside the listener loop that calls the member functions, it crashes on this assembly expression:

0F960F37  mov         eax,dword ptr [ecx]  
0F960F39  jmp         dword ptr [eax+18h] 

So, as far as I can see, the listener in the list is a nullptr (in the recreated example, the listener should be (juce::MouseListener*) juce::PopupMenu).

What can ever cause this situation? Looking at ListenerList, it doesn’t add nullptrs to the list. However, the code in question only concerns internal JUCE code. I only use mouse listeners actively two places, that are listeners to completely different components (and the code in question only calls global mouse listeners).

Perhaps I didn’t disassemble the instructions clearly (it also seems to have an additional dereferencing, possibly indicating the listenerobject -> desktop object itself is null?).

Any ideas would be appreciated.

Could it just be a dangling pointer left on that ListenerList?

Hmm, it is plausable - but I would have expected a pure virtual function call exception then… Although that of course is not guaranteed.

It’s hard to see how that would happen, since MenuWindow::~MenuWindow (the one assumingly recieving and crashing on the callback) deletes itself from the global mouse listener list at destruction, which I assume can only happen on the main thread, so no concurrency issues…

The only references to Desktop::addGlobalMouseListener that exists in my project are inside the PopupMenu and the MenuBarComponent (which I don’t use).

Yes, odd. The ListenerList class is deliberately built to be robust against items being deleted or removed while it’s in mid-iteration… If you could reproduce it then it’s probably easy to see what’s going on, but I can’t think of any good clues from this info, I’m afraid!

…just one other thought: Could the Desktop object itself be getting deleted at this point? e.g. by something shutting down the whole system and calling DeletedAtShutdown::deleteAll() in response to a mouse down event?

I’m afraid I can’t. The only reference to deleteAll() is inside shutdownJuce_GUI(), which is only called from inside the VST wrapper, OR the appWillTerminateByForce().

I can’t see any code-paths on the main-thread that will delete the Desktop in response to some mouse events. If something crashes during one of the events, I would have caught that stacktrace instead… The only thing I’m considering is, whether some crash on some other, untraced thread could inexplicibly cause the juce objects to be deleted (some termination handler… ?) on some other thread than the main-thread, thus categorizing this as a concurrency issue?

I guess a concurrency error could lead to anything, so yes, this could be one of those!