From: Ilya Leoshkevich <iii@linux.ibm.com>
To: Richard Henderson <richard.henderson@linaro.org>,
Paolo Bonzini <pbonzini@redhat.com>
Cc: "Peter Maydell" <peter.maydell@linaro.org>,
"Ilya Leoshkevich" <iii@linux.ibm.com>,
qemu-devel@nongnu.org,
"Dr . David Alan Gilbert" <dgilbert@redhat.com>,
"Christian Borntraeger" <borntraeger@de.ibm.com>,
"Stefan Hajnoczi" <stefanha@redhat.com>,
"Alex Bennée" <alex.bennee@linaro.org>,
"Andreas Krebbel" <krebbel@linux.ibm.com>
Subject: [PATCH RFC 1/1] accel/tcg: Clear PAGE_WRITE before translation
Date: Thu, 5 Aug 2021 00:46:33 +0200 [thread overview]
Message-ID: <20210804224633.154083-2-iii@linux.ibm.com> (raw)
In-Reply-To: <20210804224633.154083-1-iii@linux.ibm.com>
translate_insn() implementations fetch instruction bytes piecemeal,
which can cause qemu-user to generate inconsistent translations if
another thread modifies them concurrently [1].
Fix by marking translation block pages non-writable earlier.
[1] https://lists.nongnu.org/archive/html/qemu-devel/2021-08/msg00644.html
Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
---
accel/tcg/translate-all.c | 59 +++++++++++++++++++++---------------
accel/tcg/translator.c | 26 ++++++++++++++--
include/exec/translate-all.h | 1 +
3 files changed, 59 insertions(+), 27 deletions(-)
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index bbfcfb698c..fb9ebfad9e 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1297,31 +1297,8 @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb,
invalidate_page_bitmap(p);
#if defined(CONFIG_USER_ONLY)
- if (p->flags & PAGE_WRITE) {
- target_ulong addr;
- PageDesc *p2;
- int prot;
-
- /* force the host page as non writable (writes will have a
- page fault + mprotect overhead) */
- page_addr &= qemu_host_page_mask;
- prot = 0;
- for (addr = page_addr; addr < page_addr + qemu_host_page_size;
- addr += TARGET_PAGE_SIZE) {
-
- p2 = page_find(addr >> TARGET_PAGE_BITS);
- if (!p2) {
- continue;
- }
- prot |= p2->flags;
- p2->flags &= ~PAGE_WRITE;
- }
- mprotect(g2h_untagged(page_addr), qemu_host_page_size,
- (prot & PAGE_BITS) & ~PAGE_WRITE);
- if (DEBUG_TB_INVALIDATE_GATE) {
- printf("protecting code page: 0x" TB_PAGE_ADDR_FMT "\n", page_addr);
- }
- }
+ /* translator_loop() must have made all TB pages non-writable */
+ assert(!(p->flags & PAGE_WRITE));
#else
/* if some code is already present, then the pages are already
protected. So we handle the case where only the first TB is
@@ -2394,6 +2371,38 @@ int page_check_range(target_ulong start, target_ulong len, int flags)
return 0;
}
+void page_protect(tb_page_addr_t page_addr)
+{
+ target_ulong addr;
+ PageDesc *p;
+ int prot;
+
+ p = page_find(page_addr >> TARGET_PAGE_BITS);
+ if (p && (p->flags & PAGE_WRITE)) {
+ /*
+ * Force the host page as non writable (writes will have a page fault +
+ * mprotect overhead).
+ */
+ page_addr &= qemu_host_page_mask;
+ prot = 0;
+ for (addr = page_addr; addr < page_addr + qemu_host_page_size;
+ addr += TARGET_PAGE_SIZE) {
+
+ p = page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ continue;
+ }
+ prot |= p->flags;
+ p->flags &= ~PAGE_WRITE;
+ }
+ mprotect(g2h_untagged(page_addr), qemu_host_page_size,
+ (prot & PAGE_BITS) & ~PAGE_WRITE);
+ if (DEBUG_TB_INVALIDATE_GATE) {
+ printf("protecting code page: 0x" TB_PAGE_ADDR_FMT "\n", page_addr);
+ }
+ }
+}
+
/* called from signal handler: invalidate the code and unprotect the
* page. Return 0 if the fault was not handled, 1 if it was handled,
* and 2 if it was handled but the caller must cause the TB to be
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index c53a7f8e44..bfbe7d7ccf 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -14,6 +14,7 @@
#include "exec/exec-all.h"
#include "exec/gen-icount.h"
#include "exec/log.h"
+#include "exec/translate-all.h"
#include "exec/translator.h"
#include "exec/plugin-gen.h"
#include "sysemu/replay.h"
@@ -47,6 +48,10 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
{
uint32_t cflags = tb_cflags(tb);
bool plugin_enabled;
+ bool stop = false;
+#ifdef CONFIG_USER_ONLY
+ target_ulong page_addr = -1;
+#endif
/* Initialize DisasContext */
db->tb = tb;
@@ -71,6 +76,21 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY);
while (true) {
+#ifdef CONFIG_USER_ONLY
+ /*
+ * Make the page containing the next instruction non-writable in order
+ * to get a consistent translation if another thread is modifying the
+ * code while translate_insn() fetches the instruction bytes piecemeal.
+ * Writer threads will wait for mmap_lock() in page_unprotect().
+ */
+ if ((db->pc_next & TARGET_PAGE_MASK) != page_addr) {
+ page_addr = db->pc_next & TARGET_PAGE_MASK;
+ page_protect(page_addr);
+ }
+#endif
+ if (stop) {
+ break;
+ }
db->num_insns++;
ops->insn_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
@@ -95,7 +115,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* Stop translation if translate_insn so indicated. */
if (db->is_jmp != DISAS_NEXT) {
- break;
+ stop = true;
+ continue;
}
/*
@@ -110,7 +131,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
db->is_jmp = DISAS_TOO_MANY;
- break;
+ stop = true;
+ continue;
}
}
diff --git a/include/exec/translate-all.h b/include/exec/translate-all.h
index a557b4e2bb..9f646389af 100644
--- a/include/exec/translate-all.h
+++ b/include/exec/translate-all.h
@@ -33,6 +33,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);
#ifdef CONFIG_USER_ONLY
+void page_protect(tb_page_addr_t page_addr);
int page_unprotect(target_ulong address, uintptr_t pc);
#endif
--
2.31.1
next prev parent reply other threads:[~2021-08-04 22:47 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-08-04 22:46 [PATCH RFC 0/1] accel/tcg: Clear PAGE_WRITE before translation Ilya Leoshkevich
2021-08-04 22:46 ` Ilya Leoshkevich [this message]
2021-08-05 0:30 ` [PATCH RFC 1/1] " Richard Henderson
2021-08-05 10:56 ` Ilya Leoshkevich
2021-08-05 16:59 ` Richard Henderson
2021-08-05 20:36 ` Ilya Leoshkevich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210804224633.154083-2-iii@linux.ibm.com \
--to=iii@linux.ibm.com \
--cc=alex.bennee@linaro.org \
--cc=borntraeger@de.ibm.com \
--cc=dgilbert@redhat.com \
--cc=krebbel@linux.ibm.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=stefanha@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).