summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2012-05-10 22:40:10 +0000
committerBlue Swirl <blauwirbel@gmail.com>2012-05-19 15:49:40 +0000
commit77a8f1a5125457d845fac6aa0c2e1e2681d94f07 (patch)
tree48ebe6991720d75db8c9b952dc4ed474ef586acd
parent4636b9d1466adde156d469c92d6e7cae7311b81e (diff)
downloadfocaccia-qemu-77a8f1a5125457d845fac6aa0c2e1e2681d94f07.tar.gz
focaccia-qemu-77a8f1a5125457d845fac6aa0c2e1e2681d94f07.zip
linux-user: Fix stale tbs after mmap
If we execute linux-user code that does the following:

  * A = mmap()
  * execute code in A
  * munmap(A)
  * B = mmap(), but mmap returns the same address as A
  * execute code in B

we end up executing a stale cached tb that contains translated code
from A, while we want new code from B.

This patch adds a TB flush for mmap'ed regions, before we return them,
avoiding the whole issue. It also adds a flush for munmap, so that we
don't execute stale TBs instead of getting a segfault.

Reported-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Acked-by: Riku Voipio <riku.voipio@linaro.org>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
-rw-r--r--exec-all.h2
-rw-r--r--exec.c17
-rw-r--r--linux-user/mmap.c6
3 files changed, 24 insertions, 1 deletions
diff --git a/exec-all.h b/exec-all.h
index c1b7e1f263..9bda7f7354 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -96,6 +96,8 @@ void QEMU_NORETURN cpu_loop_exit(CPUArchState *env1);
 int page_unprotect(target_ulong address, uintptr_t pc, void *puc);
 void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
                                    int is_cpu_write_access);
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end,
+                              int is_cpu_write_access);
 #if !defined(CONFIG_USER_ONLY)
 /* cputlb.c */
 void tlb_flush_page(CPUArchState *env, target_ulong addr);
diff --git a/exec.c b/exec.c
index 0607c9b6aa..a0494c72bf 100644
--- a/exec.c
+++ b/exec.c
@@ -1075,6 +1075,23 @@ TranslationBlock *tb_gen_code(CPUArchState *env,
     return tb;
 }
 
+/*
+ * invalidate all TBs which intersect with the target physical pages
+ * starting in range [start;end[. NOTE: start and end may refer to
+ * different physical pages. 'is_cpu_write_access' should be true if called
+ * from a real cpu write access: the virtual CPU will exit the current
+ * TB if code is modified inside this TB.
+ */
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end,
+                              int is_cpu_write_access)
+{
+    while (start < end) {
+        tb_invalidate_phys_page_range(start, end, is_cpu_write_access);
+        start &= TARGET_PAGE_MASK;
+        start += TARGET_PAGE_SIZE;
+    }
+}
+
 /* invalidate all TBs which intersect with the target physical page
    starting in range [start;end[. NOTE: start and end must refer to
    the same physical page. 'is_cpu_write_access' should be true if called
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 7125d1cd4b..d9468fea90 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -573,6 +573,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
     page_dump(stdout);
     printf("\n");
 #endif
+    tb_invalidate_phys_range(start, start + len, 0);
     mmap_unlock();
     return start;
 fail:
@@ -675,8 +676,10 @@ int target_munmap(abi_ulong start, abi_ulong len)
         }
     }
 
-    if (ret == 0)
+    if (ret == 0) {
         page_set_flags(start, start + len, 0);
+        tb_invalidate_phys_range(start, start + len, 0);
+    }
     mmap_unlock();
     return ret;
 }
@@ -754,6 +757,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
         page_set_flags(old_addr, old_addr + old_size, 0);
         page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
     }
+    tb_invalidate_phys_range(new_addr, new_addr + new_size, 0);
     mmap_unlock();
     return new_addr;
 }