Three aarch64 defects in the new dynarec - #826
Merged
LibretroAdmin merged 3 commits intoAug 22, 2026
Merged
Conversation
genjmp() walks jump_table_symbols with sizeof(table)/4. The elements are uintptr_t, eight bytes here, so the loop ran twice as many iterations as the table has entries and read past its end.
emit_xkphys_fold folds the base onto the segment with
emit_orimm(HOST_TEMPREG, 0xA0000000, HOST_TEMPREG);
0xA0000000 has bits 31 and 29 set, which is not a contiguous run and so
not a valid aarch64 logical immediate. genimm() rejects it and
emit_orimm takes its fallback path, which asserts rs != HOST_TEMPREG and
imm < 65536 - both false here - and then materialises the value into
HOST_TEMPREG, destroying the source register before reading it. Release
builds define NDEBUG, so the asserts vanish and the fold silently emits
wrong code for every memory access whose base register is 64-bit wide.
Set the two bits with one instruction each. A single set bit is a
contiguous run, so both encode directly and the fallback is never
reached. x86 pays one extra instruction on a cold path.
clean_registers() deliberately understates wasdirty: it clears the bit of any register the block will rewrite before the next block reads it, because that write makes the earlier value irrelevant. That reasoning holds for the fall-through path and not for an exception, which leaves the block at an arbitrary point - possibly before the rewrite. The six exception paths wrote back i_regs->wasdirty verbatim and so skipped exactly those registers, handing the interpreter a stale register file. Add wb_exception_dirtys(), which restores the cleared bits before calling wb_dirtys(), and use it on all six.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent defects on aarch64, found while running this core on a
Raspberry Pi 5.
1. The jump table scan runs off the end
genjmp()bounds its loop withsizeof(jump_table_symbols)/4. The elementsare
uintptr_t, eight bytes here, so it iterates twice as many times as thetable has entries.
2. The XKPHYS fold emits an immediate aarch64 cannot encode
emit_xkphys_fold()callsemit_orimm(HOST_TEMPREG, 0xA0000000, HOST_TEMPREG). Bits 31 and 29 are not a contiguous run, so this is not avalid logical immediate;
genimm()rejects it andemit_orimmtakes itsfallback, which asserts
rs != HOST_TEMPREGandimm < 65536— both falsehere — then materialises the value into
HOST_TEMPREG, destroying the sourcebefore reading it. Release builds define
NDEBUG, so the asserts vanish andthe fold silently emits wrong code for every memory access with a 64-bit base
register.
Setting the two bits separately encodes directly and never reaches the
fallback. x86 pays one extra instruction on a cold path.
3. Exception paths skip registers
clean_registers()clearedclean_registers()deliberately understateswasdirty: it clears the bit ofany register the block will rewrite before the next block reads it. That
holds for the fall-through path, not for an exception, which leaves the block
at an arbitrary point. The six exception paths wrote back
wasdirtyverbatimand handed the interpreter a stale register file.
Note on verification: defects 1 and 3 were found from misbehaviour and
confirmed by fixing them. Defect 2 is silent — I found it by reading
emit_orimmafter the fold landed, and have no reproduction case to offer.