mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-30 13:24:19 +08:00
Fix TLS-load patcher corrupting short jumps immediately before FS:[0] reads (#838)
The linear scanner consumed leading 0x66 bytes without checking that the candidate starts on an instruction boundary. A short jump whose disp8 is 0x66 (EB 66) directly followed by a 66-prefixed FS:[0] load made the patcher treat the displacement as a prefix and write its call opcode over it, rewriting 'jmp forward past the TLS access' into 'jmp backward' -- an infinite loop. GTA V's AGC resource destructors hit exactly this shape and leaked the entire heap inside a container drain before any frame was presented. Reject candidates whose preceding byte is 0xEB: a bare EB can never be the last byte of a valid instruction, so such a position is provably mid-instruction. The outer byte scan then retries at the next offset, which is the true boundary, and the patch lands correctly.
This commit is contained in:
@@ -3200,7 +3200,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
{
|
{
|
||||||
nint address = (nint)(ptr + i);
|
nint address = (nint)(ptr + i);
|
||||||
int remainingBytes = scanBytes - i;
|
int remainingBytes = scanBytes - i;
|
||||||
if (TryPatchTlsLoadInstruction(address, ptr + i, remainingBytes))
|
if (TryPatchTlsLoadInstruction(address, ptr + i, remainingBytes, i))
|
||||||
{
|
{
|
||||||
num3++;
|
num3++;
|
||||||
}
|
}
|
||||||
@@ -3343,13 +3343,21 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe bool TryPatchTlsLoadInstruction(nint address, byte* source, int availableLength)
|
private unsafe bool TryPatchTlsLoadInstruction(nint address, byte* source, int availableLength, int regionOffset)
|
||||||
{
|
{
|
||||||
if (availableLength < MinTlsPatchInstructionBytes)
|
if (availableLength < MinTlsPatchInstructionBytes)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A bare 0xEB (JMP rel8) always owns a disp8 after it, so it can
|
||||||
|
// never be the last byte of a valid instruction. If it precedes our
|
||||||
|
// candidate, we're mid-instruction: reject and let the scan retry.
|
||||||
|
if (regionOffset >= 1 && source[-1] == 0xEB)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
while (offset < availableLength && source[offset] == 0x66)
|
while (offset < availableLength && source[offset] == 0x66)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user