From 464df064198a0d56af5ddde1d5a2c0d3200942e6 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 4 Jun 2023 21:42:02 +0200 Subject: [PATCH] Fix signal emission order ambiguity (record regionview) A rec-region is added to the streamview just like any other region (::add_region_view_internal). This subscribes to region->DropReferences. When the DropReferences is handled first by StreamView::remove_region_view the corresponding RegionView is destroyed. This can happen even while recording is still active, eg. when locating (which stops the current recording). MidiStreamView::setup_rec_box() is called and crashes in `dynamic_cast (rec_regions.back().second);` due to a use after free. Strictly speaking this is a logic error in how ::setup_rec_box() determines if to add or remove the rec-box. But due to the asynchronous nature of signal emission and transport-state changes the best solution is to destroy the rec-region at the same when the RegionView is destroyed. To reproduce: * create a session with a MIDI track * disconnect the input (empty MIDI regions are removed) * Preferences > Transport > *enable* latched-record-enable * use the Dummy backend's MIDI generator * connect Hardware > MIDI > MMC -> Ardour misc > MMC in OR use JACK-transport to locate while recording. --- gtk2_ardour/streamview.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc index 3b5318d087..c8e5cd47a3 100644 --- a/gtk2_ardour/streamview.cc +++ b/gtk2_ardour/streamview.cc @@ -193,6 +193,23 @@ StreamView::remove_region_view (std::weak_ptr weak_r) return; } + bool clear_rec_rects = false; + for (list,RegionView*> >::iterator i = rec_regions.begin(); i != rec_regions.end();) { + if (i->first == r) { + i = rec_regions.erase (i); + clear_rec_rects = true; + } else { + ++i; + } + } + + if (clear_rec_rects) { + for (auto const& i : rec_rects) { + delete i.rectangle; + } + rec_rects.clear(); + } + for (list::iterator i = region_views.begin(); i != region_views.end(); ++i) { if (((*i)->region()) == r) { RegionView* rv = *i; @@ -477,7 +494,8 @@ StreamView::cleanup_rec_box () rec_active = false; /* remove temp regions */ - for (auto const& i : rec_regions) { + auto rr (rec_regions); + for (auto const& i : rr) { i.first->drop_references (); }