Skip to content

[repro, do not merge] MIPS delay-slot hflag leak via UC_HOOK_MEM_READ_UNMAPPED (#2272) - #2352

Draft
retrocpugeek wants to merge 1 commit into
unicorn-engine:devfrom
retrocpugeek:repro/mips-mem-unmapped-delay-slot-2272
Draft

[repro, do not merge] MIPS delay-slot hflag leak via UC_HOOK_MEM_READ_UNMAPPED (#2272)#2352
retrocpugeek wants to merge 1 commit into
unicorn-engine:devfrom
retrocpugeek:repro/mips-mem-unmapped-delay-slot-2272

Conversation

@retrocpugeek

Copy link
Copy Markdown

This is a standalone repro, not a fix — please do not merge. It exists to make the bug easy to review/run. It accompanies the discussion on #2347.

What it shows

The spurious EXCP_RI from #2272 is not specific to UC_TLB_VIRTUAL. The same branch-delay hflag leak is reachable in the ordinary soft-MMU through a UC_HOOK_MEM_READ_UNMAPPED hook.

The added test (tests/unit/test_mips.c::test_mips_mem_read_unmapped_delay_slot_2272) runs a big-endian MIPS64 program whose b has a load in its delay slot. The load faults on an unmapped page; a UC_HOOK_MEM_READ_UNMAPPED hook maps the page and returns true, so the load resumes in place. Before the hook is called, cputlb.c runs cpu_restore_state() to sync the PC (the documented hook contract), and on MIPS that re-applies the branch-delay hflags (MIPS_HFLAG_B / MIPS_HFLAG_BDS32) into the live env. Nothing clears them, so the branch target (itself a bne) is decoded as if it sat in a delay slot and raises a spurious Reserved Instruction exception (surfaced as UC_ERR_EXCEPTION; intno 20 via UC_HOOK_INTR).

The test asserts the correct behavior and therefore fails on current mainline — that is the point of a repro:

Test test_mips_mem_read_unmapped_delay_slot_2272... [ FAILED ]
  test_mips.c: Check __err == UC_ERR_OK... failed
    Unhandled CPU exception (UC_ERR_EXCEPTION)
  test_mips.c: Check r_v1 == 7... failed

Pre-mapping the page so no hook fires makes the same program complete correctly, isolating the cause to the hook-triggered cpu_restore_state().

Relationship to #2347

#2347 fixes the UC_TLB_VIRTUAL fill path by not restoring state on a successful fill. That does not cover this path: cputlb.c's cpu_restore_state() is deliberate (it honours the synced-PC contract for user hooks), so it can't simply be dropped. The proper fix is lower-level — clear the transient branch hflags after a resume-in-place restore (in restore_state_to_opc() / its MIPS callers) — which is a broader change than the #2347 regression fix. Filing this separately so the root cause is documented and runnable; happy to open a fix PR if the direction is agreed.

🤖 Generated with Claude Code

unicorn-engine#2272)

Demonstrates that the spurious EXCP_RI from issue unicorn-engine#2272 is not specific to
UC_TLB_VIRTUAL. In the ordinary soft-MMU, a memory access in a branch delay
slot that faults on unmapped memory and is serviced by a
UC_HOOK_MEM_READ_UNMAPPED hook (which maps the page and returns true) resumes
in place after cputlb.c's cpu_restore_state() has re-applied the branch-delay
hflags (MIPS_HFLAG_B / MIPS_HFLAG_BDS32) into the live env. The branch target
is then decoded as if it sat in a delay slot, raising a spurious Reserved
Instruction exception (surfaced as UC_ERR_EXCEPTION).

This test asserts the correct behavior and therefore FAILS on current
mainline; it is a standalone repro, deliberately kept out of the vtlb fix PR.
Unlike the vtlb fill, that cpu_restore_state() call is deliberate (it honours
the synced-PC contract for user hooks), so the proper fix is lower-level
(clear the transient branch hflags after a resume-in-place restore) rather
than dropping the call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant