TWeakObjectPtr invalid(?) after assignment with a valid pointer. Crashes the engine after Exception

Don’t really know where to go with this. (Is this the right place to report possible bugs?)
We’re currently starting to use Chaos. While playtesting the engine crashes often after destructions happen. Hard to replicate the issue consistently but looking at where it goes wrong I think it’s a bug in the engine.

The following exception is thrown:

Exception thrown: read access violation.
__imp_FWeakObjectPtr::Get(…) returned nullptr.

This happens in the following piece of code in CollisionConversions.cpp in the function ConvertQueryOverlap. (Exception is thrown at the line in bold.)

#if WITH_CHAOS
// Currently geom collections are registered with a primitive component user data, but maybe custom should be adapted
// to be more general so we can support leaf identification #BGTODO
void* UserData = Actor.UserData();
UPrimitiveComponent* PossibleOwner = FChaosUserData::Get(UserData);

if(PossibleOwner)
{
OutOverlap.Component = PossibleOwner;
OutOverlap.Actor = OutOverlap.Component->GetOwner();
OutOverlap.ItemIndex = INDEX_NONE;
}

The type of OutOverlap is FOverlapResult. The component member is a TWeakObjectPtr which is initialized at the previous line. This pointer should be valid (that’s what the if statement is for) however when the owner is queried the exception is thrown.

Maybe my knowledge about TWeakObjectPtr is wrong, but it should be valid for as for as I know.

Thanks in advance.

Engine version: 4.27Chaos
Some more context: The conversion is a result of an update on the Geometry Collection Component. In the call stack the instigator is always a rotation of the player actor (SetActorRotation) with Teleport settings set to NONE. The rotation happens during the tick of a Gameplay Ability Task.

I don’t know much about Chaos, but your use of the TWeakObjectPtr looks OK.

I’m guessing it has something to do with the (void*) cast, or the implicit (UPrimitiveComponent*) cast of the FKShapeElem* result of FChaosUserData::Get.

IDK what FKShapeElem is, but if it’s not a sub-class of UPrimitiveComponent then that’s probably your problem. The docs don’t seem to make it seem like FKShapeElem can be safely casted to a UPrimitiveComponent. (Then again, the docs are pretty bad, as usual for UE).

FWIW you should use C++ casting rather than C casting. It’s possible that C++ casting will reveal whether or not the cast is invalid, whereas C casting can do just what you see here – silently crash your game by forcing a square object into a round hole.

Hope this helps!

1 Like

Thanks for your reply.

Just to make things clear: this code comes from the Engine itself. I did not write this. I noticed that in later versions this code has been changed to the following:

void* UserData = Actor.UserData();
UPrimitiveComponent* PossibleOwner = FChaosUserData::Get(UserData);

if(PossibleOwner)
{
OutOverlap.Component = PossibleOwner;
OutOverlap.OverlapObjectHandle = FActorInstanceHandle(OutOverlap.Component->GetOwner());
OutOverlap.ItemIndex = INDEX_NONE;
}

I assume the casting is not the problem: if the casting does not succeed, then PossibleOwner should be nullptr, and the bold line is never executed. (Correct me if I’m wrong.)

Looking at the code of FChaosUserData::Get for primitive components, there should not be a misinterpretation in case the C-style casting did some weird stuff:

template <> FORCEINLINE UPrimitiveComponent* FChaosUserData::Get(void* UserData)
{
if (!UserData ||
((FChaosUserData*)UserData)->Type != EChaosUserDataType::PrimitiveComponent)
{
return nullptr;
}
return (UPrimitiveComponent*)((FChaosUserData*)UserData)->Payload; }

I could try upgrading the project to a newer engine version, however this does not seems a good solution and may not fix the origin of the problem.
(Also, I want to understand what’s going wrong, and more importantly, if the source IS actually in my code.)

Incorrect/misleading/incomplete docs strike again! :rofl:

It does seem like it’s giving you back a UPrimitiveComponent* so the casting doesn’t seem to be the issue.

To answer your other question, the only time you get an error from the compiler for bad casts is if you use C++ casts.

You can C cast anything to anything. MyObject* foo = 123 will compile and execute just fine in C code, that’s why it’s so dangerous. When you later try to foo->execute() you’ll get a crash unless by some happy miracle the memory at offset 123 is actually the beginning of a MyObject.

I thought that might be what was crashing here, but given that this is engine code crashing, and the copy/paste you did of that shows that the engine does indeed expect it’s giving you back a PrimitiveComponent*, it seems like something else must be going on here.

2 Likes

Thanks for the help so far.

I’m going to dig a bit deeper in non-optimized builds to see what exactly is going on.

Is the PrimitiveComponent returned by FChaosUserData::Get being destroyed in the meantime somehow? That could explain why the raw pointer still appears valid but may become invalid in WeakObjectPtr::operator=. Or when WeakObjectPtr::Get is called from TWeakObjectPtr::operator-> before the call to GetOwner the object may be determined as invalid resulting in a nullptr being returned instead.

Are there any overlap events or similar running on the component that could destroy it (or the actor) before it gets to the crash?

Update on this (I managed to dug deeper into the problem):

Indeed this crash only happens the moment the GeometryCollectionActor is getting destroyed. I managed to reproduce this in a clean thirdperson project.

Setup:
Exact steps to recreate this crash:

  • Clean ThirdPerson 4.27Chaos template project with starter content.

  • Create blueprint (parent Actor) and add a SphereCollision component. Set radius to 2000

  • Open the character blueprint and add a ChildActorComponent. Set the child actor to the blueprint we just created.

  • Create a GeometryCollection from a simple cube (I used the one from the starter content I think) and fracture (uniform default settings).

  • Create a GeometryCollectionActor blueprint and set the GeometryCollectionComponent to use the just made geometry collection. In the event graph, add a node to beginplay to SetLifespan at 5 seconds.

  • Place the GeometryCollectionActor in the map above ground (when the game starts, this actor falls down and should break). Also, place it not too far from the player spawn.

  • Play the game and walk around in the debris when the lifespan ends.

This results in the crash above.

To prevent this crash, you can set the collision response channel on the Sphere Collider for destructibles to Ignore. (But what if you don’t want this to be ignored :wink: ?)

For my current project I can get away with ignoring overlaps on destructibles in this scenario.
My hypothesis is that indeed the overlap of the sphere collider queries some invalidated debris. Is this a real bug, or should I just know that this can happen? (And so: should I file a bug report to Epic?)

1 Like

You could search the public issue tracker for similar issues. If that doesn’t bring up anything I’d say filing a bug report is probably the best way to get this fixed quickly (unless Epic is already aware internally). Or if you have source access you could fix it in your fork and create a pull request (that at least checks PossibleOwner or the WeakObjectPtr correctly for validity). Epic would still have to fix it properly to prevent the ensure from triggering but at least it won’t crash right away anymore.

		{
			OutOverlap.Component = PossibleOwner;
			OutOverlap.OverlapObjectHandle = FActorInstanceHandle(OutOverlap.Component->GetOwner());
			OutOverlap.ItemIndex = INDEX_NONE;
		}
		else
		{
			ensureMsgf(false, TEXT("ConvertQueryOverlap called with bad payload type"));
		}