Description

Pasting from the UDN:
 
Unsafe code in AbilitySystem can lead to crash when granting an ability inside OnAbilityEnded
Hello.

 

We just ran into a crash. After investigation, it seems to be caused by the UAbilitySystemComponent::NotifyAbilityEnded() function, when trying to access the Spec pointer at the end of the function.

 

The code inside the function seems a bit unsafe :

[Image Removed]

 

(1) We grab the Spec pointer from inside the ability array (1)

(2) Then we trigger the OnAbilityEnded delegate which can cause any gameplay code the happen. In our case we where granting and ability, which invalidate the Spec pointer when abilities array is updated.

(3) After than the Spec pointer is still accessed which leads to a crash.

Jodon's Note:  Copying the Spec is probably not a valid solution because we want to remove the ReplicatedInstances later on from the actual original Spec.

Steps to Reproduce

Note that this will only occur when writing custom native code.  Instead of doing a nice full-fledged example, I'm sticking the code in a bad, but easy-to-repro spot.  This isn't "good coding" but you can at least reproduce the issue this way:

Add this near the bottom of ULyraAbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor), right after TryActivateAbilitiesOnSpawn(); (but inside the if statement):
 
 

his->OnAbilityEnded.AddWeakLambda(this, [this](const FAbilityEndedData& AbilityEnded) {
			if (AbilityEnded.bWasCancelled)			{				return;			}
			TArray<FGameplayAbilitySpecHandle> ActiveAbilities;			GetAllAbilities(ActiveAbilities);
			// Cancel a random amount of abilities to try and show the calling function is unsafe			for (const FGameplayAbilitySpecHandle& SpecHandle : ActiveAbilities)			{				if (FMath::RandBool())				{					SetRemoveAbilityOnEnd(SpecHandle);					ClearAbility(SpecHandle);
					break;				}				else if (FMath::RandBool())				{					FGameplayAbilitySpec RegrantAbility{ AbilityEnded.AbilityThatEnded.GetClass() };					GiveAbilityAndActivateOnce(RegrantAbility);				}			}		});
 

That will randomly remove/re-add abilities as you're completing them.  The idea is that you want to cause reallocations of the ActivatableItems array while the Ability is ending to force a memory stomp.

 

With that code:

  1. launch Lyra.
  2. Load up L_Expanse.
  3. Disable bots (in the Editor Settings menu).
  4. Now PIE with NetMode set as Standalone.
  5. Jump/Shoot/Dash a bunch of times (until it won't allow you to do it anymore).
  6. Exit PIE.  Go to step 4.  Keep doing this until it crashes.

If it's still a bug, it will crash at some point (likely on access to invalid memory).  If it's fixed it won't crash.  Do it about 10 runs to be safe.

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-197731 in the post.

1
Login to Vote

Fixed
ComponentUE - Gameplay - Gameplay Ability System
Affects Versions5.3
Target Fix5.4
Fix Commit29277076
Main Commit29277145
CreatedOct 11, 2023
ResolvedNov 1, 2023
UpdatedDec 12, 2023
View Jira Issue