From d93aa1eb301f7e4de85ac8d22d24c1e422879bca Mon Sep 17 00:00:00 2001 From: mpfans Date: Sun, 13 Sep 2026 05:09:33 +0800 Subject: [PATCH] fix(Core/Vehicle): avoid Vehicle-kit use-after-free in TeleportVehicle (#26116) Co-authored-by: sudlud --- src/server/game/Entities/Vehicle/Vehicle.cpp | 29 +++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index f6f3073762..fb31c0c29e 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -599,12 +599,33 @@ bool Vehicle::IsControllableVehicle() const void Vehicle::TeleportVehicle(float x, float y, float z, float ang) { - _me->GetMap()->LoadGrid(x, y); - _me->NearTeleportTo(x, y, z, ang, true); + // Snapshot passenger GUIDs and cache the base BEFORE teleporting. + // Teleporting the base (Player::TeleportTo) may drop the mount/vehicle aura + // on a long hop (>100 yd) and call ExitVehicle() -> Unit::RemoveVehicleKit() + // -> delete m_vehicleKit, freeing THIS Vehicle (and its Seats) mid-call. + // Reading Seats after that is a use-after-free (see upstream PR #26116 review). + Unit* base = _me; + Vehicle* self = this; + std::vector passengers; + passengers.reserve(Seats.size()); + for (auto const& seat : Seats) + if (!seat.second.IsEmpty()) + passengers.push_back(seat.second.Passenger.Guid); - for (SeatMap::const_iterator itr = Seats.begin(); itr != Seats.end(); ++itr) - if (Unit* passenger = ObjectAccessor::GetUnit(*GetBase(), itr->second.Passenger.Guid)) + base->GetMap()->LoadGrid(x, y); + base->NearTeleportTo(x, y, z, ang, true, true); // vehicleTeleport=true: do not dismount base mid-call + + // THIS Vehicle may have been deleted by the base teleport above (kit freed). + // If so, bail out - passengers were already ejected by ~Vehicle::Uninstall(). + if (base->GetVehicleKit() != self) + return; + + for (ObjectGuid const& guid : passengers) + if (Unit* passenger = ObjectAccessor::GetUnit(*base, guid)) { + if (!passenger->IsInWorld()) + continue; + if (passenger->IsPlayer()) { passenger->ToPlayer()->SetMover(passenger);