We have been getting rare crashes for years in the EnhancedInput Plugin that have only really happened to our players that we have ONE instance that we have gotten while visual studio was attached (just yesterday).
Our best assessment is that one of the IMCs had recently been removed, and had been destroyed by garbage collection roughly at the same time as reordermappings was reassessing, before it had been fully removed.
We’re using a child of EnhancedInput with Key Rebindings actually, and I can see 5 IMCs in the Applied IMC list there, and in OrderedInputContexts, I could see 6 entries, with one in the middle having bogus data.
We have actually have gotten 5 or so different callstacks that all seem to be based on different ways accessing bogus data can crash.
Right now, we’re considering bandaids for avoiding those various dangling pointers. We do not know if we can avoid those dangling pointers at this time… And an engine fix, we’re not certain if we could get improvements without doing another full engine upgrade.
We figure that while the pointer is bad, and we are likely to crash, because the theory is that this on removal, we can get away with a graceful avoidance for a subset of detectable scenarios. This is my present proposed bandaid.
for (const TPair<TObjectPtr<const UInputMappingContext>, int32>& ContextPair : OrderedInputContexts)
{
// Don't apply context specific keys immediately, allowing multiple mappings to the same key within the same context if required.
TArray<FKey> ContextAppliedKeys;
const UInputMappingContext* MappingContext = ContextPair.Key;
// START BANDAID
if (!IsValid(MappingContext))
{
continue;
}
if (!MappingContext->IsValidLowLevelFast()) //jlisco - suspected way to detect a dangling pointer... Nice in that it checks for some memory offset from null.
{
checkf(MappingContext->IsValidLowLevelFast(), TEXT("Rebuild Control Mappings encountered IMC %s that is failed IsValidLowLevelFast. Priority was %d with %d total IMCs in OrderedInputContexts. Had flags %d."),
*GetNameSafe(MappingContext), ContextPair.Value, OrderedInputContexts.Num(), (int32)(MappingContext->GetFlags()));
continue;
}
if (!MappingContext->IsValidLowLevel()) //jlisco - suspected way to detect a dangling pointer... Different from LowLevelFast in that it will check if the object array is actually pointing to this directly.
{
checkf(MappingContext->IsValidLowLevel(), TEXT("Rebuild Control Mappings encountered IMC %s that is failed IsValidLowLevel. Priority was %d with %d total IMCs in OrderedInputContexts. Had flags %d."),
*GetNameSafe(MappingContext), ContextPair.Value, OrderedInputContexts.Num(), (int32)(MappingContext->GetFlags()));
continue;
}
if (MappingContext->HasAnyFlags(RF_BeginDestroyed | RF_FinishDestroyed)) //jlisco - Known incomplete catch of a subscenario of crashing. Seems like a dangling pointer in the map somehow (other elements in the list are valid)
{
checkf(!MappingContext->HasAnyFlags(RF_FinishDestroyed), TEXT("Rebuild Control Mappings encountered IMC %s with Finish Destroyed Flag set. Priority was %d with %d total IMCs in OrderedInputContexts"),
*GetNameSafe(MappingContext), ContextPair.Value, OrderedInputContexts.Num());
checkf(!MappingContext->HasAnyFlags(RF_BeginDestroyed), TEXT("Rebuild Control Mappings encountered IMC %s with Begin Destroyed Flag set. Priority was %d with %d total IMCs in OrderedInputContexts"),
*GetNameSafe(MappingContext), ContextPair.Value, OrderedInputContexts.Num());
continue;
}
if (MappingContext->GetMappings().Num() > 10000) //jlisco - trying to catch more information about a client crash report... but probably this was another type of dangling pointer...
{
checkf(MappingContext->GetMappings().Num() <= 10000, TEXT("Rebuild Control Mappings encountered IMC %s with %d internal mappings (restricting max count at this time). Priority was %d with %d total IMCs in OrderedInputContexts"),
*GetNameSafe(MappingContext), MappingContext->GetMappings().Num(), ContextPair.Value, OrderedInputContexts.Num());
continue;
}
Are there any other recommendations here?