Skip to content

uc_context: tag blobs with a tamper-evident magic#2325

Closed
MarkLee131 wants to merge 1 commit into
unicorn-engine:devfrom
MarkLee131:fix/issue-2320-context-validation
Closed

uc_context: tag blobs with a tamper-evident magic#2325
MarkLee131 wants to merge 1 commit into
unicorn-engine:devfrom
MarkLee131:fix/issue-2320-context-validation

Conversation

@MarkLee131
Copy link
Copy Markdown

The struct uc_context that uc_context_alloc() returns is a normal heap allocation handed to the caller. Its public layout exposes context_size, fv and last_block, all of which uc_context_restore() reads back at face value. If anything mutates those bytes between save and restore (which the caller can do trivially because they own the allocation), the restore path produces:

  • a heap-buffer-overflow inside the env memcpy (context_size mutated),
  • a SEGV inside flatview_copy() (fv mutated),
  • or a SEGV inside find_ram_offset_last() on the next RAM op (last_block mutated).

The docs don't promise that a context blob is portable across processes (and the embedded host pointers obviously can't survive that), so the realistic threat model is intra-process: another bug that corrupts the heap region holding the context, or a caller that hands the same context to a different uc_engine. Both turn into a controllable pointer crash on restore.

This commit adds a per-engine context_seed in uc_struct (set at uc_open()) plus a save_magic header field in uc_context. The magic is a hash of context_size / arch / mode / snapshot_level / fv / last_block / ramblock_freed mixed with the engine seed. uc_context_save() recomputes and stores it on success. uc_context_restore() recomputes it from the current (possibly mutated) fields and refuses with UC_ERR_ARG on mismatch.

Catches:

  • restore from a never-saved context (save_magic == 0)
  • cross-engine restore (different engine seed)
  • any mutation of context_size / fv / last_block / arch / mode

uc_context_alloc() now uses g_malloc0() so that the new save_magic field, plus the existing snapshot_level / ramblock_freed / last_block, all start at zero rather than stack/heap garbage.

Adds two regression tests in tests/unit/test_ctl.c covering the four tamper cases above plus the cross-engine case. The non-tampered save -> restore round trip still passes.

Fixes #2320.

The struct uc_context that uc_context_alloc() returns is a normal heap
allocation handed to the caller. Its public layout exposes context_size,
fv and last_block, all of which uc_context_restore() reads back at face
value. If anything mutates those bytes between save and restore (which
the caller can do trivially because they own the allocation), the
restore path produces:

  - a heap-buffer-overflow inside the env memcpy (context_size mutated),
  - a SEGV inside flatview_copy() (fv mutated),
  - or a SEGV inside find_ram_offset_last() on the next RAM op
    (last_block mutated).

The docs don't promise that a context blob is portable across processes
(and the embedded host pointers obviously can't survive that), so the
realistic threat model is intra-process: another bug that corrupts the
heap region holding the context, or a caller that hands the same
context to a different uc_engine. Both turn into a controllable
pointer crash on restore.

This commit adds a per-engine context_seed in uc_struct (set at
uc_open()) plus a save_magic header field in uc_context. The magic is
a hash of context_size / arch / mode / snapshot_level / fv /
last_block / ramblock_freed mixed with the engine seed.
uc_context_save() recomputes and stores it on success.
uc_context_restore() recomputes it from the current (possibly
mutated) fields and refuses with UC_ERR_ARG on mismatch.

Catches:
  - restore from a never-saved context (save_magic == 0)
  - cross-engine restore (different engine seed)
  - any mutation of context_size / fv / last_block / arch / mode

uc_context_alloc() now uses g_malloc0() so that the new save_magic
field, plus the existing snapshot_level / ramblock_freed / last_block,
all start at zero rather than stack/heap garbage.

Adds two regression tests in tests/unit/test_ctl.c covering the four
tamper cases above plus the cross-engine case. The non-tampered
save -> restore round trip still passes.

Fixes unicorn-engine#2320.
@MarkLee131
Copy link
Copy Markdown
Author

folded into #2322, closing in favor of that PR

@MarkLee131 MarkLee131 closed this Apr 29, 2026
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