From 1647a6d220783296eb34dd69dd868d6db3520692 Mon Sep 17 00:00:00 2001 From: Ivan Ushakov Date: Sat, 18 Jul 2026 15:46:36 +0300 Subject: [PATCH] Formation: guard setLeader's find against an empty active range FormationUnits::setLeader ran find(begin()+1, inactive(), leader) with no guard on the active range. inactive() is begin()+firstInactive_; when there are no active units (firstInactive_==0) inactive()==begin(), so the range is find(begin()+1, begin()) -- first > last. std::find walks forward looking for last and runs off the end of the heap, the reported EXC_BAD_ACCESS (fault address 0xc25c00000, a MALLOC_SMALL region boundary), reached via UnitLegionary::Kill -> UnitActing::finishUpgrade. Guard the find with activeSize() > 0 (makeAllActive avoids the same trap only because it first sets firstInactive_ = size()). Also compare the result against inactive(), the range's end, rather than end(): find returns inactive() when the leader is not among the active units, and inactive() != end() whenever inactive units follow -- the old `it != end()` check would then swap an inactive unit to the front. Co-Authored-By: Claude Opus 4.8 --- Physics/FormationController.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Physics/FormationController.h b/Physics/FormationController.h index 097b33ee..66d44502 100644 --- a/Physics/FormationController.h +++ b/Physics/FormationController.h @@ -409,9 +409,20 @@ class FormationController { if(leader_ == leader) return; - iterator it = find(begin() + 1, inactive(), leader); - if(it != end()) - std::swap(front(), *it); + // Promote `leader` to the front of the active units if it is one of them. Guard the + // find against an empty active range: with no active units inactive() == begin(), + // so find(begin()+1, begin()) has first > last and runs forward off the end of the + // heap (the reported EXC_BAD_ACCESS). makeAllActive avoids this only because it first + // sets firstInactive_ = size(), so its guard is empty(); here the active range is a + // prefix, so we guard on activeSize(). Compare against inactive(), the range's end, + // not end(): find returns inactive() when `leader` is not active, and inactive() != + // end() whenever inactive units follow -- `it != end()` would then swap an inactive + // unit to the front. + if(activeSize() > 0){ + iterator it = find(begin() + 1, inactive(), leader); + if(it != inactive()) + std::swap(front(), *it); + } leader_ = leader; }