• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

C++ Crash Party TFS 0.4 (8.60)

Solution
I decided to release that bug in TFS 0.4 (and probably many other distros, not tested it on any other).

YOU CAN INVITE YOURSELF TO PARTY! - WHEN YOU ARE NOT A MEMBER OF ANY PARTY!

After reading gdb I found out that guy was invited to 24 parties:
PHP:
#5 0x000000000057eec4 in Player::clearPartyInvitations (this=this@entry=0x7f41e84b7eb0) at player.cpp:4609
it =
list = std::list = {[0] = 0x7f41c6fefda0, [1] = 0x7f41c13b6f20, [2] = 0x7f41c122dad0, [3] = 0x7f41c121a630, [4] = 0x7f41c1358e60, [5] = 0x7f41c6c3dfd0, [6] = 0x7f41c6938620, [7] = 0x7f4181a12590,
[8] = 0x7f4189518820, [9] = 0x7f418a1513d0, [10] = 0x7f4189377810, [11] = 0x7f418932aff0, [12] = 0x7f418a44af40...
This log seems kind useful. From what I've understood from gdb backtrace, its happening onLogout, when function clearPartyInvitations is called.

Going into the detail, this is the last function called before the crash:
Code:
bool Party::isPlayerInvited(const Player* player, bool result/* = false*/) const
{
    if(!player || player->isRemoved())
        return result;

    return std::find(inviteList.begin(), inviteList.end(), player) != inviteList.end();
}

The last line of this function is causing the crash (std::find). I'm not really expert on this. Maybe someone with more experience with c++ can debug this?
 
This log seems kind useful. From what I've understood from gdb backtrace, its happening onLogout, when function clearPartyInvitations is called.

Going into the detail, this is the last function called before the crash:
Code:
bool Party::isPlayerInvited(const Player* player, bool result/* = false*/) const
{
    if(!player || player->isRemoved())
        return result;

    return std::find(inviteList.begin(), inviteList.end(), player) != inviteList.end();
}

The last line of this function is causing the crash (std::find). I'm not really expert on this. Maybe someone with more experience with c++ can debug this?

Thanks for the tip and this will help in the search for the solution!
 
This log seems kind useful. From what I've understood from gdb backtrace, its happening onLogout, when function clearPartyInvitations is called.

Going into the detail, this is the last function called before the crash:
Code:
bool Party::isPlayerInvited(const Player* player, bool result/* = false*/) const
{
    if(!player || player->isRemoved())
        return result;

    return std::find(inviteList.begin(), inviteList.end(), player) != inviteList.end();
}

The last line of this function is causing the crash (std::find). I'm not really expert on this. Maybe someone with more experience with c++ can debug this?
the std::find function seems to be behaving correctly.. i suppose it's not actually the problem..looking closely at the gdb.. i found this

Code:
#7  0x0000555555697df3 in Game::removeCreature (this=0x555555a740a0 <g_game>, creature=0x5555780f6000, isLogout=isLogout@entry=true) at game.cpp:1032
        tile = 0x5555ee7efc00
        list = std::unordered_set with 9 elements = {[0] = 0x555564b19000, [1] = 0x55556c98b800, [2] = 0x55556646f000, [3] = 0x5555688fa800, [4] = 0x55556b169800,
          [5] = 0x5555709e6000, [6] = 0x5555780f6000, [7] = 0x555567611000, [8] = 0x5555636d9800}
        it = {<std::__detail::_Node_iterator_base<Creature*, false>> = {_M_cur = 0x555579d82f20}, <No data fields>}
        player = <optimized out>
        oldStackPosVector = std::vector of length 23448906743891, capacity 23448725278603 = {
          <error reading variable oldStackPosVector (Cannot access memory at address 0x701e320f3)>
        oldIndex = <optimized out>
        i = <optimized out>

Error reading variable from vector.. i guess it went outside of vector bounds at line 996(I'm using 3774 .. so there might be slight differences)
in this Line exactly
Code:
player->sendCreatureDisappear(creature, oldStackPosVector[i]);
You could try changing this function
Code:
bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)

To
Code:
bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)
{
    if(creature->isRemoved())
        return false;

    Tile* tile = creature->getTile();
    SpectatorVec list;
    getSpectators(list, tile->getPosition(), false, true);

    SpectatorVec::iterator it;
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureDisappear(creature, isLogout);

    if(tile != creature->getTile())
    {
        list.clear();
        tile = creature->getTile();
        getSpectators(list, tile->getPosition(), false, true);
    }

    Player* player = NULL;
    std::vector<uint32_t> oldStackPosVector;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()) && player->canSeeCreature(creature))
            oldStackPosVector.push_back(tile->getClientIndexOfThing(player, creature));
    }

    int32_t oldIndex = tile->__getIndexOfThing(creature);
    if(!map->removeCreature(creature))
        return false;

    //send to client
    uint32_t i = 0;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if(creature != (*it))
            (*it)->updateTileCache(tile);

        if(!(player = (*it)->getPlayer()) || !player->canSeeCreature(creature))
            continue;
        if(i < oldStackPosVector.size())
            player->sendCreatureDisappear(creature, oldStackPosVector[i]);
        ++i;
    }

    creature->getParent()->postRemoveNotification(NULL, creature, NULL, oldIndex, true);
    creature->onRemovedCreature();

    autoList.erase(creature->getID());
    freeThing(creature);

    removeCreatureCheck(creature);
    for(std::list<Creature*>::iterator it = creature->summons.begin(); it != creature->summons.end(); ++it)
        removeCreature(*it);

    return true;
}


And Hopefully it works :((
 
the std::find function seems to be behaving correctly.. i suppose it's not actually the problem..looking closely at the gdb.. i found this

Code:
#7  0x0000555555697df3 in Game::removeCreature (this=0x555555a740a0 <g_game>, creature=0x5555780f6000, isLogout=isLogout@entry=true) at game.cpp:1032
        tile = 0x5555ee7efc00
        list = std::unordered_set with 9 elements = {[0] = 0x555564b19000, [1] = 0x55556c98b800, [2] = 0x55556646f000, [3] = 0x5555688fa800, [4] = 0x55556b169800,
          [5] = 0x5555709e6000, [6] = 0x5555780f6000, [7] = 0x555567611000, [8] = 0x5555636d9800}
        it = {<std::__detail::_Node_iterator_base<Creature*, false>> = {_M_cur = 0x555579d82f20}, <No data fields>}
        player = <optimized out>
        oldStackPosVector = std::vector of length 23448906743891, capacity 23448725278603 = {
          <error reading variable oldStackPosVector (Cannot access memory at address 0x701e320f3)>
        oldIndex = <optimized out>
        i = <optimized out>

Error reading variable from vector.. i guess it went outside of vector bounds at line 996(I'm using 3774 .. so there might be slight differences)
in this Line exactly
Code:
player->sendCreatureDisappear(creature, oldStackPosVector[i]);
You could try changing this function
Code:
bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)

To
Code:
bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)
{
    if(creature->isRemoved())
        return false;

    Tile* tile = creature->getTile();
    SpectatorVec list;
    getSpectators(list, tile->getPosition(), false, true);

    SpectatorVec::iterator it;
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureDisappear(creature, isLogout);

    if(tile != creature->getTile())
    {
        list.clear();
        tile = creature->getTile();
        getSpectators(list, tile->getPosition(), false, true);
    }

    Player* player = NULL;
    std::vector<uint32_t> oldStackPosVector;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()) && player->canSeeCreature(creature))
            oldStackPosVector.push_back(tile->getClientIndexOfThing(player, creature));
    }

    int32_t oldIndex = tile->__getIndexOfThing(creature);
    if(!map->removeCreature(creature))
        return false;

    //send to client
    uint32_t i = 0;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if(creature != (*it))
            (*it)->updateTileCache(tile);

        if(!(player = (*it)->getPlayer()) || !player->canSeeCreature(creature))
            continue;
        if(i < oldStackPosVector.size())
            player->sendCreatureDisappear(creature, oldStackPosVector[i]);
        ++i;
    }

    creature->getParent()->postRemoveNotification(NULL, creature, NULL, oldIndex, true);
    creature->onRemovedCreature();

    autoList.erase(creature->getID());
    freeThing(creature);

    removeCreatureCheck(creature);
    for(std::list<Creature*>::iterator it = creature->summons.begin(); it != creature->summons.end(); ++it)
        removeCreature(*it);

    return true;
}


And Hopefully it works :((

Hello! I migrated my entire party system to TFS 1.3 and I'm testing it, if I do not solve it I'll use your suggestion, thank you very much for your response and attention!
 
the std::find function seems to be behaving correctly.. i suppose it's not actually the problem..looking closely at the gdb.. i found this

Code:
#7  0x0000555555697df3 in Game::removeCreature (this=0x555555a740a0 <g_game>, creature=0x5555780f6000, isLogout=isLogout@entry=true) at game.cpp:1032
        tile = 0x5555ee7efc00
        list = std::unordered_set with 9 elements = {[0] = 0x555564b19000, [1] = 0x55556c98b800, [2] = 0x55556646f000, [3] = 0x5555688fa800, [4] = 0x55556b169800,
          [5] = 0x5555709e6000, [6] = 0x5555780f6000, [7] = 0x555567611000, [8] = 0x5555636d9800}
        it = {<std::__detail::_Node_iterator_base<Creature*, false>> = {_M_cur = 0x555579d82f20}, <No data fields>}
        player = <optimized out>
        oldStackPosVector = std::vector of length 23448906743891, capacity 23448725278603 = {
          <error reading variable oldStackPosVector (Cannot access memory at address 0x701e320f3)>
        oldIndex = <optimized out>
        i = <optimized out>

Error reading variable from vector.. i guess it went outside of vector bounds at line 996(I'm using 3774 .. so there might be slight differences)
in this Line exactly
Code:
player->sendCreatureDisappear(creature, oldStackPosVector[i]);
You could try changing this function
Code:
bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)

To
Code:
bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)
{
    if(creature->isRemoved())
        return false;

    Tile* tile = creature->getTile();
    SpectatorVec list;
    getSpectators(list, tile->getPosition(), false, true);

    SpectatorVec::iterator it;
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureDisappear(creature, isLogout);

    if(tile != creature->getTile())
    {
        list.clear();
        tile = creature->getTile();
        getSpectators(list, tile->getPosition(), false, true);
    }

    Player* player = NULL;
    std::vector<uint32_t> oldStackPosVector;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()) && player->canSeeCreature(creature))
            oldStackPosVector.push_back(tile->getClientIndexOfThing(player, creature));
    }

    int32_t oldIndex = tile->__getIndexOfThing(creature);
    if(!map->removeCreature(creature))
        return false;

    //send to client
    uint32_t i = 0;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if(creature != (*it))
            (*it)->updateTileCache(tile);

        if(!(player = (*it)->getPlayer()) || !player->canSeeCreature(creature))
            continue;
        if(i < oldStackPosVector.size())
            player->sendCreatureDisappear(creature, oldStackPosVector[i]);
        ++i;
    }

    creature->getParent()->postRemoveNotification(NULL, creature, NULL, oldIndex, true);
    creature->onRemovedCreature();

    autoList.erase(creature->getID());
    freeThing(creature);

    removeCreatureCheck(creature);
    for(std::list<Creature*>::iterator it = creature->summons.begin(); it != creature->summons.end(); ++it)
        removeCreature(*it);

    return true;
}


And Hopefully it works :((

Yeah, as I said I'm not expert on this =)

Also thought about this one, but thought that something at the top of gdb log is causing this. But then I looked into newest rev of TFS 0.3, which I'm using personally, and the function looked exactly the same, and I never noticed such a crash.
 
GDB investigation example with party crash on xavato.eu

1. First start debuging with gdb ('xavato' is binary file, coreParty is crash file):
PHP:
gdb xavato coreParty

Result:
Reading symbols from xavato...done.
[New LWP 31439]
[New LWP 31437]
[New LWP 31441]
[New LWP 31463]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `./xavato'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 std::__find_if<__gnu_cxx::__normal_iterator<Player* const*, std::vector<Player*, std::allocator<Player*> > >, __gnu_cxx::__ops::_Iter_equals_val<Player const* const> > (__first=...,
__last=__last@entry=..., __pred=__pred@entry=...) at /usr/include/c++/4.9/bits/stl_algo.h:120
120 if (__pred(__first))

2. Generate stack trace:
PHP:
bt full

and we get a lot of creepy variables that got no sense:
#0 std::__find_if<__gnu_cxx::__normal_iterator<Player* const*, std::vector<Player*, std::allocator<Player*> > >, __gnu_cxx::__ops::_Iter_equals_val<Player const* const> > (
__first=<error reading variable: Cannot access memory at address 0x6073726579616c70>, __last=__last@entry=<error reading variable: Cannot access memory at address 0x6e6f602054455320>,
__pred=__pred@entry=...) at /usr/include/c++/4.9/bits/stl_algo.h:120
__trip_count = 31489385263734581
#1 0x0000000000572b8b in __find_if<__gnu_cxx::__normal_iterator<Player* const*, std::vector<Player*> >, __gnu_cxx::__ops::_Iter_equals_val<Player const* const> > (__pred=..., __last=..., __first=...)
at /usr/include/c++/4.9/bits/stl_algo.h:162
No locals.
#2 find<__gnu_cxx::__normal_iterator<Player* const*, std::vector<Player*> >, Player const*> (__val=<optimized out>, __last=<error reading variable: Cannot access memory at address 0x6e6f602054455320>,
__first=...) at /usr/include/c++/4.9/bits/stl_algo.h:3779
No locals.
#3 Party::isPlayerInvited (this=this@entry=0x7f41c6938620, player=player@entry=0x7f41e84b7eb0, result=result@entry=false) at party.cpp:439
No locals.
#4 0x0000000000572bc5 in Party::removeInvite (this=0x7f41c6938620, player=player@entry=0x7f41e84b7eb0) at party.cpp:173
it = <optimized out>
#5 0x000000000057eec4 in Player::clearPartyInvitations (this=this@entry=0x7f41e84b7eb0) at player.cpp:4609
it =
list = std::list = {[0] = 0x7f41c6fefda0, [1] = 0x7f41c13b6f20, [2] = 0x7f41c122dad0, [3] = 0x7f41c121a630, [4] = 0x7f41c1358e60, [5] = 0x7f41c6c3dfd0, [6] = 0x7f41c6938620, [7] = 0x7f4181a12590,
[8] = 0x7f4189518820, [9] = 0x7f418a1513d0, [10] = 0x7f4189377810, [11] = 0x7f418932aff0, [12] = 0x7f418a44af40, [13] = 0x7f418a4131a0, [14] = 0x7f418a47a900, [15] = 0x7f418a0c69b0,
[16] = 0x7f4188cf4e40, [17] = 0x7f419479e900, [18] = 0x7f4189a86f60, [19] = 0x7f4189a6b340, [20] = 0x7f4192ba24d0, [21] = 0x7f4189a35830, [22] = 0x7f41889dfd60, [23] = 0x7f41929a91a0}
#6 0x0000000000582b0e in Player::eek:nCreatureDisappear (this=0x7f41e84b7eb0, creature=0x7f41e84b7eb0, isLogout=<optimized out>, setOnline=<optimized out>) at player.cpp:1444
saved = <optimized out>
#7 0x00000000004a2abe in Game::removeCreature (this=0x8e76c0 <g_game>, creature=0x7f41e84b7eb0, isLogout=isLogout@entry=true, setOnline=setOnline@entry=true) at game.cpp:1022
tile = 0x7f41d7ecc6a0
list = std::list = {[0] = 0x7f41ee83d810, [1] = 0x7f41ee8423b0, [2] = 0x7f41ef2a2130, [3] = 0x7f41ee847450, [4] = 0x7f41fa0721d0, [5] = 0x7f41e84b7eb0, [6] = 0x7f41fa738f20, [7] = 0x7f41fa2bd9d0,
[8] = 0x7f41ee16ed20, [9] = 0x7f41ee1767b0, [10] = 0x7f41e827b530, [11] = 0x7f41f9caf120, [12] = 0x7f41ef2a1ad0, [13] = 0x7f41ef2a2770, [14] = 0x7f41e850c740, [15] = 0x7f41efda0ce0,
[16] = 0x7f41ee17cdd0, [17] = 0x7f41fa0b1360, [18] = 0x7f41e81dcb30, [19] = 0x7f41ef2a3360, [20] = 0x7f41ef2a2dd0, [21] = 0x7f41ee858410, [22] = 0x7f41ee85c980, [23] = 0x7f41ee85e040,
[24] = 0x7f41ee85e5f0, [25] = 0x7f41ee85da90}
i = <optimized out>
it =
player = <optimized out>
oldStackPosVector = std::vector of length -7529021625, capacity -7526581769 = {<error reading variable oldStackPosVector (Cannot access memory at address 0x7030f02e6)>
oldIndex = <optimized out>
but..

Lets start from Frames #0-#2:
#0 std::__find_if<__gnu_cxx::__normal_iterator<Player* const*, std::vector<Player*, std::allocator<Player*> > >, __gnu_cxx::__ops::_Iter_equals_val<Player const* const> > (
__first=<error reading variable: Cannot access memory at address 0x6073726579616c70>, __last=__last@entry=<error reading variable: Cannot access memory at address 0x6e6f602054455320>,
__pred=__pred@entry=...) at /usr/include/c++/4.9/bits/stl_algo.h:120
__trip_count = 31489385263734581
#1 0x0000000000572b8b in __find_if<__gnu_cxx::__normal_iterator<Player* const*, std::vector<Player*> >, __gnu_cxx::__ops::_Iter_equals_val<Player const* const> > (__pred=..., __last=..., __first=...)
at /usr/include/c++/4.9/bits/stl_algo.h:162
No locals.
#2 find<__gnu_cxx::__normal_iterator<Player* const*, std::vector<Player*> >, Player const*> (__val=<optimized out>, __last=<error reading variable: Cannot access memory at address 0x6e6f602054455320>,
__first=...) at /usr/include/c++/4.9/bits/stl_algo.h:3779
No locals.
It's some C++ stl_algo.h library, not part of TFS, we are in most cases not able to fix it and in 99.999% it's related to 'wrong use' of functions (read not existing list), not bug in C++ basic libraries.

Frame #3:
#3 Party::isPlayerInvited (this=this@entry=0x7f41c6938620, player=player@entry=0x7f41e84b7eb0, result=result@entry=false) at party.cpp:439
No locals.
Here is some TFS function! Now we can check what is in sources party.cpp function Party::isPlayerInvited:
PHP:
bool Party::isPlayerInvited(const Player* player, bool result/* = false*/) const
First variable is Player. We can check who is was. First copy player memory address from gdb:
0x7f41e84b7eb0
Now assign that address to variable in gdb and cast it to Player* type:
PHP:
p (Player*) 0x7f41e84b7eb0
Result is:
$1 = (Player *) 0x7f41e84b7eb0
Now we can check what variables are available in sources player.h, it extends creature.h and there is... name. We can print it:
PHP:
print $1.name
Result is:
$2 = "Summtingwong"

After short investigation I know that player who crashed server is Summtingwong. He could do it to clone items or he could crash without intention.

In your case he did it to clone items. You must contact that player and pay him for information what he did to crash server.

In case of Xavato server I had movies from Server Cam System ( https://www.xavato.eu/?subtopic=cams ) which record movies for all players online. I just watched movie and found what he did and why it crashed server.
 
I decided to release that bug in TFS 0.4 (and probably many other distros, not tested it on any other).

YOU CAN INVITE YOURSELF TO PARTY! - WHEN YOU ARE NOT A MEMBER OF ANY PARTY!

After reading gdb I found out that guy was invited to 24 parties:
PHP:
#5 0x000000000057eec4 in Player::clearPartyInvitations (this=this@entry=0x7f41e84b7eb0) at player.cpp:4609
it =
list = std::list = {[0] = 0x7f41c6fefda0, [1] = 0x7f41c13b6f20, [2] = 0x7f41c122dad0, [3] = 0x7f41c121a630, [4] = 0x7f41c1358e60, [5] = 0x7f41c6c3dfd0, [6] = 0x7f41c6938620, [7] = 0x7f4181a12590,
[8] = 0x7f4189518820, [9] = 0x7f418a1513d0, [10] = 0x7f4189377810, [11] = 0x7f418932aff0, [12] = 0x7f418a44af40, [13] = 0x7f418a4131a0, [14] = 0x7f418a47a900, [15] = 0x7f418a0c69b0,
[16] = 0x7f4188cf4e40, [17] = 0x7f419479e900, [18] = 0x7f4189a86f60, [19] = 0x7f4189a6b340, [20] = 0x7f4192ba24d0, [21] = 0x7f4189a35830, [22] = 0x7f41889dfd60, [23] = 0x7f41929a91a0}

I also knew his name from gdb, so I watched movie (CamSystem) and saw these messages:
PHP:
10:07 Summtingwong has been invited. Open the party channel to communicate with your members.
10:07 Summtingwong has invited you to his party.
10:07 You have left the party.
10:07 Summtingwong has left the party.

After few checks I found out that function playerInviteToParty (in game.cpp) creates new Party (when you invite first player to it), but it does not check, if invited player is not an inviting player.

To invite 'yourself' you need to use OTClient, Elfbot or some other bot. There is no option 'Invite to party' when you click on yourself in Tibia client, but with bot you can send any 'packet'.
That's why no one found that bug in past 7 years (since 0.4 release).

Code you need to add this (in 0.4 r3777):
PHP:
    if (invitedPlayer == player)
        return false;
Add it above:
PHP:
Party* party = player->getParty();

-----------------------
I've checked TFS 1.x sources and there is also no check for this (invite yourself):
otland/forgottenserver
Maybe there is other 'logout' procedure, that will remove invalid invitations without crash, but I got no server/bot to test it.
-----------------------

IS MY SERVER SAFE?
To check, if your server is safe, you need to write some bot that invite yourself and then disband party.
After few seconds of spam (invite -> disband -> invite -> ...), you just need to logout. If you server is bugged, it will crash.


EDIT:
gdb
from first post of thread:
#5 0x00005555557b1004 in Player::clearPartyInvitations (this=this@entry=0x5555b481e000) at player.cpp:5520
it = 0x55555787f500
list = std::__cxx11::list = {[0] = 0x55555789ef80, [1] = 0x55555787f500}
Guy is invited to 2 parties, probably he was a leader of both and that's why it crashed.

Best answer? :D
 
Last edited:
Solution
I decided to release that bug in TFS 0.4 (and probably many other distros, not tested it on any other).

YOU CAN INVITE YOURSELF TO PARTY! - WHEN YOU ARE NOT A MEMBER OF ANY PARTY!

After reading gdb I found out that guy was invited to 24 parties:
PHP:
#5 0x000000000057eec4 in Player::clearPartyInvitations (this=this@entry=0x7f41e84b7eb0) at player.cpp:4609
it =
list = std::list = {[0] = 0x7f41c6fefda0, [1] = 0x7f41c13b6f20, [2] = 0x7f41c122dad0, [3] = 0x7f41c121a630, [4] = 0x7f41c1358e60, [5] = 0x7f41c6c3dfd0, [6] = 0x7f41c6938620, [7] = 0x7f4181a12590,
[8] = 0x7f4189518820, [9] = 0x7f418a1513d0, [10] = 0x7f4189377810, [11] = 0x7f418932aff0, [12] = 0x7f418a44af40, [13] = 0x7f418a4131a0, [14] = 0x7f418a47a900, [15] = 0x7f418a0c69b0,
[16] = 0x7f4188cf4e40, [17] = 0x7f419479e900, [18] = 0x7f4189a86f60, [19] = 0x7f4189a6b340, [20] = 0x7f4192ba24d0, [21] = 0x7f4189a35830, [22] = 0x7f41889dfd60, [23] = 0x7f41929a91a0}

I also knew his name from gdb, so I watched movie (CamSystem) and saw these messages:
PHP:
10:07 Summtingwong has been invited. Open the party channel to communicate with your members.
10:07 Summtingwong has invited you to his party.
10:07 You have left the party.
10:07 Summtingwong has left the party.

After few checks I found out that function playerInviteToParty (in game.cpp) creates new Party (when you invite first player to it), but it does not check, if invited player is not an inviting player.

To invite 'yourself' you need to use OTClient, Elfbot or some other bot. There is no option 'Invite to party' when you click on yourself in Tibia client, but with bot you can send any 'packet'.
That's why no one found that bug in past 7 years (since 0.4 release).

Code you need to add is (in 0.4 r3777):
PHP:
    if (invitePlayer == player)
        return false;
Add it above:
PHP:
Party* party = player->getParty();

-----------------------
I've checked TFS 1.x sources and there is also no check for this (invite yourself):
otland/forgottenserver
Maybe there is other 'logout' procedure, that will remove invalid invitations without crash, but I got no server/bot to test it.
-----------------------

IS MY SERVER SAFE?
To check, if your server is safe, you need to write some bot that invite yourself and then disband party.
After few seconds of spam (invite -> disband -> invite -> ...), you just need to logout. If you server is bugged, it will crash.


EDIT:
gdb
from first post of thread:

Guy is invited to 2 parties, probably he was a leader of both and that's why it crashed.

Best answer? :D
Great explanation, congratulations!
 
I decided to release that bug in TFS 0.4 (and probably many other distros, not tested it on any other).

YOU CAN INVITE YOURSELF TO PARTY! - WHEN YOU ARE NOT A MEMBER OF ANY PARTY!

After reading gdb I found out that guy was invited to 24 parties:
PHP:
#5 0x000000000057eec4 in Player::clearPartyInvitations (this=this@entry=0x7f41e84b7eb0) at player.cpp:4609
it =
list = std::list = {[0] = 0x7f41c6fefda0, [1] = 0x7f41c13b6f20, [2] = 0x7f41c122dad0, [3] = 0x7f41c121a630, [4] = 0x7f41c1358e60, [5] = 0x7f41c6c3dfd0, [6] = 0x7f41c6938620, [7] = 0x7f4181a12590,
[8] = 0x7f4189518820, [9] = 0x7f418a1513d0, [10] = 0x7f4189377810, [11] = 0x7f418932aff0, [12] = 0x7f418a44af40, [13] = 0x7f418a4131a0, [14] = 0x7f418a47a900, [15] = 0x7f418a0c69b0,
[16] = 0x7f4188cf4e40, [17] = 0x7f419479e900, [18] = 0x7f4189a86f60, [19] = 0x7f4189a6b340, [20] = 0x7f4192ba24d0, [21] = 0x7f4189a35830, [22] = 0x7f41889dfd60, [23] = 0x7f41929a91a0}

I also knew his name from gdb, so I watched movie (CamSystem) and saw these messages:
PHP:
10:07 Summtingwong has been invited. Open the party channel to communicate with your members.
10:07 Summtingwong has invited you to his party.
10:07 You have left the party.
10:07 Summtingwong has left the party.

After few checks I found out that function playerInviteToParty (in game.cpp) creates new Party (when you invite first player to it), but it does not check, if invited player is not an inviting player.

To invite 'yourself' you need to use OTClient, Elfbot or some other bot. There is no option 'Invite to party' when you click on yourself in Tibia client, but with bot you can send any 'packet'.
That's why no one found that bug in past 7 years (since 0.4 release).

Code you need to add is (in 0.4 r3777):
PHP:
    if (invitePlayer == player)
        return false;
Add it above:
PHP:
Party* party = player->getParty();

-----------------------
I've checked TFS 1.x sources and there is also no check for this (invite yourself):
otland/forgottenserver
Maybe there is other 'logout' procedure, that will remove invalid invitations without crash, but I got no server/bot to test it.
-----------------------

IS MY SERVER SAFE?
To check, if your server is safe, you need to write some bot that invite yourself and then disband party.
After few seconds of spam (invite -> disband -> invite -> ...), you just need to logout. If you server is bugged, it will crash.


EDIT:
gdb
from first post of thread:

Guy is invited to 2 parties, probably he was a leader of both and that's why it crashed.

Best answer? :D

Thank you very much for helping, you explained everything in detail, simply thank you!
 
Back
Top