From f86f25d137c662b6cb7fc1fae4f9ecfb39ad6399 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:50:18 -0600 Subject: [PATCH] [core] adjust usage of std::stack pop calls This may fix use-after-frees as pop() can call destructors. --- src/map/ai/ai_container.cpp | 32 +++++++++++++++++++++++------ src/map/ai/helpers/action_queue.cpp | 4 ++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/map/ai/ai_container.cpp b/src/map/ai/ai_container.cpp index b57440e740e..dafcab1b42d 100644 --- a/src/map/ai/ai_container.cpp +++ b/src/map/ai/ai_container.cpp @@ -431,14 +431,34 @@ void CAIContainer::Tick(timer::time_point _tick) { Controller->Tick(_tick); } - CState* top = nullptr; - while (!m_stateStack.empty() && (top = m_stateStack.top().get())->DoUpdate(_tick)) + + while (!m_stateStack.empty()) { - if (top == GetCurrentState()) + CState* top = m_stateStack.top().get(); + + if (top) + { + // If DoUpdate returns true, the state has signaled it's done + // Clean it up. + // If the state stack is not empty, the next state will be polled. + if (top->DoUpdate(_tick)) + { + // the state may change (and get cleaned up) during DoUpdate as a consequence of things it does + // Only clean up the state if the current state is still the same one we ran DoUpdate on + if (top == GetCurrentState()) + { + top->Cleanup(_tick); + m_stateStack.pop(); + } + } + else // The state isn't done yet, preserve the state stack and bail out + { + break; + } + } + else // This should be dead code, but just in case... { - auto state = std::move(m_stateStack.top()); - m_stateStack.pop(); - state->Cleanup(_tick); + break; } } diff --git a/src/map/ai/helpers/action_queue.cpp b/src/map/ai/helpers/action_queue.cpp index 6514329ebeb..6bc1c3873f8 100644 --- a/src/map/ai/helpers/action_queue.cpp +++ b/src/map/ai/helpers/action_queue.cpp @@ -50,8 +50,8 @@ void CAIActionQueue::checkAction(timer::time_point tick) if (tick > topaction.start_time + topaction.delay) { queueAction_t action = timerQueue.top(); - timerQueue.pop(); handleAction(action); + timerQueue.pop(); } else { @@ -64,8 +64,8 @@ void CAIActionQueue::checkAction(timer::time_point tick) if (tick > topaction.start_time + topaction.delay && (!topaction.checkState || PEntity->PAI->CanChangeState())) { auto action = actionQueue.top(); - actionQueue.pop(); handleAction(action); + actionQueue.pop(); } else {