Error save score.xml Enigma-Game

Hi,

I’m currently doing some work to “flatpak” the enigma game. This is a private project to learn to work with flatpak-builder. The enigma project page explains, that they are happy with distributing their project, if I’m successful I’ll get in touch with the developer and ask for permission to publish on flathub.

I try to integrate the version 1.30 of enigma to flatpak. I’m on a good way and can build and run it without any problems.

But if I “win” a level or try to exit it over the menu, enigma crashes with a SIGABRT. The following output is generated with gdb:

/usr/include/c++/10.2.0/bits/stl_vector.h:1063: std::vector<_Tp, _Alloc>::const_reference std::vector<_Tp, _Alloc>::operator[](std::vector<_Tp, _Alloc>::size_type) const [with _Tp = unsigned char; _Alloc = std::allocator<unsigned char>; std::vector<_Tp, _Alloc>::const_reference = const unsigned char&; std::vector<_Tp, _Alloc>::size_type = long unsigned int]: Assertion '__builtin_expect(__n < this->size(), true)' failed.

Thread 1 "enigma" received signal SIGABRT, Aborted.
0x00007ffff74917f5 in raise () from /usr/lib/x86_64-linux-gnu/libc.so.6
(gdb) backtrace
#0  0x00007ffff74917f5 in raise () from /usr/lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff747a855 in abort () from /usr/lib/x86_64-linux-gnu/libc.so.6
#2  0x00005555557d5835 in std::__replacement_assert (__condition=0x5555557efdd8 "__builtin_expect(__n < this->size(), true)", 
    __function=0x5555557fa320 "std::vector<_Tp, _Alloc>::const_reference std::vector<_Tp, _Alloc>::operator[](std::vector<_Tp, _Alloc>::size_type) const [with _Tp = unsigned char; _Alloc = std::allocator<unsigned char>; std::vector"..., __line=1063, 
    __file=0x5555557eff68 "/usr/include/c++/10.2.0/bits/stl_vector.h")
    at /usr/include/c++/10.2.0/x86_64-unknown-linux-gnu/bits/c++config.h:457
#3  std::vector<unsigned char, std::allocator<unsigned char> >::operator[] (this=<optimized out>, __n=0)
    at /usr/include/c++/10.2.0/bits/stl_vector.h:1063
#4  std::vector<unsigned char, std::allocator<unsigned char> >::operator[] (__n=0, this=<optimized out>)
    at /usr/include/c++/10.2.0/bits/stl_vector.h:1061
#5  zipios::writeByteSeq (vec=std::vector of length 0, capacity 0, vec=std::vector of length 0, capacity 0, os=...)
    at ./../zipios++/zipheadio.h:153
#6  zipios::operator<< (os=..., zlh=...) at zipheadio.cpp:114
#7  0x00005555557d8938 in zipios::ZipOutputStreambuf::putNextEntry (this=this@entry=0x7fffffffc340, entry=...)
    at zipoutputstreambuf.cpp:77
#8  0x0000555555626fe9 in enigma::writeToZip (zipStream=..., filename="score.xml", size=size@entry=1512, contents=...)
    at file_zip.cc:67
#9  0x000055555573d4be in enigma::lev::ScoreManager::save (this=0x55555838e8d0) at lev/ScoreManager.cc:460
#10 0x0000555555606dd1 in enigma::client::Client::level_finished (
    this=0x55555592eb60 <enigma::client::(anonymous namespace)::client_instance>) at client.cc:810
#11 0x00005555556072b3 in enigma::client::Msg_Command (cmd=...) at client.cc:937
#12 0x0000555555679bb7 in enigma::server::Tick (dtime=dtime@entry=0.01) at server.cc:368
#13 0x0000555555629d81 in enigma::game::StartGame () at game.cc:83
#14 0x00005555556ca85d in enigma::gui::LevelMenu::on_action (this=0x7fffffffd2d0, w=<optimized out>) at gui/LevelMenu.cc:281
#15 0x00005555556dc98a in enigma::gui::LevelWidget::trigger_action (this=this@entry=0x555555f45990) at gui/LevelWidget.cc:104
#16 0x00005555556de8c8 in enigma::gui::LevelWidget::handle_mousedown (this=0x555555f45990, e=<optimized out>)
    at gui/LevelWidget.cc:462
#17 0x00005555556decc6 in enigma::gui::LevelWidget::on_event (this=0x555555f45990, e=...) at gui/LevelWidget.cc:424
#18 0x00005555556e6950 in enigma::gui::Menu::handle_event (this=this@entry=0x7fffffffd2d0, e=...) at gui/Menu.cc:169
#19 0x00005555556e6c83 in enigma::gui::Menu::manage (this=this@entry=0x7fffffffd2d0) at gui/Menu.cc:96
--Type <RET> for more, q to quit, c to continue without paging--
#20 0x00005555556d8b5d in enigma::gui::LevelPackMenu::manageLevelMenu (this=this@entry=0x7fffffffd6f0) at gui/LevelPackMenu.cc:437
#21 0x00005555556e5587 in enigma::gui::MainMenu::on_action (this=0x7fffffffda70, w=<optimized out>) at gui/MainMenu.cc:559
#22 0x00005555556f305a in enigma::gui::Widget::invoke_listener (this=this@entry=0x5555580fdd30) at gui/widgets.cc:63
#23 0x00005555556f5396 in enigma::gui::PushButton::on_event (this=0x5555580fdd30, e=...) at gui/widgets.cc:756
#24 0x00005555556e6950 in enigma::gui::Menu::handle_event (this=this@entry=0x7fffffffda70, e=...) at gui/Menu.cc:169
#25 0x00005555556e6c83 in enigma::gui::Menu::manage (this=this@entry=0x7fffffffda70) at gui/Menu.cc:96
#26 0x00005555556e5369 in enigma::gui::ShowMainMenu () at gui/MainMenu.cc:623
#27 0x00005555555eddae in main (argc=<optimized out>, argv=<optimized out>) at main.cc:994

First version of flatpak manifest can be found GitHub - MrTarantoga/flathub at org.nongnu.enigma.

So what is the problem? Enigma tries to write the “score.xml” if you win a level or exit the game. But as you see in the gdb output a SIGABRT and the score.xml not written. I’ve built the same enigma version on a fresh debian virtual machine and the same error does not occur, I could play the game without any errors.

I’m pretty sure I’ve done some sandbox configuration errors, but I’ve no idea what could possible go wrong. Does anyone see a soultion or can help me?

Thank you
– Tarantoga

There is likely a bug in upstream where it trigger an assert in newer version of the C++ library. Which version of gcc do you have on the debian system? It’s probably older than what the freedesktop sdk has.

Looking closer at the stack trace, it tries tries to access an element on an empty vector (size 0, capacity 0).

Newer C++ libraries will assert. This is wrong in any case.

Well, it compiles on the new Debian Bullsey with gcc 10.2, and it runs without any problems.
I also tried it with the SDK version 19.08, 21.08 and 18.08 and the same error occurred.

Well, in my opinion try to write an empty container should not throw any kind of error, in case is just does write nothing. But this is a huge runtime difference between flatpak and the standard debian distribution. On a short overview of the code section, I do not see any kind of problem which could rise to assert. But at the weekend I will investigate it more carefully.

Believe what you want, but accessing any element in an empty vector in C++ is a bug. operator[] doesn’t check bounds - it’s in the documentation.

Not sure why it doesn’t crash outside of flatpak.

Reading the code further, it triggers the assert in flatpak (good) but otherwise since it never accesses the refrenced memory, then it behave like it didn’t happen.

Definitely a bug.

Now if the code used vec.data() instead of &vec[0], it would work.

1 Like

Yeah of course, but that is not what I’m talking about. It is pretty clear that this is code smell from hell and nobody should do that in this anymore. So more precisely, from my side:

#5  zipios::writeByteSeq (vec=std::vector of length 0, capacity 0, vec=std::vector of length 0, capacity 0, os=...)
    at ./../zipios++/zipheadio.h:153
#6  zipios::operator<< (os=..., zlh=...) at zipheadio.cpp:114
#7  0x00005555557d8938 in zipios::ZipOutputStreambuf::putNextEntry (this=this@entry=0x7fffffffc340, entry=...)
    at zipoutputstreambuf.cpp:77
#8  0x0000555555626fe9 in enigma::writeToZip (zipStream=..., filename="score.xml", size=size@entry=1512, contents=...)
    at file_zip.cc:67

Looking in the code, they try to save the score.xml. But by injecting a whole object not doing some crazy pointer stuff. The critical code section is:

inline void writeByteSeq ( ostream &os, const vector < unsigned char > &vec ) {
  os.rdbuf()->sputn( reinterpret_cast< const char * >( &( vec[ 0 ] ) ), vec.size() ) ;
}

The reinterpret_cast does only make the array entry point available for the sputn function. This memory must exists, because it holds the pointer to the heap with the array.

The only aspect, that might be asserted the compiler first try to dereference the invalid pointer and next to read the address of the memory that is dereferenced. Yeah, but this sounds crazy.

In the next step, sputn should check the size before it tries to read. I do not know if the implementation will do so, but I hope.

Do I miss something?

Cheers
–Tarantoga

I will try to fix it. Thank you for your advice.

as I said the assert is in operator[]