Jump to content

  •  

Bug Tracker Migration

June 3rd
Good news everyone! The staff has decided that it is time to slowly kill off this Bug Tracker. We will begin the process of slowly migrating from this Bug Tracker over to our Github Issues which can be found here: https://github.com/HerculesWS/Hercules/issues

Over the next couple of days, I will be closing off any opportunity to create new reports. However, I still will keep the opportunity to reply to existing Bug Reports. Doing this will allow us to slowly fix any bug reports we have listed here so that we can easily migrate over to our Issue Tracker.

Update - June 7th 2015: Creating new bug posts has been disabled. Please use our https://github.com/HerculesWS/Hercules/issues tracker to post bugs. Users are still able to reply to existing bug posts.

- Administration

Issue Information

  • #004982

  • 5 - Critical

  • Fixed

Issue Confirmations

  • Yes (0)No (0)
Photo

quest index memory corruption in quest_add()

Posted by Hercules Bot on 26 June 2011 - 04:26 PM

Originally posted by theultramage
http://www.eathena.w...er&showbug=4982

There is a copy-paste typo in function quest_add() that trashes the in-memory quest index of all already finished quests. If the index is used without re-logging, it will cause an out of bounds read, possibly resulting in a server crash.
QUOTE
memmove(&sd->quest_log[i+1], &sd->quest_log[i], sizeof(struct quest)*(sd->num_quests-sd->avail_quests));
memmove(sd->quest_index+i+1, sd->quest_log+i, sizeof(int)*(sd->num_quests-sd->avail_quests));

The questlog array is split into two parts - active quests, followed by completed quests. When adding a new quest, all completed quests are shifted one array position to the right using memmove(). The same thing is then done to the quest_index array, which is paired with the questlog array.

The typo was introduced 2 years ago in r14039 as a followup to r14038. The reason why it wasn't discovered earlier is, that the index corruption only affects already completed entries, and that the index is actually used only in 2 places, one of which only processes active entries. And even the remaining one only executes vulnerable code as a special case.
CODE
int quest_check(TBL_PC * sd, int quest_id, quest_check_type type)
{...
    case HUNTING:
    ARR_FIND(0, MAX_QUEST_OBJECTIVES, j, sd->quest_log[i].count[j] < quest_db[sd->quest_index[i]].count[j]);

To actually execute this, you need to 1) start a hunting quest, 2) have it marked as completed, but not delete it, 3) start another quest without relogging, 4) execute checkquest(HUNTING) on the already completed quest id. These are very special circumstances - however, that's exactly what the renewal novice ground script does.

PS: here's my reproduction script (might not actually crash, I used a debugger to verify the issue).
CODE
prontera.gat,145,175,4    script    Test Poring    909,{

    // cleanup
    erasequest 1001;
    erasequest 60107; // "Hunting Fabres"

    // setup
    setquest 60107;
    completequest 60107;

    // corrupt
    setquest 1001;

     // crash
    checkquest(60107,HUNTING);
}


PS: Crash was reported by Gepard in #athena.

This post has been edited by theultramage: Jun 26 2011, 10:02 AM