Summary
LatteSHRC_RemoveFromCaches (src/Cafe/HW/Latte/Core/LatteShader.cpp, ~line 182) can spin forever when the shader being removed is not the first entry of its cache chain, hard-hanging the emulator.
Details
The chain-walk loop never advances shaderChain:
LatteDecompilerShader* shaderChain = baseIt->second;
while (shaderChain->next)
{
if (shaderChain->next == shader)
{
shaderChain->next = shaderChain->next->next;
removed = true;
break;
}
// shaderChain is never advanced -> infinite loop if the target isn't the first 'next'
}
When shaderChain->next != shader, neither the loop condition nor the body changes, so it loops forever. It only terminates when the target happens to be the head's direct next.
Trigger
Reachable from the async deleteShader path (LatteAsyncCommands → LatteSHRC_RemoveFromCacheByHash → RemoveFromCaches) when a shader resolves to a non-head aux variant of a chain with 2+ members.
Suggested direction
Advance the pointer at the end of the loop body (shaderChain = shaderChain->next;).
Summary
LatteSHRC_RemoveFromCaches(src/Cafe/HW/Latte/Core/LatteShader.cpp, ~line 182) can spin forever when the shader being removed is not the first entry of its cache chain, hard-hanging the emulator.Details
The chain-walk loop never advances
shaderChain:When
shaderChain->next != shader, neither the loop condition nor the body changes, so it loops forever. It only terminates when the target happens to be the head's directnext.Trigger
Reachable from the async
deleteShaderpath (LatteAsyncCommands→LatteSHRC_RemoveFromCacheByHash→RemoveFromCaches) when a shader resolves to a non-head aux variant of a chain with 2+ members.Suggested direction
Advance the pointer at the end of the loop body (
shaderChain = shaderChain->next;).