[CODE] Switch bug (long)

From: Juliano Ravasi Ferraz (jferraz@descalvado.linkway.com.br)
Date: 06/17/01

Warning: This message is somewhat long, it describes the steps to
reproduce a bug, and discusses a way to fix it.

      I have found a bug on CircleMUD 3.0 bpl18, envolving the
close_socket() and the SWITCH command.  Here is a simple way
to reproduce it (greetings and motds stripped):

First: find a mob to switch into.
     500H 100M 82V > go aglandiir
     [ 3375] The Lair [ INDOORS NO_TRACK PRIVATE ]
     The Dragon Prince, Aglandiir, sits here looking at you curiously.
     ...he glows with a bright light!

     500H 100M 82V > switch aglandiir
At this point, we break our connection.
     telnet> close
     Connection closed.
     Jun 16 21:38:30 :: WARNING: EOF on socket read (connection broken by
     Jun 16 21:38:30 :: Closing link to: God.
     Jun 16 21:38:30 :: No connections.  Going to sleep.
And reconnect...
     $ telnet localhost 5000
     Jun 16 21:38:32 :: New connection.  Waking up.
     Connected to localhost.
     Escape character is '^]'.


     By what name do you wish to be known? God
     Jun 16 21:38:35 :: God [localhost] has reconnected.


     500H 100M 82V > switch aglandiir
     You can't do that, the body is already in use!
Hugh?! Who is using its body???

     500H 100M 82V > purge aglandiir

     500H 100M 82V >

     Welcome to CircleMUD!
     0) Exit from CircleMUD.
     1) Enter the game.
     2) Enter description.
     3) Read the background story.
     4) Change password.
     5) Delete this character.

        Make your choice:
Mmmm... something is wrong... I have been sent to the menu...
        Make your choice: 1
     Connection closed by foreign host.
     [1]+  Segmentation fault      (core dumped) bin/circle 5000

      I have found the bug in the close_socket() function:

     if (d->character) {
        * Plug memory leak, from Eric Green.
       if (!IS_NPC(d->character) && PLR_FLAGGED(d->character, PLR_MAILING)
           && d->str) {
         if (*(d->str))
       if (STATE(d) == CON_PLAYING || STATE(d) == CON_DISCONNECT) {
         struct char_data *link_challenged = d->original ? d->original :

         /* We are guaranteed to have a person. */
         act("$n has lost $s link.", TRUE, link_challenged, 0, 0, TO_ROOM);
         save_char(link_challenged, NOWHERE);
         sprintf(buf, "Closing link to: %s.", GET_NAME(link_challenged));
         mudlog(buf, NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(link_challenged)),
         link_challenged->desc = NULL;
       } else {

      On the above case, d->original points to the God character,
d->character points to Aglandiir.  d->original->desc is already NULL,
because CircleMUD considers linkless a player while it is switched.
close_socket() sets link_challenged->desc to NULL, and leaves
d->character->desc pointing to the descriptor that will be freed on
the end of the function.

      Changing the code to...

         link_challenged->desc = NULL;
         if (d->original && d->original->desc == d)
        d->original->desc = NULL;
         if (d->character->desc == d)
        d->character->desc = NULL;

      ...fixed the bug for me.  Probably, there is no need for doing the
check and the clean on d->original->desc.  I couldn't be able to imagine
any circumstance where a character on d->original is linked to a
descriptor.  The SWITCH command does a 'ch->desc = NULL;' among other
things.  But a sanity check is better than running the risk.  The second
check, before doing the clean on d->character->desc, is a sanity check
too.  I think that is always expected that 'd->character->desc == d' and
'ch->desc->character == ch' on any normal circumstances.

Juliano R Ferraz

Captain ... one .. harmless ... little ... Tribble?
I'm at the corner of Walk and Don't Walk.

   | FAQ: http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html |
   | Archives: http://post.queensu.ca/listserv/wwwarch/circle.html |

This archive was generated by hypermail 2b30 : 12/05/01 PST