Repository navigation
Perform the access after a handled *_PROT hook grants it - #2460
Open
tom-effernelli wants to merge 1 commit into
Open
tom-effernelli wants to merge 1 commit into
tom-effernelli wants to merge 1 commit into
Conversation
store_helper() and load_helper() read the TLB entry into a local tlb_addr before running the UC_HOOK_MEM_WRITE_PROT / READ_PROT hooks. When the hook calls uc_mem_protect() and toggles write access, the memory region commit flushes the TLB, but the handled path only checked the stale local value, which still "hits", so the entry was never refilled: - on a store, the stale TLB_DISCARD_WRITE still applied and the write that triggered the hook was silently dropped, although the hook made the page writable and returned true; - on a load, the access went through the flushed entry's addend and could crash the host. After a handled hook, look the region up again (stop with UC_ERR_MAP if the hook unmapped it) and re-read the TLB entry, refilling it on a miss. This implements the contract documented in unicorn.h: returning true continues the access once the hook made the memory accessible. A hook that returns true without granting access still drops the write, as before. Fixes unicorn-engine#2368 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sdo1fYpWGRmybV5et7sDxu
This branch has not been deployed
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.
Fixes #2368.
The bug
When a
UC_HOOK_MEM_WRITE_PROThook makes the page writable withuc_mem_protect()and returnstrue, the write that triggered the hook is silently dropped; later writes land.store_helper()reads the TLB entry into a localtlb_addrbefore running the hook. Toggling write access inuc_mem_protect()flushes the TLB (memory region commit →tcg_commit()→tlb_flush()), but the handled path only checks the stale local value, which still "hits", so the entry is never refilled and the staleTLB_DISCARD_WRITEdrops the store. Auc_ctl_flush_tlb()inside the hook doesn't help, for the same reason.load_helper()has the same pattern forUC_HOOK_MEM_READ_PROT: if the hook toggles write access (e.g.NONE→READ|WRITE), the load goes through the flushed entry's addend and can crash the host.The fix
After a handled
*_PROThook, look the region up again (stop withUC_ERR_MAPif the hook unmapped it) and re-read the TLB entry, refilling it on a miss. This implements the contract documented ininclude/unicorn/unicorn.h: "returning true to continue execution will only work if the accessed memory is made accessible with the correct permissions during the hook."Deliberately unchanged / out of scope:
truewithout granting access still drops the write, as before;*_UNMAPPED-handled branches,READ_PROTon the second page of split loads, and fetch (Fixing stale code tlb after fetch-prot hook #2365).Tests
Three new tests in
tests/unit/test_mem.c, which fail before the change (the first two lose the write, the third crashes with an access violation) and pass after:test_mem_protect_write_prot_hook_grant(the reproducer from writes to readonly mem get discarded when MEM_WRITE_PROT hook returns true #2368)test_mem_protect_write_prot_hook_grant_cross_page(a store split across a writable and a read-only page)test_mem_protect_read_prot_hook_grantTested locally on Windows with MinGW (MSYS2 gcc), x86 only:
test_mem,test_x86andtest_ctlpass (excepttest_mem_read_and_write_large_memory_block, which needs ARM64, not built). I'm relying on CI for the other targets.Note on #2415
Draft PR #2415 by @kibertoad addresses this more broadly, and also changes the hook semantics (returning
trueperforms the access even without granting it). This PR is a minimal, behaviour-preserving fix offered as something small to review and land in the meantime; I'm happy to close it if you prefer #2415.This change was prepared with the help of Claude Code.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Sdo1fYpWGRmybV5et7sDxu