Engine crash on UGameplayStatics::SpawnDecalAttached

I got this crash:

Access violation - code c0000005
(first/second chance not available)
STAKERII_Win64_Shipping!UDecalComponent::SetLifeSpan()
STAKERII_Win64_Shipping!CreateDecalComponent() STAKERII_Win64_Shipping!UGameplayStatics::SpawnDecalAttached()
STAKERII_Win64_Shipping!ASTAKERII_Effect::BeginPlay()
[d:\source\STAKERII\effects\STAKERII_Effect.cpp:74]
STAKERII_Win64_Shipping!AActor::PostActorConstruction()
STAKERII_Win64_Shipping!AActor::FinishSpawning()
STAKERII_Win64_Shipping!UGameplayStatics::FinishSpawningActor()
STAKERII_Win64_Shipping!ASTAKERII_Bomb_Base::Explode()

Chain:

void ASTAKERII_Bomb_Base::Explode(const FHitResult& Impact)
{
			if (ExplosionTemplate)
			{
				const FRotator SpawnRotation = Impact.ImpactNormal.Rotation();

				FTransform SpawnTM(SpawnRotation, ImpactLocation);
				class ASTAKERII_ExplosionEffects* EffectActor = Cast<ASTAKERII_ExplosionEffects>(UGameplayStatics::BeginDeferredActorSpawnFromClass(this, ExplosionTemplate, SpawnTM));
				if (EffectActor)
				{
					EffectActor->SetSurfaceHit(Impact);
					EffectActor->SetDamageTypeClass(DamageTypeClass);
					UGameplayStatics::FinishSpawningActor(EffectActor, SpawnTM);
				}
			}

void ASTAKERII_Effect::BeginPlay()
{
	Super::BeginPlay();

	if (DefaultFX)
	{
		UGameplayStatics::SpawnEmitterAtLocation(this, DefaultFX, GetActorLocation(), GetActorRotation());
	}

	if (DefaultSound)
	{
		UGameplayStatics::PlaySoundAtLocation(this, DefaultSound, GetActorLocation());
	}

	if (DefaultDecal.DecalMaterial)
	{
		FRotator RandomDecalRotation = SurfaceHit.ImpactNormal.Rotation();
		RandomDecalRotation.Roll = FMath::FRandRange(-180.0f, 180.0f);

		UGameplayStatics::SpawnDecalAttached(DefaultDecal.DecalMaterial, FVector(DefaultDecal.DecalSize, DefaultDecal.DecalSize, 1.0f),
			SurfaceHit.Component.Get(), SurfaceHit.BoneName,
			SurfaceHit.ImpactPoint, RandomDecalRotation, EAttachLocation::KeepWorldPosition,
			DefaultDecal.LifeSpan);
	}
}

UGameplayStatics::SpawnDecalAttached calls decal component creaton:

else
				{
					UDecalComponent* DecalComp = CreateDecalComponent(DecalMaterial, DecalSize, AttachToComponent->GetWorld(), AttachToComponent->GetOwner(), LifeSpan);
					DecalComp->AttachTo(AttachToComponent, AttachPointName);

No pointer check here, so it should be created ever

UDecalComponent* CreateDecalComponent(class UMaterialInterface* DecalMaterial, FVector DecalSize, UWorld* World, AActor* Actor, float LifeSpan)
{
	UDecalComponent* DecalComp = NewObject<UDecalComponent>((Actor ? Actor : (UObject*)World));
	DecalComp->bAllowAnyoneToDestroyMe = true;
	DecalComp->DecalMaterial = DecalMaterial;
	DecalComp->DecalSize = DecalSize;
	DecalComp->bAbsoluteScale = true;
	DecalComp->RegisterComponentWithWorld(World);

	if (LifeSpan > 0.f)
	{
		DecalComp->SetLifeSpan(LifeSpan);
	}

	return DecalComp;
}

Here is also no pointer check so it again should be created ever.
So crash happens in SetLifeSpan():

void UDecalComponent::SetLifeSpan(const float LifeSpan)
{
	if (LifeSpan > 0.f)
	{
		GetWorld()->GetTimerManager().SetTimer(TimerHandle_DestroyDecalComponent, this, &UDecalComponent::LifeSpanCallback, LifeSpan, false);
	}
	else
	{
		GetWorld()->GetTimerManager().ClearTimer(TimerHandle_DestroyDecalComponent);
	}
}

As you can see World was never checked to be a non-zero pointer and valid. I think that is why this crash happened, because with some unknown reason world of component where decal wanted to be attached is zero or invalid

UGameplayStatics has also many other functions where pointers not checked to be non-zeroed

Hey h20,

Generally, it is up to the developer (you) to check the pointer before sending it into functions, such as into UGameplayStatics::SpawnDecalAttached.

With that said, if you want to put a list of functions together that aren’t checking for null pointers, I can enter a feature request to have them looked at by the development team. Then, they can decide if they have the resources to update them.

Hello. Tnx for answer. As i can see in sources there are many places where pointers are checked but not everywhere, maybe it was done so for fast programming speed, i do not know.
As a programmer I do not think it is a proper way to check everything in end functions, because developer can check main object pointer he use and he can not know what he should also check else which is used inside sources calls. If every time developer will check all possible pointers - it will cause many of redundant code, and it looks not professionall :slight_smile: so beter to check used pointers or objects inside currently executed function, especially if it is static function, or one of the basic functions inside engine source code.

But ok, i understand your answer, tnx.