Glib::Dispatcher can no longer be destroyed during emit
The following program attempts to destroy a Glib::Dispatcher
during the emission of its own signal.
It works on glibmm 2.4 but crashes on glibmm 2.68 with a use-after-free, leading to a bus error:
#include <optional>
#include <glibmm/dispatcher.h>
#include <glibmm/main.h>
int main()
{
auto mainloop = Glib::MainLoop::create();
std::optional<Glib::Dispatcher> dispatcher;
Glib::signal_idle().connect([&] {
dispatcher.emplace();
dispatcher->connect([&] {
dispatcher.reset(); // !!!
mainloop->quit();
});
dispatcher->emit();
return false;
});
mainloop->run();
}
Furthermore, commenting out the line which performs the self-destruction, annotated !!!
, fixes the crash.
The use-after-free is:
Invalid read of size 8
at 0x48D6F42: Glib::DispatchNotifier::pipe_io_handler(Glib::IOCondition) (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x48D4A6F: Glib::IOSource::dispatch(sigc::slot_base*) (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x48D68EE: Glib::Source::dispatch_vfunc(_GSource*, int (*)(void*), void*) (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x49C3F68: ??? (in /usr/lib/libglib-2.0.so.0.7800.1)
by 0x4A22326: ??? (in /usr/lib/libglib-2.0.so.0.7800.1)
by 0x49C4B96: g_main_loop_run (in /usr/lib/libglib-2.0.so.0.7800.1)
by 0x10A419: main (simpletest.cpp:24)
Address 0x510b108 is 8 bytes inside a block of size 48 free'd
at 0x4844B3F: operator delete(void*, unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x48D70C4: Glib::DispatchNotifier::unreference_instance(Glib::DispatchNotifier*, Glib::Dispatcher::Impl*) (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x48D7154: Glib::Dispatcher::~Dispatcher() (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x10C245: std::_Optional_payload_base<Glib::Dispatcher>::_M_destroy() (optional:287)
by 0x10C17D: std::_Optional_payload_base<Glib::Dispatcher>::_M_reset() (optional:318)
by 0x10C199: std::_Optional_base_impl<Glib::Dispatcher, std::_Optional_base<Glib::Dispatcher, false, false> >::_M_reset() (optional:468)
by 0x10C087: std::optional<Glib::Dispatcher>::reset() (optional:1170)
by 0x10A264: main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const (simpletest.cpp:15)
by 0x10B014: void std::__invoke_impl<void, main::{lambda()#1}::operator()() const::{lambda()#1}&>(std::__invoke_other, main::{lambda()#1}::operator()() const::{lambda()#1}&) (invoke.h:61)
by 0x10AE82: std::__invoke_result<main::{lambda()#1}::operator()() const::{lambda()#1}&>::type std::__invoke<main::{lambda()#1}::operator()() const::{lambda()#1}&>(main::{lambda()#1}::operator()() const::{lambda()#1}&) (invoke.h:96)
by 0x10AC53: std::invoke_result<main::{lambda()#1}::operator()() const::{lambda()#1}&>::type std::invoke<main::{lambda()#1}::operator()() const::{lambda()#1}&>(main::{lambda()#1}::operator()() const::{lambda()#1}&) (functional:113)
by 0x10A93D: decltype(auto) sigc::adaptor_functor<main::{lambda()#1}::operator()() const::{lambda()#1}>::operator()<>() const (adaptor_trait.h:100)
Block was alloc'd at
at 0x4841F73: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x48DCF82: Glib::DispatchNotifier::reference_instance(std::shared_ptr<Glib::MainContext> const&) (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x48DCFE5: Glib::Dispatcher::Impl::Impl(std::shared_ptr<Glib::MainContext> const&) (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x48DD044: Glib::Dispatcher::Dispatcher() (in /usr/lib/libglibmm-2.68.so.1.3.0)
by 0x10C30E: void std::_Construct<Glib::Dispatcher>(Glib::Dispatcher*) (stl_construct.h:119)
by 0x10C269: void std::_Optional_payload_base<Glib::Dispatcher>::_M_construct<>() (optional:278)
by 0x10C1B5: void std::_Optional_base_impl<Glib::Dispatcher, std::_Optional_base<Glib::Dispatcher, false, false> >::_M_construct<>() (optional:457)
by 0x10C03F: std::enable_if<is_constructible_v<Glib::Dispatcher>, Glib::Dispatcher&>::type std::optional<Glib::Dispatcher>::emplace<>() (optional:918)
by 0x10A2AA: main::{lambda()#1}::operator()() const (simpletest.cpp:12)
by 0x10B0C6: bool std::__invoke_impl<bool, main::{lambda()#1}&>(std::__invoke_other, main::{lambda()#1}&) (invoke.h:61)
by 0x10AF62: std::__invoke_result<main::{lambda()#1}&>::type std::__invoke<main::{lambda()#1}&>(main::{lambda()#1}&) (invoke.h:96)
by 0x10ADA3: std::invoke_result<main::{lambda()#1}&>::type std::invoke<main::{lambda()#1}&>(main::{lambda()#1}&) (functional:113)