FInstanceSceneDataBuffers::FAccessTag 이슈: PointerHash(this)가 0을 반환 할 수 있음

다음과 같은 콜스택으로 크래시가 보고되어 원인 파악해보았는데, 수정이 필요할 것으로

생각되어 문의드립니다.

원인

InstanceDataSceneProxy.h의 BeginWriteAccess에 다음 check문이 있습니다.

FWriteView BeginWriteAccess(FAccessTag AccessTag)

{

check(AccessTag.Kind == FAccessTag::EKind::Writer && AccessTag.WriterTag != 0u);

}

FAccessTag는 WriterTag == 0을 invalid로 예약하고 있는데, 엔진 전반에서 writer tag를 FAccessTag(PointerHash(this)) 형태로 생성하는 패턴이 널리 사용됩니다.

예시 (FastGeoInstancedStaticMeshComponent.cpp:97-98):

FInstanceSceneDataBuffers::FAccessTag AccessTag(PointerHash(this));

auto View = InstanceSceneDataBuffers.BeginWriteAccess(AccessTag);

이 패턴은 ISMInstanceDataSceneProxy.cpp, InstanceDataManager.cpp, InstancedSkinnedMeshComponent.cpp 등에서도 동일하게 사용되며, 확인된 것만 총 14곳 이상입니다.

PointerHash가 0을 반환하는 조건

uint32 PointerHash(const void* Key)

{

const UPTRINT PtrInt = reinterpret_cast<UPTRINT>(Key) >> 4;

return UE::Private::MurmurFinalize32((uint32)PtrInt);

}

여기에 두 가지 문제가 겹칩니다.

1) 64비트 포인터의 uint32 truncation

(uint32)PtrInt은 포인터의 상위 32비트를 버립니다. 따라서 this의 하위 36비트(>> 4 후 하위 32비트)가 모두 0인 주소 — 예를 들어 0x00000002’00000000 같은 주소에 객체가 할당되면 (uint32)(ptr >> 4) == 0이 됩니다.

2) MurmurFinalize32(0)은 확정적으로 0을 반환

즉, 확률적 문제가 아니라 특정 주소 조건에서 반드시 0을 반환하며, 이 경우 WriterTag != 0 계약을 위반하여 크래시로 이어집니다.

수정 제안

드물게 발생하지만 발생 시 반드시 크래시로 이어지며, 동일 패턴이 엔진 전반 14곳 이상에서 사용되고 있으므로 보완이 필요할 것 같습니다.

가능한 수정 방향:

- (A) FAccessTag 생성자에서 전역 보장 — WriterTag = InWriterTag ? InWriterTag : 1u 로 non-zero를 강제

- (B) 각 호출처에서 non-zero 정규화 — Tag = PointerHash(this); if (Tag == 0) Tag = 1;

호출처가 14곳 이상이므로 **(A)**가 누락 없이 안전할 것으로 보이는데, 어떤 방향이 적절할지 의견 부탁드립니다.

[Attachment Removed]

재현 방법
재현은 간헐적이므로, 코드흐름만 살펴봐주시면 감사하곘습니다.

[Attachment Removed]

안녕하세요.

해당 질문 관련 이슈가 CL 49601174에서 수정된 것으로 확인됩니다.

https://github.com/EpicGames/UnrealEngine/commit/9fe954fe5fc5034a29956ef5d9c4ec2c94b50213

해당 커밋에서 질문 예시와 같이 하위 비트가 모두 0인 경우에도 유효한 값을 반환하도록 수정되었으며 널 포인터의 경우에 대해서는 0를 반환하도록 하여 기존 의도대로 동작하도록 수정되었습니다.

같은 이슈가 다시 발생한다면 해당 수정 사항을 적용하여 문제를 해결해보시는 것을 권유드립니다.

감사합니다.

[Attachment Removed]