mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-01 18:13:57 +00:00
Fix use-after-free crash and misaligned protocol access
Stop active ScenePlayer animation in OnActorEnter/OnActorExit before modifying ride animation state — the ScenePlayer may still hold a reference to the ride vehicle ROI that ClearRideAnimation frees. Deactivate() and OnWorldDisabled() already had this guard. Add alignment padding to MessageHeader (13→14 bytes) so uint16_t fields in packed protocol structs no longer sit at odd offsets (UBSan violation). Breaking wire format change — all clients and relay must update together. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f3c6d90cf1
commit
e57164d345
@ -78,6 +78,7 @@ enum LightChangeType : uint8_t {
|
||||
|
||||
struct MessageHeader {
|
||||
uint8_t type;
|
||||
uint8_t _pad;
|
||||
uint32_t peerId;
|
||||
uint32_t sequence;
|
||||
uint32_t target;
|
||||
|
||||
@ -175,7 +175,7 @@ MxResult NetworkManager::Tickle()
|
||||
}
|
||||
else if (IsConnected()) {
|
||||
AnimCancelMsg cancelMsg{};
|
||||
cancelMsg.header = {MSG_ANIM_CANCEL, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
cancelMsg.header = {MSG_ANIM_CANCEL, 0, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
SendMessage(cancelMsg);
|
||||
}
|
||||
m_localPendingAnimInterest = -1;
|
||||
@ -326,7 +326,7 @@ void NetworkManager::CancelLocalAnimInterest()
|
||||
}
|
||||
else if (IsConnected()) {
|
||||
AnimCancelMsg msg{};
|
||||
msg.header = {MSG_ANIM_CANCEL, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
msg.header = {MSG_ANIM_CANCEL, 0, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
SendMessage(msg);
|
||||
}
|
||||
|
||||
@ -638,7 +638,7 @@ void NetworkManager::ProcessPendingRequests()
|
||||
}
|
||||
else if (IsConnected()) {
|
||||
AnimInterestMsg msg{};
|
||||
msg.header = {MSG_ANIM_INTEREST, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
msg.header = {MSG_ANIM_INTEREST, 0, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
msg.animIndex = animIndex;
|
||||
ThirdPersonCamera::Controller* animCam = GetCamera();
|
||||
msg.displayActorIndex = animCam ? animCam->GetDisplayActorIndex() : 0;
|
||||
@ -715,7 +715,7 @@ void NetworkManager::BroadcastLocalState()
|
||||
}
|
||||
|
||||
PlayerStateMsg msg{};
|
||||
msg.header = {MSG_STATE, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.header = {MSG_STATE, 0, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.actorId = actorId;
|
||||
msg.worldId = inRestrictedArea ? WORLD_NOT_VISIBLE : (int8_t) currentWorld->GetWorldId();
|
||||
msg.vehicleType = DetectVehicleType(userActor);
|
||||
@ -1066,7 +1066,7 @@ void NetworkManager::SendEmote(uint8_t p_emoteId)
|
||||
cam->TriggerEmote(p_emoteId);
|
||||
|
||||
EmoteMsg msg{};
|
||||
msg.header = {MSG_EMOTE, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.header = {MSG_EMOTE, 0, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.emoteId = p_emoteId;
|
||||
SendMessage(msg);
|
||||
}
|
||||
@ -1201,7 +1201,7 @@ bool NetworkManager::IsClonedCharacter(const char* p_name) const
|
||||
void NetworkManager::SendCustomize(uint32_t p_targetPeerId, uint8_t p_changeType, uint8_t p_partIndex)
|
||||
{
|
||||
CustomizeMsg msg{};
|
||||
msg.header = {MSG_CUSTOMIZE, m_localPeerId, m_sequence++, TARGET_BROADCAST_ALL};
|
||||
msg.header = {MSG_CUSTOMIZE, 0, m_localPeerId, m_sequence++, TARGET_BROADCAST_ALL};
|
||||
msg.targetPeerId = p_targetPeerId;
|
||||
msg.changeType = p_changeType;
|
||||
msg.partIndex = p_partIndex;
|
||||
@ -1664,7 +1664,7 @@ void NetworkManager::HandleAnimStartLocally(uint16_t p_animIndex, bool p_localIn
|
||||
AnimUpdateMsg NetworkManager::BuildAnimUpdateMsg(uint16_t p_animIndex, uint32_t p_target)
|
||||
{
|
||||
AnimUpdateMsg msg{};
|
||||
msg.header = {MSG_ANIM_UPDATE, m_localPeerId, m_sequence++, p_target};
|
||||
msg.header = {MSG_ANIM_UPDATE, 0, m_localPeerId, m_sequence++, p_target};
|
||||
msg.animIndex = p_animIndex;
|
||||
|
||||
const Animation::AnimSession* session = m_animSessionHost.FindSession(p_animIndex);
|
||||
@ -1699,7 +1699,7 @@ void NetworkManager::SendAnimUpdateToPlayer(uint16_t p_animIndex, uint32_t p_tar
|
||||
void NetworkManager::BroadcastAnimStart(uint16_t p_animIndex)
|
||||
{
|
||||
AnimStartMsg msg{};
|
||||
msg.header = {MSG_ANIM_START, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.header = {MSG_ANIM_START, 0, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.animIndex = p_animIndex;
|
||||
SendMessage(msg);
|
||||
|
||||
@ -1720,7 +1720,7 @@ void NetworkManager::BroadcastAnimComplete(uint16_t p_animIndex)
|
||||
}
|
||||
|
||||
AnimCompleteMsg msg{};
|
||||
msg.header = {MSG_ANIM_COMPLETE, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.header = {MSG_ANIM_COMPLETE, 0, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.eventId = (static_cast<uint64_t>(SDL_rand_bits()) << 32) | static_cast<uint64_t>(SDL_rand_bits());
|
||||
msg.objectId = animInfo->m_objectId;
|
||||
msg.participantCount = 0;
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// Protocol constants — must stay in sync with protocol.h
|
||||
|
||||
// MessageHeader binary layout: type(1) + peerId(4) + sequence(4) + target(4) = 13 bytes
|
||||
export const HEADER_SIZE = 13;
|
||||
export const PEER_ID_OFFSET = 1;
|
||||
export const SEQUENCE_OFFSET = 5;
|
||||
export const TARGET_OFFSET = 9;
|
||||
// MessageHeader binary layout: type(1) + _pad(1) + peerId(4) + sequence(4) + target(4) = 14 bytes
|
||||
export const HEADER_SIZE = 14;
|
||||
export const PEER_ID_OFFSET = 2;
|
||||
export const SEQUENCE_OFFSET = 6;
|
||||
export const TARGET_OFFSET = 10;
|
||||
|
||||
// Routing target constants
|
||||
export const TARGET_BROADCAST = 0;
|
||||
@ -19,7 +19,7 @@ export const MSG_ASSIGN_ID = 0xff;
|
||||
// AssignIdMsg: compact server-only message — type(1) + peerId(4)
|
||||
const ASSIGN_ID_SIZE = 1 + 4;
|
||||
|
||||
// HostAssignMsg: header(13) + hostPeerId(4)
|
||||
// HostAssignMsg: header(14) + hostPeerId(4)
|
||||
const HOST_ASSIGN_SIZE = HEADER_SIZE + 4;
|
||||
|
||||
export function createAssignIdMsg(peerId: number): ArrayBuffer {
|
||||
|
||||
@ -237,7 +237,7 @@ MxBool WorldStateSync::HandleSkyLightMutation(uint8_t p_entityType, uint8_t p_ch
|
||||
void WorldStateSync::SendSnapshotRequest()
|
||||
{
|
||||
RequestSnapshotMsg msg{};
|
||||
msg.header = {MSG_REQUEST_SNAPSHOT, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
msg.header = {MSG_REQUEST_SNAPSHOT, 0, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
SendMessage(msg);
|
||||
|
||||
m_snapshotRequested = true;
|
||||
@ -275,7 +275,7 @@ void WorldStateSync::SendWorldSnapshot(uint32_t p_targetPeerId)
|
||||
stateBuffer[dataLength++] = (uint8_t) lightPos;
|
||||
|
||||
WorldSnapshotMsg msg{};
|
||||
msg.header = {MSG_WORLD_SNAPSHOT, m_localPeerId, m_sequence++, p_targetPeerId};
|
||||
msg.header = {MSG_WORLD_SNAPSHOT, 0, m_localPeerId, m_sequence++, p_targetPeerId};
|
||||
msg.dataLength = (uint16_t) dataLength;
|
||||
|
||||
std::vector<uint8_t> msgBuf(sizeof(WorldSnapshotMsg) + dataLength);
|
||||
@ -288,7 +288,7 @@ void WorldStateSync::SendWorldSnapshot(uint32_t p_targetPeerId)
|
||||
void WorldStateSync::BroadcastWorldEvent(uint8_t p_entityType, uint8_t p_changeType, uint8_t p_entityIndex)
|
||||
{
|
||||
WorldEventMsg msg{};
|
||||
msg.header = {MSG_WORLD_EVENT, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.header = {MSG_WORLD_EVENT, 0, m_localPeerId, m_sequence++, TARGET_BROADCAST};
|
||||
msg.entityType = p_entityType;
|
||||
msg.changeType = p_changeType;
|
||||
msg.entityIndex = p_entityIndex;
|
||||
@ -298,7 +298,7 @@ void WorldStateSync::BroadcastWorldEvent(uint8_t p_entityType, uint8_t p_changeT
|
||||
void WorldStateSync::SendWorldEventRequest(uint8_t p_entityType, uint8_t p_changeType, uint8_t p_entityIndex)
|
||||
{
|
||||
WorldEventRequestMsg msg{};
|
||||
msg.header = {MSG_WORLD_EVENT_REQUEST, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
msg.header = {MSG_WORLD_EVENT_REQUEST, 0, m_localPeerId, m_sequence++, TARGET_HOST};
|
||||
msg.entityType = p_entityType;
|
||||
msg.changeType = p_changeType;
|
||||
msg.entityIndex = p_entityIndex;
|
||||
|
||||
@ -105,6 +105,16 @@ void Controller::OnActorEnter(IslePathActor* p_actor)
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop external animation before modifying ride/display state —
|
||||
// the ScenePlayer may hold a reference to the ride vehicle ROI.
|
||||
if (m_animPlaying) {
|
||||
if (m_animStopCallback) {
|
||||
m_animStopCallback();
|
||||
}
|
||||
m_animPlaying = false;
|
||||
m_animStopCallback = nullptr;
|
||||
}
|
||||
|
||||
LegoROI* newROI = userActor->GetROI();
|
||||
if (!newROI) {
|
||||
return;
|
||||
@ -157,6 +167,16 @@ void Controller::OnActorExit(IslePathActor* p_actor)
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop external animation before clearing ride animation state —
|
||||
// the ScenePlayer may hold a reference to the ride vehicle ROI.
|
||||
if (m_animPlaying) {
|
||||
if (m_animStopCallback) {
|
||||
m_animStopCallback();
|
||||
}
|
||||
m_animPlaying = false;
|
||||
m_animStopCallback = nullptr;
|
||||
}
|
||||
|
||||
if (m_animator.GetCurrentVehicleType() != VEHICLE_NONE) {
|
||||
m_animator.ClearRideAnimation();
|
||||
m_animator.ClearAll();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user