uc_context: tag blobs with a tamper-evident magic#2325
Closed
MarkLee131 wants to merge 1 commit into
Closed
Conversation
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.
This was referenced Apr 28, 2026
Open
Author
|
folded into #2322, closing in favor of that PR |
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.
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:
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:
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.