aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanislav Shmarov <snarpix@gmail.com>2016-07-07 11:33:12 +0300
committerRichard Henderson <rth@twiddle.net>2016-07-08 13:17:38 -0700
commit7399a337e4126f7c8c8af3336726f001378c4798 (patch)
treee58f0235241cecdfe2f5b711dcd287dbd70a071e
parent81daabaf7a572f138a8b88ba6eea556bdb0cce46 (diff)
translate-all: Fix user-mode self-modifying code in 2 page long TB
In user-mode emulation Translation Block can consist of 2 guest pages. In that case QEMU also mprotects 2 host pages that are dedicated for guest memory, containing instructions. QEMU detects self-modifying code with SEGFAULT signal processing. In case if instruction in 1st page is modifying memory of 2nd page (or vice versa) QEMU will mark 2nd page with PAGE_WRITE, invalidate TB, generate new TB contatining 1 guest instruction and exit to CPU loop. QEMU won't call mprotect, and new TB will cause same SEGFAULT. Page will have both PAGE_WRITE_ORG and PAGE_WRITE flags, so QEMU will handle the signal as guest binary problem, and exit with guest SEGFAULT. Solution is to do following: In case if current TB was invalidated continue to invalidate TBs from remaining guest pages and mark pages as PAGE_WRITE. After that disable host page protection with mprotect. If current tb was invalidated longjmp to main loop. That is more efficient, since we won't get SEGFAULT when executing new TB. Reviewed-by: Sergey Fedorov <sergey.fedorov@linaro.org> Signed-off-by: Stanislav Shmarov <snarpix@gmail.com> Message-Id: <1467880392-1043630-1-git-send-email-snarpix@gmail.com> Signed-off-by: Richard Henderson <rth@twiddle.net>
-rw-r--r--translate-all.c10
1 files changed, 5 insertions, 5 deletions
diff --git a/translate-all.c b/translate-all.c
index eaa95e4cd7..0d47c1c0cf 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -2000,6 +2000,7 @@ int page_check_range(target_ulong start, target_ulong len, int flags)
int page_unprotect(target_ulong address, uintptr_t pc)
{
unsigned int prot;
+ bool current_tb_invalidated;
PageDesc *p;
target_ulong host_start, host_end, addr;
@@ -2021,6 +2022,7 @@ int page_unprotect(target_ulong address, uintptr_t pc)
host_end = host_start + qemu_host_page_size;
prot = 0;
+ current_tb_invalidated = false;
for (addr = host_start ; addr < host_end ; addr += TARGET_PAGE_SIZE) {
p = page_find(addr >> TARGET_PAGE_BITS);
p->flags |= PAGE_WRITE;
@@ -2028,10 +2030,7 @@ int page_unprotect(target_ulong address, uintptr_t pc)
/* and since the content will be modified, we must invalidate
the corresponding translated code. */
- if (tb_invalidate_phys_page(addr, pc)) {
- mmap_unlock();
- return 2;
- }
+ current_tb_invalidated |= tb_invalidate_phys_page(addr, pc);
#ifdef DEBUG_TB_CHECK
tb_invalidate_check(addr);
#endif
@@ -2040,7 +2039,8 @@ int page_unprotect(target_ulong address, uintptr_t pc)
prot & PAGE_BITS);
mmap_unlock();
- return 1;
+ /* If current TB was invalidated return to main loop */
+ return current_tb_invalidated ? 2 : 1;
}
mmap_unlock();
return 0;