summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2012-01-13 17:05:41 +0100
committerYury Usishchev <y.usishchev@samsung.com>2014-12-10 13:47:57 +0300
commite2f4cb071c5df20dc8b608099fce037bb4d95ba5 (patch)
tree24eaaffbc0bf8fc3df461d3e0cc04456c6285dcc
parente7182f249406f06c4be4711614ca82a7a12821d4 (diff)
downloadqemu-e2f4cb071c5df20dc8b608099fce037bb4d95ba5.tar.gz
qemu-e2f4cb071c5df20dc8b608099fce037bb4d95ba5.tar.bz2
qemu-e2f4cb071c5df20dc8b608099fce037bb4d95ba5.zip
linux-user: fix segfault deadlock
When entering the guest we take a lock to ensure that nobody else messes with our TB chaining while we're doing it. If we get a segfault inside that code, we manage to work on, but will not unlock the lock. This patch forces unlocking of that lock in the segv handler. I'm not sure this is the right approach though. Maybe we should rather make sure we don't segfault in the code? I would greatly appreciate someone more intelligible than me to look at this :). Example code to trigger this is at: http://csgraf.de/tmp/conftest.c Reported-by: Fabio Erculiani <lxnay@sabayon.org> Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Andreas Färber <afaerber@suse.de>
-rw-r--r--include/exec/spinlock.h10
-rw-r--r--user-exec.c4
2 files changed, 14 insertions, 0 deletions
diff --git a/include/exec/spinlock.h b/include/exec/spinlock.h
index a72edda1d..e460e129a 100644
--- a/include/exec/spinlock.h
+++ b/include/exec/spinlock.h
@@ -24,6 +24,12 @@
#include <pthread.h>
#define spin_lock pthread_mutex_lock
#define spin_unlock pthread_mutex_unlock
+static inline void spin_unlock_safe(pthread_mutex_t *lock)
+{
+ /* unlocking an unlocked mutex results in undefined behavior */
+ pthread_mutex_trylock(lock);
+ pthread_mutex_unlock(lock);
+}
#define spinlock_t pthread_mutex_t
#define SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER
@@ -46,4 +52,8 @@ static inline void spin_unlock(spinlock_t *lock)
{
}
+static inline void spin_unlock_safe(spinlock_t *lock)
+{
+}
+
#endif
diff --git a/user-exec.c b/user-exec.c
index 1ff8673ac..22f96925e 100644
--- a/user-exec.c
+++ b/user-exec.c
@@ -94,6 +94,10 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address,
qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
pc, address, is_write, *(unsigned long *)old_set);
#endif
+
+ /* Maybe we're still holding the TB fiddling lock? */
+ spin_unlock_safe(&tcg_ctx.tb_ctx.tb_lock);
+
/* XXX: locking issue */
if (is_write && h2g_valid(address)
&& page_unprotect(h2g(address), pc, puc)) {