Skip to content

emu_start does not honor until after TB chaining across runs (order-dependent fall-through) #2341

Description

@retrocpugeek

Summary

On a single Uc instance, the order in which two adjacent functions are emulated changes the result: running function B and then function A causes A's emulation to run past its until address into B's code, eventually faulting with UC_ERR_FETCH_UNMAPPED. Running A first works correctly.

Environment

  • Unicorn master @ 7c5db941 (built from source, x86-only config), Python bindings, UC_ARCH_X86 / UC_MODE_64, Linux x86-64
  • Verified the fault is independent of any local patches by building pristine master in a clean worktree and loading it via LIBUNICORN_PATH.

Details

func_a and func_b are laid out back to back, so func_a_end == func_b_start. When func_a is emulated, its ret pops a seeded return address equal to func_a_end, which is also the until passed to emu_start, so emulation should stop there.

Emulating func_b first chains its translation blocks. On the subsequent func_a run, func_a's terminal block is already chain-linked to the block at func_a_end (func_b's entry), so the chained jump bypasses the per-block until check. Instead of stopping, control falls through into func_b; its epilogue then returns to a poisoned stack slot (0xDEADBEEF) and faults.

Calling uc.ctl_flush_tb() between the two runs works around it (drops the stale chain), confirming TB chaining as the cause.

Reproducer

A self-contained regress-style reproducer (3 cases: working order, faulting order, and the flush workaround):

https://github.com/retrocpugeek/unicorn/blob/8f5cb0d4/tests/regress/minimal_tb_chain_repro.py

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions