Welcome to the Builder Academy

Question another core dump not long after the one earlier

More
20 Dec 2024 23:01 - 20 Dec 2024 23:20 #10478 by JTP
I really hope that you can see what it wrong, as it is very annoying that someone want to crash the mud. And does it so often.

my QCLASS is posted in the previous message.
Last edit: 20 Dec 2024 23:20 by JTP.

Please Log in or Create an account to join the conversation.

More
21 Dec 2024 20:51 - 21 Dec 2024 20:52 #10480 by thomas
I think we'll need to see the whole nanny() function to help.
I've tried replicating your crash on a stock nanny() and it doesn't happen no matter what I throw at it.
Last edit: 21 Dec 2024 20:52 by thomas.

Please Log in or Create an account to join the conversation.

More
21 Dec 2024 21:00 #10481 by JTP
/* deal with newcomers and other non-playing sockets */
void nanny(struct descriptor_data *d, char *arg)
{
int load_result; /* Overloaded variable */
int player_i;

/* OasisOLC states */
struct {
int state;
void (*func)(struct descriptor_data *, char *);
} olc_functions[] = {
{ CON_OEDIT, oedit_parse },
{ CON_ZEDIT, zedit_parse },
{ CON_SEDIT, sedit_parse },
{ CON_MEDIT, medit_parse },
{ CON_REDIT, redit_parse },
{ CON_CEDIT, cedit_parse },
{ CON_TRIGEDIT, trigedit_parse },
{ CON_AEDIT, aedit_parse },
{ CON_HEDIT, hedit_parse },
{ CON_QEDIT, qedit_parse },
{ CON_PREFEDIT, prefedit_parse },
{ CON_IBTEDIT, ibtedit_parse },
{ CON_MSGEDIT, msgedit_parse },
{ -1, NULL }
};

skip_spaces(&arg);

/* Quick check for the OLC states. */
for (player_i = 0; olc_functions[player_i].state >= 0; player_i++)
if (STATE(d) == olc_functions[player_i].state) {
(*olc_functions[player_i].func)(d, arg);
return;
}

/* Not in OLC. */
switch (STATE(d)) {
case CON_GET_PROTOCOL:
write_to_output(d, "Collecting Protocol Information... Please Wait.\r\n");
return;
break;
case CON_GET_NAME: /* wait for input of name */
if (d->character == NULL) {
CREATE(d->character, struct char_data, 1);
clear_char(d->character);
CREATE(d->character->player_specials, struct player_special_data, 1);

new_mobile_data(d->character);

GET_HOST(d->character) = strdup(d->host);
d->character->desc = d;
}
if (!*arg)
STATE(d) = CON_CLOSE;
else {
char buf[MAX_INPUT_LENGTH], tmp_name[MAX_INPUT_LENGTH];

if ((_parse_name(arg, tmp_name)) || strlen(tmp_name) < 2 ||
strlen(tmp_name) > MAX_NAME_LENGTH || !valid_name(tmp_name) ||
fill_word(strcpy(buf, tmp_name)) || reserved_word(buf)) { /* strcpy: OK (mutual MAX_INPUT_LENGTH) */
write_to_output(d, "Invalid name, please try another.\r\nName: ");
return;
}
if ((player_i = load_char(tmp_name, d->character)) > -1) {
GET_PFILEPOS(d->character) = player_i;

if (PLR_FLAGGED(d->character, PLR_DELETED)) {
/* Make sure old files are removed so the new player doesn't get the
* deleted player's equipment. */
if ((player_i = get_ptable_by_name(tmp_name)) >= 0)
remove_player(player_i);

/* We get a false positive from the original deleted character. */
free_char(d->character);

/* Check for multiple creations. */
if (!valid_name(tmp_name)) {
write_to_output(d, "Invalid name, please try another.\r\nName: ");
return;
}
CREATE(d->character, struct char_data, 1);
clear_char(d->character);
CREATE(d->character->player_specials, struct player_special_data, 1);

new_mobile_data(d->character);

if (GET_HOST(d->character))
free(GET_HOST(d->character));
GET_HOST(d->character) = strdup(d->host);

d->character->desc = d;
CREATE(d->character->player.name, char, strlen(tmp_name) + 1);
strcpy(d->character->player.name, CAP(tmp_name)); /* strcpy: OK (size checked above) */
GET_PFILEPOS(d->character) = player_i;
write_to_output(d, "Did I get that right, %s (\t(Y\t)/\t(N\t))? ", tmp_name);
STATE(d) = CON_NAME_CNFRM;
} else {
/* undo it just in case they are set */
REMOVE_BIT_AR(PLR_FLAGS(d->character), PLR_WRITING);
REMOVE_BIT_AR(PLR_FLAGS(d->character), PLR_MAILING);
REMOVE_BIT_AR(PLR_FLAGS(d->character), PLR_CRYO);
d->character->player.time.logon = time(0);
write_to_output(d, "Password: ");
echo_off(d);
d->idle_tics = 0;
STATE(d) = CON_PASSWORD;
}
} else {
/* player unknown -- make new character */

/* Check for multiple creations of a character. */
if (!valid_name(tmp_name)) {
write_to_output(d, "Invalid name, please try another.\r\nName: ");
return;
}
CREATE(d->character->player.name, char, strlen(tmp_name) + 1);
strcpy(d->character->player.name, CAP(tmp_name)); /* strcpy: OK (size checked above) */

write_to_output(d, "Did I get that right, %s (\t(Y\t)/\t(N\t))? ", tmp_name);
STATE(d) = CON_NAME_CNFRM;
}
}
break;

case CON_NAME_CNFRM: /* wait for conf. of new name */
if (UPPER(*arg) == 'Y') {
if (isbanned(d->host) >= BAN_NEW) {
mudlog(NRM, LVL_GOD, TRUE, "Request for new char %s denied from [%s] (siteban)", GET_PC_NAME(d->character), d->host);
write_to_output(d, "Sorry, new characters are not allowed from your site!\r\n");
STATE(d) = CON_CLOSE;
return;
}
if (circle_restrict) {
write_to_output(d, "Sorry, new players can't be created at the moment.\r\n");
mudlog(NRM, LVL_GOD, TRUE, "Request for new char %s denied from [%s] (wizlock)", GET_PC_NAME(d->character), d->host);
STATE(d) = CON_CLOSE;
return;
}
perform_new_char_dupe_check(d);
write_to_output(d, "New character.\r\nGive me a password for %s: ", GET_PC_NAME(d->character));
echo_off(d);
STATE(d) = CON_NEWPASSWD;
} else if (*arg == 'n' || *arg == 'N') {
write_to_output(d, "Okay, what IS it, then? ");
free(d->character->player.name);
d->character->player.name = NULL;
STATE(d) = CON_GET_NAME;
} else
write_to_output(d, "Please type Yes or No: ");
break;

case CON_PASSWORD: /* get pwd for known player */
/* To really prevent duping correctly, the player's record should be reloaded
* from disk at this point (after the password has been typed). However I'm
* afraid that trying to load a character over an already loaded character is
* going to cause some problem down the road that I can't see at the moment.
* So to compensate, I'm going to (1) add a 15 or 20-second time limit for
* entering a password, and (2) re-add the code to cut off duplicates when a
* player quits. JE 6 Feb 96 */

echo_on(d); /* turn echo back on */

/* New echo_on() eats the return on telnet. Extra space better than none. */
write_to_output(d, "\r\n");

if (!*arg)
STATE(d) = CON_CLOSE;
else {
if (strncmp(CRYPT(arg, GET_PASSWD(d->character)), GET_PASSWD(d->character), MAX_PWD_LENGTH)) {
mudlog(BRF, LVL_GOD, TRUE, "Bad PW: %s [%s]", GET_NAME(d->character), d->host);
GET_BAD_PWS(d->character)++;
save_char(d->character);
if (++(d->bad_pws) >= CONFIG_MAX_BAD_PWS) { /* 3 strikes and you're out. */
write_to_output(d, "Wrong password... disconnecting.\r\n");
STATE(d) = CON_CLOSE;
} else {
write_to_output(d, "Wrong password.\r\nPassword: ");
echo_off(d);
}
return;
}

/* Password was correct. */
load_result = GET_BAD_PWS(d->character);
GET_BAD_PWS(d->character) = 0;
d->bad_pws = 0;

if (isbanned(d->host) == BAN_SELECT &&
!PLR_FLAGGED(d->character, PLR_SITEOK)) {
write_to_output(d, "Sorry, this char has not been cleared for login from your site!\r\n");
STATE(d) = CON_CLOSE;
mudlog(NRM, LVL_GOD, TRUE, "Connection attempt for %s denied from %s", GET_NAME(d->character), d->host);
return;
}
if (GET_LEVEL(d->character) < circle_restrict) {
write_to_output(d, "The game is temporarily restricted.. try again later.\r\n");
STATE(d) = CON_CLOSE;
mudlog(NRM, LVL_GOD, TRUE, "Request for login denied for %s [%s] (wizlock)", GET_NAME(d->character), d->host);
return;
}
/* check and make sure no other copies of this player are logged in */
if (perform_dupe_check(d))
return;

if (GET_LEVEL(d->character) >= LVL_IMMORT)
write_to_output(d, "%s", imotd);
else
write_to_output(d, "%s", motd);

if (GET_INVIS_LEV(d->character))
mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE, "%s has connected. (invis %d)", GET_NAME(d->character), GET_INVIS_LEV(d->character));
else
mudlog(BRF, LVL_IMMORT, TRUE, "%s has connected.", GET_NAME(d->character));

/* Add to the list of 'recent' players (since last reboot) */
if (AddRecentPlayer(GET_NAME(d->character), d->host, FALSE, FALSE) == FALSE)
{
mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE, "Failure to AddRecentPlayer (returned FALSE).");
}

if (load_result) {
write_to_output(d, "\r\n\r\n\007\007\007"
"%s%d LOGIN FAILURE%s SINCE LAST SUCCESSFUL LOGIN.%s\r\n",
CCRED(d->character, C_SPR), load_result,
(load_result > 1) ? "S" : "", CCNRM(d->character, C_SPR));
GET_BAD_PWS(d->character) = 0;
}
write_to_output(d, "\r\n*** PRESS RETURN: ");
STATE(d) = CON_RMOTD;
}
break;

case CON_NEWPASSWD:
case CON_CHPWD_GETNEW:
if (!*arg || strlen(arg) > MAX_PWD_LENGTH || strlen(arg) < 3 ||
!str_cmp(arg, GET_PC_NAME(d->character))) {
write_to_output(d, "\r\nIllegal password.\r\nPassword: ");
return;
}
strncpy(GET_PASSWD(d->character), CRYPT(arg, GET_PC_NAME(d->character)), MAX_PWD_LENGTH); /* strncpy: OK (G_P:MAX_PWD_LENGTH+1) */
*(GET_PASSWD(d->character) + MAX_PWD_LENGTH) = '\0';

write_to_output(d, "\r\nPlease retype password: ");
if (STATE(d) == CON_NEWPASSWD)
STATE(d) = CON_CNFPASSWD;
else
STATE(d) = CON_CHPWD_VRFY;
break;

case CON_CNFPASSWD:
case CON_CHPWD_VRFY:
if (strncmp(CRYPT(arg, GET_PASSWD(d->character)), GET_PASSWD(d->character),
MAX_PWD_LENGTH)) {
write_to_output(d, "\r\nPasswords don't match... start over.\r\nPassword: ");
if (STATE(d) == CON_CNFPASSWD)
STATE(d) = CON_NEWPASSWD;
else
STATE(d) = CON_CHPWD_GETNEW;
return;
}
echo_on(d);

if (STATE(d) == CON_CNFPASSWD) {
write_to_output(d, "\r\nWhat is your sex (\t(M\t)/\t(F\t))? ");
STATE(d) = CON_QSEX;
} else {
save_char(d->character);
write_to_output(d, "\r\nDone.\r\n%s", CONFIG_MENU);
STATE(d) = CON_MENU;
}
break;

case CON_QSEX: /* query sex of new user */
switch (*arg) {
case 'm':
case 'M':
d->character->player.sex = SEX_MALE;
break;
case 'f':
case 'F':
d->character->player.sex = SEX_FEMALE;
break;
default:
write_to_output(d, "That is not a sex..\r\n"
"What IS your sex? ");
return;
}

write_to_output(d, "%s\r\nRace: ", race_menu);
STATE(d) = CON_QRACE;
break;

case CON_QRACE:
load_result = parse_race(*arg);
if (load_result == RACE_UNDEFINED) {
write_to_output(d, "\r\nThat's not a race.\r\nRace: ");
return;
} else
GET_RACE(d->character) = load_result;

write_to_output(d, "\r\nSelect a class:\r\n");

int i;
for (i = 0; i < NUM_CLASSES; i++) {
if (classRaceAllowed[(int) GET_RACE(d->character)])
write_to_output(d, "%s", class_menu);
}
write_to_output(d, "\r\nClass: ");

STATE(d) = CON_QCLASS;
break;

case CON_QCLASS:
load_result = parse_class(*arg);
if (load_result == CLASS_UNDEFINED || !classRaceAllowed[GET_RACE(d->character)][load_result]) {
write_to_output(d, "\r\nThat's not a class.\r\nClass: ");
return;
}

GET_CLASS(d->character) = load_result;

write_to_output(d, "\r\nPress enter to roll your stats.");
STATE(d) = CON_QROLLSTATS;
break;

case CON_QROLLSTATS:
switch (*arg) {
case 'y':
case 'Y':
break;
case 'n':
case'N':
default:
roll_real_abils(d->character);
write_to_output(d, "\r\nSTR: [%d/%d] Int: [%d] Wis: [%d] Dex:"
" [%d] Con: [%d] Cha: [%d]",
GET_STR(d->character), GET_ADD(d->character),
GET_INT(d->character), GET_WIS(d->character),
GET_DEX(d->character), GET_CON(d->character),
GET_CHA(d->character));
write_to_output(d, "\r\nKeep these stats? (y/n)");
return;
}

if (d->olc) {
free(d->olc);
d->olc = NULL;
}
if (GET_PFILEPOS(d->character) < 0)
GET_PFILEPOS(d->character) = create_entry(GET_PC_NAME(d->character));
/* Now GET_NAME() will work properly. */
init_char(d->character);
save_char(d->character);
save_player_index();
write_to_output(d, "%s\r\n*** PRESS RETURN: ", motd);
STATE(d) = CON_RMOTD;
/* make sure the last log is updated correctly. */
GET_PREF(d->character)= rand_number(1, 128000);
GET_HOST(d->character)= strdup(d->host);

mudlog(NRM, LVL_GOD, TRUE, "%s [%s] new player.", GET_NAME(d->character), d->host);

/* Add to the list of 'recent' players (since last reboot) */
if (AddRecentPlayer(GET_NAME(d->character), d->host, TRUE, FALSE) == FALSE)
{
mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE, "Failure to AddRecentPlayer (returned FALSE).");
}
break;

case CON_RMOTD: /* read CR after printing motd */
write_to_output(d, "%s", CONFIG_MENU);
if (IS_HAPPYHOUR > 0){
write_to_output(d, "\r\n");
write_to_output(d, "\tyThere is currently a Happyhour!\tn\r\n");
write_to_output(d, "\r\n");
}
add_llog_entry(d->character, LAST_CONNECT);
STATE(d) = CON_MENU;
break;

case CON_MENU: { /* get selection from main menu */

switch (*arg) {
case '0':
write_to_output(d, "Goodbye.\r\n");
add_llog_entry(d->character, LAST_QUIT);
STATE(d) = CON_CLOSE;
break;

case '1':
load_result = enter_player_game(d);
send_to_char(d->character, "%s", CONFIG_WELC_MESSG);

/* Clear their load room if it's not persistant. */
if (!PLR_FLAGGED(d->character, PLR_LOADROOM))
GET_LOADROOM(d->character) = NOWHERE;
save_char(d->character);

greet_mtrigger(d->character, -1);
greet_memory_mtrigger(d->character);

act("$n has entered the game.", TRUE, d->character, 0, 0, TO_ROOM);

STATE(d) = CON_PLAYING;
MXPSendTag( d, "<VERSION>" );
if (GET_LEVEL(d->character) == 0) {
do_start(d->character);
send_to_char(d->character, "%s", CONFIG_START_MESSG);
do_newbie(d->character); /* newbie eq */

}
look_at_room(d->character, 0);
if (has_mail(GET_IDNUM(d->character)))
send_to_char(d->character, "You have mail waiting.\r\n");
if (load_result == 2) { /* rented items lost */
send_to_char(d->character, "\r\n\007You could not afford your rent!\r\n"
"Your possesions have been donated to the Salvation Army!\r\n");
}
d->has_prompt = 0;
/* We've updated to 3.1 - some bits might be set wrongly: */
REMOVE_BIT_AR(PRF_FLAGS(d->character), PRF_BUILDWALK);
break;

case '2':
if (d->character->player.description) {
write_to_output(d, "Current description:\r\n%s", d->character->player.description);
/* Don't free this now... so that the old description gets loaded as the
* current buffer in the editor. Do setup the ABORT buffer here, however. */
d->backstr = strdup(d->character->player.description);
}
write_to_output(d, "Enter the new text you'd like others to see when they look at you.\r\n");
send_editor_help(d);
d->str = &d->character->player.description;
d->max_str = PLR_DESC_LENGTH;
STATE(d) = CON_PLR_DESC;
break;

case '3':
page_string(d, background, 0);
STATE(d) = CON_RMOTD;
break;

case '4':
write_to_output(d, "\r\nEnter your old password: ");
echo_off(d);
STATE(d) = CON_CHPWD_GETOLD;
break;

case '5':
write_to_output(d, "\r\nEnter your password for verification: ");
echo_off(d);
STATE(d) = CON_DELCNF1;
break;

default:
write_to_output(d, "\r\nThat's not a menu choice!\r\n%s", CONFIG_MENU);
break;
}
break;
}

case CON_CHPWD_GETOLD:
if (strncmp(CRYPT(arg, GET_PASSWD(d->character)), GET_PASSWD(d->character), MAX_PWD_LENGTH)) {
echo_on(d);
write_to_output(d, "\r\nIncorrect password.\r\n%s", CONFIG_MENU);
STATE(d) = CON_MENU;
} else {
write_to_output(d, "\r\nEnter a new password: ");
STATE(d) = CON_CHPWD_GETNEW;
}
return;

case CON_DELCNF1:
echo_on(d);
if (strncmp(CRYPT(arg, GET_PASSWD(d->character)), GET_PASSWD(d->character), MAX_PWD_LENGTH)) {
write_to_output(d, "\r\nIncorrect password.\r\n%s", CONFIG_MENU);
STATE(d) = CON_MENU;
} else {
write_to_output(d, "\r\nYOU ARE ABOUT TO DELETE THIS CHARACTER PERMANENTLY.\r\n"
"ARE YOU ABSOLUTELY SURE?\r\n\r\n"
"Please type \"yes\" to confirm: ");
STATE(d) = CON_DELCNF2;
}
break;

case CON_DELCNF2:
if (!strcmp(arg, "yes") || !strcmp(arg, "YES")) {
if (PLR_FLAGGED(d->character, PLR_FROZEN)) {
write_to_output(d, "You try to kill yourself, but the ice stops you.\r\n"
"Character not deleted.\r\n\r\n");
STATE(d) = CON_CLOSE;
return;
}
if (GET_LEVEL(d->character) < LVL_GRGOD)
SET_BIT_AR(PLR_FLAGS(d->character), PLR_DELETED);
save_char(d->character);
Crash_delete_file(GET_NAME(d->character));
/* If the selfdelete_fastwipe flag is set (in config.c), remove all the
* player's immediately. */
if (selfdelete_fastwipe)
if ((player_i = get_ptable_by_name(GET_NAME(d->character))) >= 0) {
SET_BIT(player_table[player_i].flags, PINDEX_SELFDELETE);
remove_player(player_i);
}

delete_variables(GET_NAME(d->character));
write_to_output(d, "Character '%s' deleted! Goodbye.\r\n", GET_NAME(d->character));
mudlog(NRM, LVL_GOD, TRUE, "%s (lev %d) has self-deleted.", GET_NAME(d->character), GET_LEVEL(d->character));
STATE(d) = CON_CLOSE;
return;
} else {
write_to_output(d, "\r\nCharacter not deleted.\r\n%s", CONFIG_MENU);
STATE(d) = CON_MENU;
}
break;

/* It is possible, if enough pulses are missed, to kick someone off while they
* are at the password prompt. We'll let the game_loop()axe them. */
case CON_CLOSE:
break;

default:
log("SYSERR: Nanny: illegal state of con'ness (%d) for '%s'; closing connection.",
STATE(d), d->character ? GET_NAME(d->character) : "<unknown>");
STATE(d) = CON_DISCONNECT; /* Safest to do. */
break;
}
}

Please Log in or Create an account to join the conversation.

More
21 Dec 2024 22:35 #10482 by thomas
This looks like a double free() - if the close_socket method is called twice, the d->character variable will be attempted free'd twice.
I've noticed a call pattern where it's possible for this to happen; if process_output fails, it will _first_ close_socket() and then return -1 which will trigger a second close_socket() in the game_loop. This appears to be a stock bug.
I suggest trying to comment out the close_socket in process_output: github.com/tbamud/tbamud/blob/88b3027ec6...65e/src/comm.c#L1599

Please Log in or Create an account to join the conversation.

More
21 Dec 2024 23:04 - 21 Dec 2024 23:14 #10483 by JTP
Thanks Thomas

So there was no problem with the others things I posted ?



I hope that is it. Because it's killing me to have people exploit it and do it daily.

I just made the comment out of that line and rebooted
Last edit: 21 Dec 2024 23:14 by JTP.

Please Log in or Create an account to join the conversation.

Time to create page: 0.196 seconds