Skip to contentSkip to conversation

"cpu-exec.c: avoid AREG0 use" breaks x86 emulation on x86-64

Qemu-devel Applied Jan Kiszka 8 participants 18 messages Jun 30, 2011 – Aug 11, 2011

lore
Share & tools
lore

Jan Kiszka

author
#0 lore raw

Hi Blue,

commit cea5f9a28f breaks here, just starting qemu without any parameters:

Starting program: qemu-system-x86_64
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault. 0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6 (gdb) bt

#0  0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6
#1  0x00000000004eb96c in cpu_x86_exec (env=0x11d09a0) at cpu-exec.c:233
#2  0x000000000040f056 in tcg_cpu_exec (env=0x11d09a0) at cpus.c:1059
#3  cpu_exec_all () at cpus.c:1100
#4  0x000000000058cfcb in main_loop () at vl.c:1380
#5  main (argc=<value optimized out>, argv=<value optimized out>, envp=<value optimized out>) at vl.c:3318

Please have a look.

Jan

Signature · 2 lines
Siemens AG, Corporate Technology, CT T DE IT 1
Corporate Competence Center Embedded Linux
#1 lore raw

Am 30.06.2011 18:47, schrieb Jan Kiszka:

Quoted 18 lines

Hi Blue,

commit cea5f9a28f breaks here, just starting qemu without any parameters:

Starting program: qemu-system-x86_64
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6
(gdb) bt
#0 0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6
#1 0x00000000004eb96c in cpu_x86_exec (env=0x11d09a0) at cpu-exec.c:233
#2 0x000000000040f056 in tcg_cpu_exec (env=0x11d09a0) at cpus.c:1059
#3 cpu_exec_all () at cpus.c:1100
#4 0x000000000058cfcb in main_loop () at vl.c:1380
#5 main (argc=<value optimized out>, argv=<value optimized out>, envp=<value optimized out>) at vl.c:3318

Quoted 3 lines

Please have a look.

Jan

Hi,

the same commit works here. Maybe this is caused by different timing? You might try -singlestep. Does QEMU emulate any guest code, or is logging output empty when QEMU was run with -d in_asm?

Regards,
Stefan

#2 lore raw
In reply to Jan Kiszka
Quoted 18 lines · Jan Kiszka

Hi Blue,

commit cea5f9a28f breaks here, just starting qemu without any parameters:

Starting program: qemu-system-x86_64
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6
#1  0x00000000004eb96c in cpu_x86_exec (env=0x11d09a0) at cpu-exec.c:233
#2  0x000000000040f056 in tcg_cpu_exec (env=0x11d09a0) at cpus.c:1059
#3  cpu_exec_all () at cpus.c:1100
#4  0x000000000058cfcb in main_loop () at vl.c:1380
#5  main (argc=<value optimized out>, argv=<value optimized out>, envp=<value optimized out>) at vl.c:3318

Quoted 9 lines

Please have a look.

Jan

--
Siemens AG, Corporate Technology, CT T DE IT 1
Corporate Competence Center Embedded Linux

It works on gcc 4.4.5 x64. Which version is your gcc?

#3 lore raw
Quoted 21 lines · TeLeMan
Quoted 20 lines · Jan Kiszka

Hi Blue,

commit cea5f9a28f breaks here, just starting qemu without any parameters:

Starting program: qemu-system-x86_64
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff39ac770 in __sigsetjmp () from /lib64/libc.so.6
#1  0x00000000004eb96c in cpu_x86_exec (env=0x11d09a0) at cpu-exec.c:233
#2  0x000000000040f056 in tcg_cpu_exec (env=0x11d09a0) at cpus.c:1059
#3  cpu_exec_all () at cpus.c:1100
#4  0x000000000058cfcb in main_loop () at vl.c:1380
#5  main (argc=<value optimized out>, argv=<value optimized out>, envp=<value optimized out>) at vl.c:3318

Please have a look.

Quoted 8 lines
Quoted 7 lines

Jan

--
Siemens AG, Corporate Technology, CT T DE IT 1
Corporate Competence Center Embedded Linux

It works on gcc 4.4.5 x64. Which version is your gcc?

Works for me too. The change added a second argument to tcg_qemu_tb_exec() and in tcg/i386/* we assume that the second register is %rsi. Isn't that so also with your gcc?

#4 lore raw

PATCH 1/1 tcg: Reload local variables after return from longjmp

From: Jan Kiszka <[email protected]>

Recent compilers look deep into cpu_exec, find longjmp as a noreturn function and decide to smash some stack variables as they won't be used again. This may lead to env becoming invalid after return from setjmp, causing crashes. Fix it by reloading env from cpu_single_env in that case.

---
 cpu-exec.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
cpu-exec.c +4 -0
index 20e3ec4..de0d716 100644
@@ -587,6 +587,10 @@ int cpu_exec(CPUState *env)
                 /* reset soft MMU for next block (it can currently
                    only be set by a memory fault) */
             } /* for(;;) */
+        } else {
+            /* Reload env after longjmp - the compiler may have smashed all
+             * local variables as longjmp is marked 'noreturn'. */
+            env = cpu_single_env;
         }
     } /* for(;;) */
 
#5 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 7 lines · Jan Kiszka

From: Jan Kiszka <[email protected]>

Recent compilers look deep into cpu_exec, find longjmp as a noreturn function and decide to smash some stack variables as they won't be used again. This may lead to env becoming invalid after return from setjmp, causing crashes. Fix it by reloading env from cpu_single_env in that case.

Nice. Could you try if gcc flag -Wclobbered catches something using your compiler without your patch:

commit f826f0d0f5cf5dd18a0d34159c1a3bc8f2e6ddf4

Author: Blue Swirl <[email protected]>
Date:   Sun Sep 26 11:58:38 2010 +0000

Add gcc warning -Wclobbered

configure +1 -2
index 88159ac..2417205 100755
@@ -1038,7 +1038,7 @@ fi
 gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
 gcc_flags="-Wformat-security -Wformat-y2k -Winit-self
-Wignored-qualifiers $gcc_flags"
 gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
-gcc_flags="-fstack-protector-all -Wendif-labels $gcc_flags"
+gcc_flags="-fstack-protector-all -Wendif-labels -Wclobbered $gcc_flags"
 cat > $TMPC << EOF
 int main(void) { return 0; }

EOF

Quoted 21 lines

---
 cpu-exec.c |    4 ++++

 1 files changed, 4 insertions(+), 0 deletions(-)
cpu-exec.c +0 -0
index 20e3ec4..de0d716 100644
@@ -587,6 +587,10 @@ int cpu_exec(CPUState *env)
                 /* reset soft MMU for next block (it can currently

                   only be set by a memory fault) */
            } /* for(;;) */
+        } else {
+            /* Reload env after longjmp - the compiler may have smashed all
+             * local variables as longjmp is marked 'noreturn'. */
+            env = cpu_single_env;
        }
    } /* for(;;) */

#6 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 25 lines · Blue Swirl
Quoted 7 lines · Jan Kiszka

From: Jan Kiszka <[email protected]>

Recent compilers look deep into cpu_exec, find longjmp as a noreturn function and decide to smash some stack variables as they won't be used again. This may lead to env becoming invalid after return from setjmp, causing crashes. Fix it by reloading env from cpu_single_env in that case.

Nice. Could you try if gcc flag -Wclobbered catches something using your compiler without your patch:

commit f826f0d0f5cf5dd18a0d34159c1a3bc8f2e6ddf4

Author: Blue Swirl <[email protected]>
Date:   Sun Sep 26 11:58:38 2010 +0000

Add gcc warning -Wclobbered

configure +0 -0
index 88159ac..2417205 100755

configure fi

Quoted 9 lines
configure +1 -2
@@ -1038,7 +1038,7 @@ fi
 gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
 gcc_flags="-Wformat-security -Wformat-y2k -Winit-self
-Wignored-qualifiers $gcc_flags"
 gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
-gcc_flags="-fstack-protector-all -Wendif-labels $gcc_flags"
+gcc_flags="-fstack-protector-all -Wendif-labels -Wclobbered $gcc_flags"
 cat > $TMPC << EOF
 int main(void) { return 0; }
 EOF

Neither native gcc 4.5.0 nor mingw's 4.6.0 detect this issue. However, 4.6 found this - and 3 false positives:

monitor.c +5 -1
index 67ceb46..d7edbbb 100644
@@ -3253,6 +3253,7 @@ static const mon_cmd_t qmp_query_cmds[] = {
 /*******************************************************************/
 
 static const char *pch;
+static char *saved_key;
 static jmp_buf expr_env;
 
 #define MD_TLONG 0
@@ -4254,8 +4255,11 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
                     }
                     typestr++;
                 }
-                if (get_expr(mon, &val, &p))
+                saved_key = key;
+                if (get_expr(mon, &val, &p)) {
+                    key = saved_key;
                     goto fail;
+                }
                 /* Check if 'i' is greater than 32-bit */
                 if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
                     monitor_printf(mon, "\'%s\' has failed: ", cmdname);

But the right way to deal with it is to return error codes and get rid of this setjmp/longjmp mess for get_expr.

Jan

#7 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 6 lines · Jan Kiszka

static const char *pch;

monitor.c +1 -0
+static char *saved_key;
  static jmp_buf expr_env;
 
  #define MD_TLONG 0

monitor.c static const mon_cmd_t *monitor_parse_command(Monitor *mon, · line 4262

Quoted 9 lines
monitor.c +4 -1
@@ -4254,8 +4255,11 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
                      }
                      typestr++;
                  }
-                if (get_expr(mon,&val,&p))
+                saved_key = key;
+                if (get_expr(mon,&val,&p)) {
+                    key = saved_key;
                      goto fail;
+                }

Please make saved_key a volatile local instead.

Paolo

Applied
#8 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

In reply to Jan Kiszka

Thanks, applied.

Quoted 29 lines · Jan Kiszka

From: Jan Kiszka <[email protected]>

Recent compilers look deep into cpu_exec, find longjmp as a noreturn function and decide to smash some stack variables as they won't be used again. This may lead to env becoming invalid after return from setjmp, causing crashes. Fix it by reloading env from cpu_single_env in that case.

---
 cpu-exec.c |    4 ++++

 1 files changed, 4 insertions(+), 0 deletions(-)
cpu-exec.c +0 -0
index 20e3ec4..de0d716 100644
@@ -587,6 +587,10 @@ int cpu_exec(CPUState *env)
                 /* reset soft MMU for next block (it can currently

                   only be set by a memory fault) */
            } /* for(;;) */
+        } else {
+            /* Reload env after longjmp - the compiler may have smashed all
+             * local variables as longjmp is marked 'noreturn'. */
+            env = cpu_single_env;
        }
    } /* for(;;) */

#9 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

In reply to Jan Kiszka
Quoted 7 lines · Jan Kiszka

From: Jan Kiszka <[email protected]>

Recent compilers look deep into cpu_exec, find longjmp as a noreturn function and decide to smash some stack variables as they won't be used again. This may lead to env becoming invalid after return from setjmp, causing crashes. Fix it by reloading env from cpu_single_env in that case.

Can you give more details of what compiler/platform this was a problem for? My reading of the C standard is that the compiler isn't allowed to trash env across this longjmp, because it's a variable of automatic scope which isn't modified between the setjmp and the longjmp...

(We've been looking at this because reloading from cpu_single_env is the wrong fix in the case of user-mode where there are multiple-threads.)

Thanks
-- PMM

#10 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 10 lines · Peter Maydell
Quoted 5 lines
Quoted 5 lines

Recent compilers look deep into cpu_exec, find longjmp as a noreturn function and decide to smash some stack variables as they won't be used again. This may lead to env becoming invalid after return from setjmp, causing crashes. Fix it by reloading env from cpu_single_env in that case.

Can you give more details of what compiler/platform this was a problem for? My reading of the C standard is that the compiler isn't allowed to trash env across this longjmp, because it's a variable of automatic scope which isn't modified between the setjmp and the longjmp...

longjmp can destroy any non-volatile variable (-Wclobbered warns about this).

Paolo

#11 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 9 lines · Paolo Bonzini
Quoted 5 lines · Peter Maydell

Can you give more details of what compiler/platform this was a problem for? My reading of the C standard is that the compiler isn't allowed to trash env across this longjmp, because it's a variable of automatic scope which isn't modified between the setjmp and the longjmp...

longjmp can destroy any non-volatile variable (-Wclobbered warns about this).

"All accessible objects have values [...] as of the time the longjmp function was called, except that the values of objects of automatic storage duration that are local to the function containing the invocation of the corresponding setjmp macro that do not have volatile-qualified type and have been changed between the setjmp invocation and longjmp call are indeterminate."
-- C99 section 7.13.2.1 para 3.

So variables may only be destroyed if they are all of:
* local to the function calling setjmp
* not volatile
* changed between setjmp and longjmp

We don't change env between the setjmp and longjmp so the compiler should not trash it. (Indeed according to Jan in
http://lists.gnu.org/archive/html/qemu-devel/2011-07/msg00144.html
-Wclobbered doesn't complain about this code.)

-- PMM

#12 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 8 lines · Peter Maydell

"All accessible objects have values [...] as of the time the longjmp function was called, except that the values of objects of automatic storage duration that are local to the function containing the invocation of the corresponding setjmp macro that do not have volatile-qualified type and have been changed between the setjmp invocation and longjmp call are indeterminate."
-- C99 section 7.13.2.1 para 3.

Quoted 4 lines

So variables may only be destroyed if they are all of:
* local to the function calling setjmp
* not volatile
* changed between setjmp and longjmp

I didn't remember this third part. Thanks for bringing up facts. :)

Quoted 4 lines

We don't change env between the setjmp and longjmp so the compiler should not trash it. (Indeed according to Jan in
http://lists.gnu.org/archive/html/qemu-devel/2011-07/msg00144.html
-Wclobbered doesn't complain about this code.)

Then it's a compiler bug, not smartness. Making env volatile (or making a volatile copy if there is a performance impact) should still be enough to work around it.

Paolo

#13 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 9 lines · Paolo Bonzini
Quoted 4 lines · Peter Maydell

We don't change env between the setjmp and longjmp so the compiler should not trash it. (Indeed according to Jan in
http://lists.gnu.org/archive/html/qemu-devel/2011-07/msg00144.html
-Wclobbered doesn't complain about this code.)

Then it's a compiler bug, not smartness.  Making env volatile (or making a volatile copy if there is a performance impact) should still be enough to work around it.

Yes. (It would have to be a volatile copy, I think, env is a function parameter and I don't think you can make those volatile.)
https://bugs.launchpad.net/qemu/+bug/823902 includes some discussion of the effects on the test of adding the volatile copy.

-- PMM

#14 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 8 lines · Peter Maydell
Quoted 4 lines
Quoted 4 lines

Then it's a compiler bug, not smartness. Making env volatile (or making a volatile copy if there is a performance impact) should still be enough to work around it.

Yes. (It would have to be a volatile copy, I think, env is a function parameter and I don't think you can make those volatile.)
https://bugs.launchpad.net/qemu/+bug/823902 includes some discussion of the effects on the test of adding the volatile copy.

I'm not sure about what to read from there:

Quoted 10 lines

If I make cpu_single_env thread local with __thread and leave 0d101... in, then again it works reliably on 32bit Lucid, and is flaky on 64 bit Oneiric (5/10 2 hangs, 3 segs)

I've also tried using a volatile local variable in cpu_exec to hold a copy of env and restore that rather than cpu_single_env. With this it's solid on 32bit lucid and flaky on 64bit Oneirc; these failures on 64bit OO look like it running off the end of the code buffer (all 0 code), jumping to non-existent code addresses and a seg in tb_reset_jump_recursive2.

It looks like neither a thread-local cpu_single_env nor a volatile copy fix the bug?!?

I cannot think off-hand of a reason why thread-local cpu_single_env should not work for iothread under Unix, BTW. Since cpu_single_env is only set/used by a thread at a time (under the global lock), its users cannot distinguish between a thread-local variable and a global.

The only problem would be Windows, which runs cpu_signal in a thread different than the CPU thread. But that can be fixed easily in qemu_cpu_kick_thread.

Paolo

#15 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 13 lines · Paolo Bonzini

I'm not sure about what to read from there:

Quoted 10 lines

If I make cpu_single_env thread local with __thread and leave 0d101... in, then again it works reliably on 32bit Lucid, and is flaky on 64 bit Oneiric (5/10 2 hangs, 3 segs)

I've also tried using a volatile local variable in cpu_exec to hold a copy of env and restore that rather than cpu_single_env. With this it's solid on 32bit lucid and flaky on 64bit Oneirc; these failures on 64bit OO look like it running off the end of the code buffer (all 0 code), jumping to non-existent code addresses and a seg in tb_reset_jump_recursive2.

Quoted 2 lines

It looks like neither a thread-local cpu_single_env nor a volatile copy fix the bug?!?

As I say at the bottom of that bug I'm assuming I'm hitting multiple bugs. Although it's not clear to me why I don't hit them on 32bit lucid.

Dave

#16 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

In reply to Paolo Bonzini
Quoted 24 lines · Paolo Bonzini
Quoted 9 lines · Peter Maydell
Quoted 4 lines
Quoted 4 lines

Then it's a compiler bug, not smartness.  Making env volatile (or making a volatile copy if there is a performance impact) should still be enough to work around it.

Yes. (It would have to be a volatile copy, I think, env is a function parameter and I don't think you can make those volatile.)
https://bugs.launchpad.net/qemu/+bug/823902  includes some discussion of the effects on the test of adding the volatile copy.

I'm not sure about what to read from there:

Quoted 10 lines

If I make cpu_single_env thread local with __thread and leave 0d101... in, then again it works reliably on 32bit Lucid, and is flaky on 64 bit Oneiric (5/10 2 hangs, 3 segs)

I've also tried using a volatile local variable in cpu_exec to hold a copy of env and restore that rather than cpu_single_env. With this it's solid on 32bit lucid and flaky on 64bit Oneirc; these failures on 64bit OO look like it running off the end of the code buffer (all 0 code), jumping to non-existent code addresses and a seg in tb_reset_jump_recursive2.

Quoted 2 lines

It looks like neither a thread-local cpu_single_env nor a volatile copy fix the bug?!?

My guess is that we're running into the various other problems qemu has with significantly multithreaded programs in user-mode, which makes it a bit hard to disentangle this specific regression. (In particular segfaults in tb_reset_jump_recursive2 are almost certainly bug 668799.)

Quoted 4 lines

I cannot think off-hand of a reason why thread-local cpu_single_env should not work for iothread under Unix, BTW.  Since cpu_single_env is only set/used by a thread at a time (under the global lock), its users cannot distinguish between a thread-local variable and a global.

Thanks for the clarification. As you say, as long as we don't ever try to access it from another thread we're fine...

Quoted 3 lines

The only problem would be Windows, which runs cpu_signal in a thread different than the CPU thread.  But that can be fixed easily in qemu_cpu_kick_thread.

...and we just need to fix this.

-- PMM

#17 lore raw

Re: [Qemu-devel] [PATCH] tcg: Reload local variables after return from longjmp

Quoted 7 lines · Peter Maydell
Quoted 4 lines

I cannot think off-hand of a reason why thread-local cpu_single_env should not work for iothread under Unix, BTW. Since cpu_single_env is only set/used by a thread at a time (under the global lock), its users cannot distinguish between a thread-local variable and a global.

Thanks for the clarification. As you say, as long as we don't ever try to access it from another thread we're fine...

Yes, and the current usage of the lock should be enough of a guarantee.

Quoted 5 lines
Quoted 3 lines

The only problem would be Windows, which runs cpu_signal in a thread different than the CPU thread. But that can be fixed easily in qemu_cpu_kick_thread.

...and we just need to fix this.

Untested (uncompiled) patch follows:

cpus.c +11 -4
index 6bf4e3f..04e52fe 100644
@@ -179,10 +179,10 @@ static void cpu_handle_guest_debug(CPUState *env)
 }
 
 #ifdef CONFIG_IOTHREAD
-static void cpu_signal(int sig)
+static inline void do_cpu_kick(CPUState *env)
 {
-    if (cpu_single_env) {
-        cpu_exit(cpu_single_env);
+    if (env) {
+        cpu_exit(env);
     }
     exit_request = 1;
 }
@@ -476,6 +476,13 @@ static void qemu_kvm_init_cpu_signals(CPUState *env)
     }
 }
 
+#ifdef CONFIG_IOTHREAD
+static void cpu_signal(int sig)
+{
+    do_cpu_kick(cpu_single_env);
+}
+#endif
+
 static void qemu_tcg_init_cpu_signals(void)
 {
 #ifdef CONFIG_IOTHREAD
@@ -858,7 +865,7 @@ static void qemu_cpu_kick_thread(CPUState *env)
 #else /* _WIN32 */
     if (!qemu_cpu_is_self(env)) {
         SuspendThread(env->thread->thread);
-        cpu_signal(0);
+        do_cpu_kick(env);
         ResumeThread(env->thread->thread);
     }
 #endif