texus / TGUI

Cross-platform modern c++ GUI

Home Page:https://tgui.eu

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Gui Builder crashes randomly on creating a new form with - presumably - an underscore

Schweini07 opened this issue · comments

When trying to create a new form named e.g. "test_form.txt" with the Gui Builder file dialog, the program will sometimes crash.
image
This seems to cause from the file dialog trying to destroy, which for some reasons causes a segmentation fault.
The piece of code where this happens is located in ChildWindow.cpp at line 597:

void ChildWindow::destroy()
{
    if (m_parent)
        m_parent->remove(shared_from_this());
}

What is the really strange thing about this crash, is the fact, that it only seems to happen when using an underscore. Maybe there are other characters that could produce it, but I weren't able to find them yet.
What also wonders me, is, that the segmentation fault should be totally unrelated to the underscore, but yet they still seem to correlate.

Output of a binary compiled with -fsanitize-address
=================================================================
==3811==ERROR: AddressSanitizer: heap-use-after-free on address 0x6210001cb6c8 at pc 0x7f8c08f964c9 bp 0x7ffe4740a310 sp 0x7ffe4740a300
READ of size 8 at 0x6210001cb6c8 thread T0
    #0 0x7f8c08f964c8 in std::_Hashtable<unsigned int, std::pair<unsigned int const, std::function<void ()> >, std::allocator<std::pair<unsigned int const, std::function<void ()> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::size() const /usr/include/c++/12.2.1/bits/hashtable.h:649
    #1 0x7f8c08f8ae39 in std::_Hashtable<unsigned int, std::pair<unsigned int const, std::function<void ()> >, std::allocator<std::pair<unsigned int const, std::function<void ()> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::empty() const /usr/include/c++/12.2.1/bits/hashtable.h:653
    #2 0x7f8c08f7cb85 in std::unordered_map<unsigned int, std::function<void ()>, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::function<void ()> > > >::empty() const /usr/include/c++/12.2.1/bits/unordered_map.h:306
    #3 0x7f8c092a27b2 in tgui::SignalTyped<bool*>::emit(tgui::Widget const*, bool*) /home/laurenz/Dokumente/Programmieren/C++/tgui/include/TGUI/Signal.hpp:357
    #4 0x7f8c09262aab in tgui::ChildWindow::close() /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widgets/ChildWindow.cpp:587
    #5 0x7f8c09033131 in tgui::FileDialog::filesSelected(std::vector<tgui::Filesystem::Path, std::allocator<tgui::Filesystem::Path> >) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widgets/FileDialog.cpp:861
    #6 0x7f8c09034a74 in tgui::FileDialog::confirmButtonPressed() /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widgets/FileDialog.cpp:914
    #7 0x7f8c0902f328 in tgui::FileDialog::keyPressed(tgui::Event::KeyEvent const&) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widgets/FileDialog.cpp:563
    #8 0x7f8c095b505b in tgui::Container::processKeyPressEvent(tgui::Event::KeyEvent) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Container.cpp:1208
    #9 0x7f8c092e6cfb in tgui::BackendGui::handleEvent(tgui::Event) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Backend/Window/BackendGui.cpp:178
    #10 0x55ff7c00a94a in GuiBuilder::mainLoop() /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:446
    #11 0x55ff7bf24741 in main /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/main.cpp:70
    #12 0x7f8c0883c78f  (/usr/lib/libc.so.6+0x2378f)
    #13 0x7f8c0883c849 in __libc_start_main (/usr/lib/libc.so.6+0x23849)
    #14 0x55ff7bf24534 in _start (/home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/gui-builder+0x40534)

0x6210001cb6c8 is located 2504 bytes inside of 4000-byte region [0x6210001cad00,0x6210001cbca0)
freed by thread T0 here:
    #0 0x7f8c09ac178a in operator delete(void*, unsigned long) /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_new_delete.cpp:164
    #1 0x7f8c090d75ff in std::__new_allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::deallocate(std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/12.2.1/bits/new_allocator.h:158
    #2 0x7f8c090cde54 in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::deallocate(std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&, std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/12.2.1/bits/alloc_traits.h:496
    #3 0x7f8c090c2d51 in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::~__allocated_ptr() /usr/include/c++/12.2.1/bits/allocated_ptr.h:74
    #4 0x7f8c0912b209 in std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_destroy() /usr/include/c++/12.2.1/bits/shared_ptr_base.h:623
    #5 0x7f8c08f79d4c in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/12.2.1/bits/shared_ptr_base.h:191
    #6 0x7f8c08f6b183 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use_cold() /usr/include/c++/12.2.1/bits/shared_ptr_base.h:199
    #7 0x7f8c08f628d5 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/12.2.1/bits/shared_ptr_base.h:353
    #8 0x7f8c08f6b1cf in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/12.2.1/bits/shared_ptr_base.h:1071
    #9 0x7f8c08f6581d in std::__shared_ptr<tgui::Widget, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/12.2.1/bits/shared_ptr_base.h:1524
    #10 0x7f8c08f85e7a in std::__shared_ptr<tgui::Widget, (__gnu_cxx::_Lock_policy)2>::operator=(std::__shared_ptr<tgui::Widget, (__gnu_cxx::_Lock_policy)2>&&) /usr/include/c++/12.2.1/bits/shared_ptr_base.h:1620
    #11 0x7f8c08f764d5 in std::shared_ptr<tgui::Widget>::operator=(std::shared_ptr<tgui::Widget>&&) /usr/include/c++/12.2.1/bits/shared_ptr.h:440
    #12 0x7f8c095aa147 in tgui::Container::removeAllWidgets() /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Container.cpp:396
    #13 0x7f8c092e752e in tgui::BackendGui::removeAllWidgets() /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Backend/Window/BackendGui.cpp:297
    #14 0x55ff7c01af10 in GuiBuilder::loadEditingScreen(tgui::String const&) /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:888
    #15 0x55ff7c02e74b in GuiBuilder::createNewForm(tgui::String) /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:1332
    #16 0x55ff7c01719e in operator() /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:855
    #17 0x55ff7c08ae2a in __invoke_impl<void, GuiBuilder::loadStartScreen()::<lambda()>::<lambda(const tgui::String&)>&, const tgui::String&> /usr/include/c++/12.2.1/bits/invoke.h:61
    #18 0x55ff7c07b173 in __invoke_r<void, GuiBuilder::loadStartScreen()::<lambda()>::<lambda(const tgui::String&)>&, const tgui::String&> /usr/include/c++/12.2.1/bits/invoke.h:111
    #19 0x55ff7c071aba in _M_invoke /usr/include/c++/12.2.1/bits/std_function.h:290
    #20 0x55ff7c0d41b2 in std::function<void (tgui::String const&)>::operator()(tgui::String const&) const /usr/include/c++/12.2.1/bits/std_function.h:591
    #21 0x55ff7c016600 in operator() /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:828
    #22 0x55ff7c09cdb0 in __invoke_impl<void, const GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)>&, const tgui::String&> /usr/include/c++/12.2.1/bits/invoke.h:61
    #23 0x55ff7c094739 in __invoke<const GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)>&, const tgui::String&> /usr/include/c++/12.2.1/bits/invoke.h:96
    #24 0x55ff7c08ac5b in invoke<const GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)>&, const tgui::String&> /usr/include/c++/12.2.1/functional:110
    #25 0x55ff7c07aeb5 in invokeFunc<const GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)>&, const tgui::String&> /home/laurenz/Dokumente/Programmieren/C++/tgui/include/TGUI/Signal.hpp:252
    #26 0x55ff7c071883 in operator() /home/laurenz/Dokumente/Programmieren/C++/tgui/include/TGUI/Signal.hpp:721
    #27 0x55ff7c0adedb in __invoke_impl<void, tgui::SignalFileDialogPaths::connect<GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)> >(const GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)>&)::<lambda()>&> /usr/include/c++/12.2.1/bits/invoke.h:61
    #28 0x55ff7c0a2d80 in __invoke_r<void, tgui::SignalFileDialogPaths::connect<GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)> >(const GuiBuilder::showLoadFileWindow(const tgui::String&, const tgui::String&, bool, const tgui::String&, const std::function<void(const tgui::String&)>&)::<lambda(const tgui::String&)>&)::<lambda()>&> /usr/include/c++/12.2.1/bits/invoke.h:111
    #29 0x55ff7c09ce17 in _M_invoke /usr/include/c++/12.2.1/bits/std_function.h:290

previously allocated by thread T0 here:
    #0 0x7f8c09ac0672 in operator new(unsigned long) /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_new_delete.cpp:95
    #1 0x7f8c090d75c0 in std::__new_allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) /usr/include/c++/12.2.1/bits/new_allocator.h:137
    #2 0x7f8c090cddaa in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) /usr/include/c++/12.2.1/bits/alloc_traits.h:464
    #3 0x7f8c090c2cb7 in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<tgui::FileDialog, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&) /usr/include/c++/12.2.1/bits/allocated_ptr.h:98
    #4 0x7f8c090b3f2b in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<tgui::FileDialog, std::allocator<void>>(tgui::FileDialog*&, std::_Sp_alloc_shared_tag<std::allocator<void> >) /usr/include/c++/12.2.1/bits/shared_ptr_base.h:969
    #5 0x7f8c090a2a71 in std::__shared_ptr<tgui::FileDialog, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) /usr/include/c++/12.2.1/bits/shared_ptr_base.h:1712
    #6 0x7f8c0908cf6a in std::shared_ptr<tgui::FileDialog>::shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) /usr/include/c++/12.2.1/bits/shared_ptr.h:464
    #7 0x7f8c0907ae8f in std::shared_ptr<std::enable_if<!std::is_array<tgui::FileDialog>::value, tgui::FileDialog>::type> std::make_shared<tgui::FileDialog>() /usr/include/c++/12.2.1/bits/shared_ptr.h:1010
    #8 0x7f8c0902d16f in tgui::FileDialog::create(tgui::String const&, tgui::String const&) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widgets/FileDialog.cpp:285
    #9 0x55ff7c0167ee in GuiBuilder::showLoadFileWindow(tgui::String const&, tgui::String const&, bool, tgui::String const&, std::function<void (tgui::String const&)> const&) /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:817
    #10 0x55ff7c017480 in operator() /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:854
    #11 0x55ff7c0adf91 in __invoke_impl<void, GuiBuilder::loadStartScreen()::<lambda()>&> /usr/include/c++/12.2.1/bits/invoke.h:61
    #12 0x55ff7c0a2f66 in __invoke_r<void, GuiBuilder::loadStartScreen()::<lambda()>&> /usr/include/c++/12.2.1/bits/invoke.h:111
    #13 0x55ff7c09cf35 in _M_invoke /usr/include/c++/12.2.1/bits/std_function.h:290
    #14 0x7f8c0942f595 in std::function<void ()>::operator()() const /usr/include/c++/12.2.1/bits/std_function.h:591
    #15 0x7f8c094123b1 in tgui::Signal::emit(tgui::Widget const*) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Signal.cpp:93
    #16 0x7f8c0920b7aa in tgui::SignalTyped<tgui::Vector2<float> >::emit(tgui::Widget const*, tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/include/TGUI/Signal.hpp:361
    #17 0x7f8c0954c377 in tgui::Panel::leftMouseReleased(tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widgets/Panel.cpp:143
    #18 0x7f8c091d198a in tgui::Widget::mouseReleased(tgui::Event::MouseButton, tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widget.cpp:1289
    #19 0x7f8c095b4992 in tgui::Container::processMouseReleaseEvent(tgui::Event::MouseButton, tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Container.cpp:1166
    #20 0x7f8c095b1094 in tgui::Container::leftMouseReleased(tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Container.cpp:901
    #21 0x7f8c0954c3a3 in tgui::Panel::leftMouseReleased(tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widgets/Panel.cpp:145
    #22 0x7f8c091d198a in tgui::Widget::mouseReleased(tgui::Event::MouseButton, tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Widget.cpp:1289
    #23 0x7f8c095b4992 in tgui::Container::processMouseReleaseEvent(tgui::Event::MouseButton, tgui::Vector2<float>) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Container.cpp:1166
    #24 0x7f8c092e6a45 in tgui::BackendGui::handleEvent(tgui::Event) /home/laurenz/Dokumente/Programmieren/C++/tgui/src/Backend/Window/BackendGui.cpp:158
    #25 0x55ff7c00a94a in GuiBuilder::mainLoop() /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/GuiBuilder.cpp:446
    #26 0x55ff7bf24741 in main /home/laurenz/Dokumente/Programmieren/C++/tgui/gui-builder/src/main.cpp:70
    #27 0x7f8c0883c78f  (/usr/lib/libc.so.6+0x2378f)

SUMMARY: AddressSanitizer: heap-use-after-free /usr/include/c++/12.2.1/bits/hashtable.h:649 in std::_Hashtable<unsigned int, std::pair<unsigned int const, std::function<void ()> >, std::allocator<std::pair<unsigned int const, std::function<void ()> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::size() const
Shadow bytes around the buggy address:
  0x0c4280031680: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4280031690: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c42800316a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c42800316b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c42800316c0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c42800316d0: fd fd fd fd fd fd fd fd fd[fd]fd fd fd fd fd fd
  0x0c42800316e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c42800316f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4280031700: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4280031710: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4280031720: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==3811==ABORTING

After some more digging I found out, that the crash must occur because m_parent currently does not exist, and therefore a READ memory access occurs.
It would probably be a good idea to implement a check if something like this is the case, and then throw an error.

Why exactly m_parent is a nullptr in this example is still a mystery to me, but I'll try to find out why.

While I can't reproduce this, I think I know what goes wrong based on the call stack.

Inside GuiBuilder::showLoadFileWindow there is the following code:

fileDialog->onFileSelect([onLoad](const tgui::String& selectedFile){
    onLoad(selectedFile);
});

The onLoad function may call gui.removeAllWidgets() which would destroy the file dialog as well. After the onFileSelect callback finishes, the file dialog will attempt to close itself which would result in the "heap-use-after-free" error spotted by the sanitizer and a potential segmentation fault as it access memory which has already been deleted.

I've pushed a changed that hopefully fixes this. Can you check if it solves the crashes for you?

For the other issue you opened about the memory leak, I not sure yet where to start looking, but I'll get back to that later when I find sufficient time.

Yes, the crash doesn't occur anymore now. Thanks very much!

For the other issue you opened about the memory leak, I not sure yet where to start looking, but I'll get back to that later when I find sufficient time.

I'll try to look into it tomorrow again as well. I had some ideas to why the leak happens, but none of them confirmed themselves.