QEMU-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [Bug 1863025] [NEW] Use-after-free after flush in TCG accelerator
@ 2020-02-12 22:01 Yifan
  2020-02-14 14:23 ` [Bug 1863025] " Alex Bennée
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Yifan @ 2020-02-12 22:01 UTC (permalink / raw)
  To: qemu-devel

Public bug reported:

I believe I found a UAF in TCG that can lead to a guest VM escape. The
security list informed me "This can not be treated as a security issue."
and to post it here. I am looking at the 4.2.0 source code. The issue
requires a race and I will try to describe it in terms of three
concurrent threads.

Thread A:

A1. qemu_tcg_cpu_thread_fn runs work loop
A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
A3. start_exclusive critical section entered
A4. do_tb_flush is called, TB memory freed/re-allocated
A5. end_exclusive exits critical section

Thread B:

B1. qemu_tcg_cpu_thread_fn runs work loop
B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
B3. tcg_tb_alloc obtains a new TB

Thread C:

C1. qemu_tcg_cpu_thread_fn runs work loop
C2. cpu_exec_step_atomic executes
C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
C4. start_exclusive critical section entered
C5. cpu_tb_exec executes the TB code
C6. end_exclusive exits critical section

Consider the following sequence of events:
  B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
  B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

In short, because thread C uses the TB in the critical section, there is
no guarantee that the pointer has not been "freed" (rather the memory is
marked as re-usable) and therefore a use-after-free occurs.

Since the TCG generated code can be in the same memory as the TB data
structure, it is possible for an attacker to overwrite the UAF pointer
with code generated from TCG. This can overwrite key pointer values and
could lead to code execution on the host outside of the TCG sandbox.

** Affects: qemu
     Importance: Undecided
         Status: New

** Description changed:

- I believe I found a UAF in TCG that can lead to a guest VM escape. The security 
- list informed me "This can not be treated as a security issue." and to post it 
- here. I am looking at the 4.2.0 source code. The issue requires a race and I 
- will try to describe it in terms of three concurrent threads.
- 
- I am looking 
- at the 4.2.0 source code. The issue requires a race and I will try to describe 
- it in terms of three concurrent threads.
+ I believe I found a UAF in TCG that can lead to a guest VM escape. The
+ security list informed me "This can not be treated as a security issue."
+ and to post it here. I am looking at the 4.2.0 source code. The issue
+ requires a race and I will try to describe it in terms of three
+ concurrent threads.
  
  Thread A:
  
  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section
  
  Thread B:
  
  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB
  
  Thread C:
  
  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section
  
  Consider the following sequence of events:
-   B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 => 
-   B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6
+   B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
+   B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6
  
- In short, because thread C uses the TB in the critical section, there is no 
- guarantee that the pointer has not been "freed" (rather the memory is marked as 
- re-usable) and therefore a use-after-free occurs.
+ In short, because thread C uses the TB in the critical section, there is
+ no guarantee that the pointer has not been "freed" (rather the memory is
+ marked as re-usable) and therefore a use-after-free occurs.
  
- Since the TCG generated code can be in the same memory as the TB data structure,
- it is possible for an attacker to overwrite the UAF pointer with code generated
- from TCG. This can overwrite key pointer values and could lead to code 
- execution on the host outside of the TCG sandbox.
+ Since the TCG generated code can be in the same memory as the TB data
+ structure, it is possible for an attacker to overwrite the UAF pointer
+ with code generated from TCG. This can overwrite key pointer values and
+ could lead to code execution on the host outside of the TCG sandbox.

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  New

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Bug 1863025] Re: Use-after-free after flush in TCG accelerator
  2020-02-12 22:01 [Bug 1863025] [NEW] Use-after-free after flush in TCG accelerator Yifan
@ 2020-02-14 14:23 ` " Alex Bennée
  2020-02-14 14:29 ` Alex Bennée
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Alex Bennée @ 2020-02-14 14:23 UTC (permalink / raw)
  To: qemu-devel

** Tags added: tcg

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  Confirmed

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Bug 1863025] Re: Use-after-free after flush in TCG accelerator
  2020-02-12 22:01 [Bug 1863025] [NEW] Use-after-free after flush in TCG accelerator Yifan
  2020-02-14 14:23 ` [Bug 1863025] " Alex Bennée
@ 2020-02-14 14:29 ` Alex Bennée
  2020-02-14 14:49 ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Alex Bennée
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Alex Bennée @ 2020-02-14 14:29 UTC (permalink / raw)
  To: qemu-devel

** Changed in: qemu
     Assignee: (unassigned) => Alex Bennée (ajbennee)

** Changed in: qemu
       Status: New => Confirmed

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  Confirmed

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025)
@ 2020-02-14 14:49 ` Alex Bennée
  2020-02-14 14:49   ` [Bug 1863025] Re: Use-after-free after flush in TCG accelerator Alex Bennée
                     ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Alex Bennée @ 2020-02-14 14:49 UTC (permalink / raw)
  To: qemu-devel
  Cc: Bug 1863025, Paolo Bonzini, Alex Bennée, Yifan, Richard Henderson

The bug describes a race whereby cpu_exec_step_atomic can acquire a TB
which is invalidated by a tb_flush before we execute it. This doesn't
affect the other cpu_exec modes as a tb_flush by it's nature can only
occur on a quiescent system. The race was described as:

  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

      C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
          (same TB as B2)

          A3. start_exclusive critical section entered
          A4. do_tb_flush is called, TB memory freed/re-allocated
          A5. end_exclusive exits critical section

  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc reallocates TB from B2

      C4. start_exclusive critical section entered
      C5. cpu_tb_exec executes the TB code that was free in A4

The simplest fix is to widen the exclusive period to include the TB
lookup. As a result we can drop the complication of checking we are in
the exclusive region before we end it.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Yifan <me@yifanlu.com>
Cc: Bug 1863025 <1863025@bugs.launchpad.net>
---
 accel/tcg/cpu-exec.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 2560c90eec7..d95c4848a47 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -240,6 +240,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
     uint32_t cf_mask = cflags & CF_HASH_MASK;
 
     if (sigsetjmp(cpu->jmp_env, 0) == 0) {
+        start_exclusive();
+
         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
         if (tb == NULL) {
             mmap_lock();
@@ -247,8 +249,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
             mmap_unlock();
         }
 
-        start_exclusive();
-
         /* Since we got here, we know that parallel_cpus must be true.  */
         parallel_cpus = false;
         cc->cpu_exec_enter(cpu);
@@ -271,14 +271,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
         qemu_plugin_disable_mem_helpers(cpu);
     }
 
-    if (cpu_in_exclusive_context(cpu)) {
-        /* We might longjump out of either the codegen or the
-         * execution, so must make sure we only end the exclusive
-         * region if we started it.
-         */
-        parallel_cpus = true;
-        end_exclusive();
-    }
+
+    /*
+     * As we start the exclusive region before codegen we must still
+     * be in the region if we longjump out of either the codegen or
+     * the execution.
+     */
+    g_assert(cpu_in_exclusive_context(cpu));
+    parallel_cpus = true;
+    end_exclusive();
 }
 
 struct tb_desc {
-- 
2.20.1



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Bug 1863025] Re: Use-after-free after flush in TCG accelerator
  2020-02-14 14:49 ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Alex Bennée
@ 2020-02-14 14:49   ` Alex Bennée
  2020-02-14 15:22   ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Paolo Bonzini
  2020-02-14 23:31   ` Richard Henderson
  2 siblings, 0 replies; 12+ messages in thread
From: Alex Bennée @ 2020-02-14 14:49 UTC (permalink / raw)
  To: qemu-devel

The bug describes a race whereby cpu_exec_step_atomic can acquire a TB
which is invalidated by a tb_flush before we execute it. This doesn't
affect the other cpu_exec modes as a tb_flush by it's nature can only
occur on a quiescent system. The race was described as:

  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

      C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
          (same TB as B2)

          A3. start_exclusive critical section entered
          A4. do_tb_flush is called, TB memory freed/re-allocated
          A5. end_exclusive exits critical section

  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc reallocates TB from B2

      C4. start_exclusive critical section entered
      C5. cpu_tb_exec executes the TB code that was free in A4

The simplest fix is to widen the exclusive period to include the TB
lookup. As a result we can drop the complication of checking we are in
the exclusive region before we end it.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Yifan <me@yifanlu.com>
Cc: Bug 1863025 <1863025@bugs.launchpad.net>
---
 accel/tcg/cpu-exec.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 2560c90eec7..d95c4848a47 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -240,6 +240,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
     uint32_t cf_mask = cflags & CF_HASH_MASK;
 
     if (sigsetjmp(cpu->jmp_env, 0) == 0) {
+        start_exclusive();
+
         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
         if (tb == NULL) {
             mmap_lock();
@@ -247,8 +249,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
             mmap_unlock();
         }
 
-        start_exclusive();
-
         /* Since we got here, we know that parallel_cpus must be true.  */
         parallel_cpus = false;
         cc->cpu_exec_enter(cpu);
@@ -271,14 +271,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
         qemu_plugin_disable_mem_helpers(cpu);
     }
 
-    if (cpu_in_exclusive_context(cpu)) {
-        /* We might longjump out of either the codegen or the
-         * execution, so must make sure we only end the exclusive
-         * region if we started it.
-         */
-        parallel_cpus = true;
-        end_exclusive();
-    }
+
+    /*
+     * As we start the exclusive region before codegen we must still
+     * be in the region if we longjump out of either the codegen or
+     * the execution.
+     */
+    g_assert(cpu_in_exclusive_context(cpu));
+    parallel_cpus = true;
+    end_exclusive();
 }
 
 struct tb_desc {
-- 
2.20.1


** Tags added: mttcg

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  Confirmed

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Bug 1863025] Re: Use-after-free after flush in TCG accelerator
  2020-02-12 22:01 [Bug 1863025] [NEW] Use-after-free after flush in TCG accelerator Yifan
                   ` (2 preceding siblings ...)
  2020-02-14 14:49 ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Alex Bennée
@ 2020-02-14 14:51 ` Alex Bennée
  2020-02-14 18:09 ` Yifan
  2020-02-14 18:18 ` Yifan
  5 siblings, 0 replies; 12+ messages in thread
From: Alex Bennée @ 2020-02-14 14:51 UTC (permalink / raw)
  To: qemu-devel

I've attached a variant of the suggested patch which simply expands the
exclusive period. It's hard to test extensively as not many things use
the EXCP_ATOMIC mechanism. Can I ask how you found the bug and if you
can re-test with the suggested patch?

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  Confirmed

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025)
  2020-02-14 14:49 ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Alex Bennée
  2020-02-14 14:49   ` [Bug 1863025] Re: Use-after-free after flush in TCG accelerator Alex Bennée
@ 2020-02-14 15:22   ` Paolo Bonzini
  2020-02-14 23:31   ` Richard Henderson
  2 siblings, 0 replies; 12+ messages in thread
From: Paolo Bonzini @ 2020-02-14 15:22 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Bug 1863025, Yifan, Richard Henderson

On 14/02/20 15:49, Alex Bennée wrote:
> The bug describes a race whereby cpu_exec_step_atomic can acquire a TB
> which is invalidated by a tb_flush before we execute it. This doesn't
> affect the other cpu_exec modes as a tb_flush by it's nature can only
> occur on a quiescent system. The race was described as:
> 
>   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
>   B3. tcg_tb_alloc obtains a new TB
> 
>       C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
>           (same TB as B2)
> 
>           A3. start_exclusive critical section entered
>           A4. do_tb_flush is called, TB memory freed/re-allocated
>           A5. end_exclusive exits critical section
> 
>   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
>   B3. tcg_tb_alloc reallocates TB from B2
> 
>       C4. start_exclusive critical section entered
>       C5. cpu_tb_exec executes the TB code that was free in A4
> 
> The simplest fix is to widen the exclusive period to include the TB
> lookup. As a result we can drop the complication of checking we are in
> the exclusive region before we end it.

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Cc: Yifan <me@yifanlu.com>
> Cc: Bug 1863025 <1863025@bugs.launchpad.net>
> ---
>  accel/tcg/cpu-exec.c | 21 +++++++++++----------
>  1 file changed, 11 insertions(+), 10 deletions(-)
> 
> diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
> index 2560c90eec7..d95c4848a47 100644
> --- a/accel/tcg/cpu-exec.c
> +++ b/accel/tcg/cpu-exec.c
> @@ -240,6 +240,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
>      uint32_t cf_mask = cflags & CF_HASH_MASK;
>  
>      if (sigsetjmp(cpu->jmp_env, 0) == 0) {
> +        start_exclusive();
> +
>          tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
>          if (tb == NULL) {
>              mmap_lock();
> @@ -247,8 +249,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
>              mmap_unlock();
>          }
>  
> -        start_exclusive();
> -
>          /* Since we got here, we know that parallel_cpus must be true.  */
>          parallel_cpus = false;
>          cc->cpu_exec_enter(cpu);
> @@ -271,14 +271,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
>          qemu_plugin_disable_mem_helpers(cpu);
>      }
>  
> -    if (cpu_in_exclusive_context(cpu)) {
> -        /* We might longjump out of either the codegen or the
> -         * execution, so must make sure we only end the exclusive
> -         * region if we started it.
> -         */
> -        parallel_cpus = true;
> -        end_exclusive();
> -    }
> +
> +    /*
> +     * As we start the exclusive region before codegen we must still
> +     * be in the region if we longjump out of either the codegen or
> +     * the execution.
> +     */
> +    g_assert(cpu_in_exclusive_context(cpu));
> +    parallel_cpus = true;
> +    end_exclusive();
>  }
>  
>  struct tb_desc {
> 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Bug 1863025] Re: Use-after-free after flush in TCG accelerator
  2020-02-12 22:01 [Bug 1863025] [NEW] Use-after-free after flush in TCG accelerator Yifan
                   ` (3 preceding siblings ...)
  2020-02-14 14:51 ` [Bug 1863025] Re: Use-after-free after flush in TCG accelerator Alex Bennée
@ 2020-02-14 18:09 ` Yifan
  2020-02-14 18:18 ` Yifan
  5 siblings, 0 replies; 12+ messages in thread
From: Yifan @ 2020-02-14 18:09 UTC (permalink / raw)
  To: qemu-devel

I found it just by launching Ubuntu 19.10 live cd with QXL driver. I
will re-test this weekend.

The workaround I had is to check the number of TLB flushes and to re-try
obtaining the TB if the number changes. There is a penalty for the case
where TLB is flushed but should not degrade performance in most cases. I
think obtaining the lock earlier will slow down the VM if EXCP_ATOMIC is
used often.

Of course, I am assuming TLB flush is the only way to cause this bug.

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index d1c2b6ea1fd..d83b578299b 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -250,8 +250,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
     uint32_t flags;
     uint32_t cflags = 1;
     uint32_t cf_mask = cflags & CF_HASH_MASK;
+    unsigned flush_count;
 
     if (sigsetjmp(cpu->jmp_env, 0) == 0) {
+retry:
+        flush_count = tb_flush_count();
         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
         if (tb == NULL) {
             mmap_lock();
@@ -260,6 +263,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
         }
 
         start_exclusive();
+        /* do_tb_flush() might run and make tb invalid */
+        if (flush_count != tb_flush_count()) {
+            end_exclusive();
+            goto retry;
+        }
 
         /* Since we got here, we know that parallel_cpus must be true.  */
         parallel_cpus = false;
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 4ed9d0abaf2..ecf7d3b53ff 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -2696,6 +2696,11 @@ void tcg_flush_softmmu_tlb(CPUState *cs)
 #endif
 }
 
+unsigned tb_flush_count(void)
+{
+    return atomic_read(&tb_ctx.tb_flush_count);
+}
+
 #if defined(CONFIG_NO_RWX)
 void tb_exec_memory_lock(void)
 {
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 5ccc9485812..1bc61fa6d76 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -584,6 +584,7 @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr);
 void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr);
 
 /* translate-all.c */
+unsigned tb_flush_count(void);
 #if defined(CONFIG_NO_RWX)
 void tb_exec_memory_lock(void);
 bool tb_is_exec(const TranslationBlock *tb);

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  Confirmed

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Bug 1863025] Re: Use-after-free after flush in TCG accelerator
  2020-02-12 22:01 [Bug 1863025] [NEW] Use-after-free after flush in TCG accelerator Yifan
                   ` (4 preceding siblings ...)
  2020-02-14 18:09 ` Yifan
@ 2020-02-14 18:18 ` Yifan
  5 siblings, 0 replies; 12+ messages in thread
From: Yifan @ 2020-02-14 18:18 UTC (permalink / raw)
  To: qemu-devel

Apologies, the patch got messed up.

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index c01f59c743..7a9e8c94bd 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -238,8 +238,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
     uint32_t flags;
     uint32_t cflags = 1;
     uint32_t cf_mask = cflags & CF_HASH_MASK;
+    unsigned flush_count;
 
     if (sigsetjmp(cpu->jmp_env, 0) == 0) {
+retry:
+        flush_count = tb_flush_count();
         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
         if (tb == NULL) {
             mmap_lock();
@@ -248,6 +251,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
         }
 
         start_exclusive();
+        /* do_tb_flush() might run and make tb invalid */
+        if (flush_count != tb_flush_count()) {
+            end_exclusive();
+            goto retry;
+        }
 
         /* Since we got here, we know that parallel_cpus must be true.  */
         parallel_cpus = false;
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 9f48da9472..2fb7da9b51 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -2674,3 +2674,8 @@ void tcg_flush_softmmu_tlb(CPUState *cs)
     tlb_flush(cs);
 #endif
 }
+
+unsigned tb_flush_count(void)
+{
+    return atomic_read(&tb_ctx.tb_flush_count);
+}
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index d85e610e85..aa3c2d219a 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -579,6 +579,9 @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr);
 /* exec.c */
 void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr);
 
+/* translate-all.c */
+unsigned tb_flush_count(void);
+
 MemoryRegionSection *
 address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
                                   hwaddr *xlat, hwaddr *plen,

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  Confirmed

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025)
  2020-02-14 14:49 ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Alex Bennée
  2020-02-14 14:49   ` [Bug 1863025] Re: Use-after-free after flush in TCG accelerator Alex Bennée
  2020-02-14 15:22   ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Paolo Bonzini
@ 2020-02-14 23:31   ` Richard Henderson
  2020-02-15  0:01     ` Yifan Lu
  2 siblings, 1 reply; 12+ messages in thread
From: Richard Henderson @ 2020-02-14 23:31 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Bug 1863025, Richard Henderson, Yifan, Paolo Bonzini

On 2/14/20 6:49 AM, Alex Bennée wrote:
> The bug describes a race whereby cpu_exec_step_atomic can acquire a TB
> which is invalidated by a tb_flush before we execute it. This doesn't
> affect the other cpu_exec modes as a tb_flush by it's nature can only
> occur on a quiescent system. The race was described as:
> 
>   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
>   B3. tcg_tb_alloc obtains a new TB
> 
>       C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
>           (same TB as B2)
> 
>           A3. start_exclusive critical section entered
>           A4. do_tb_flush is called, TB memory freed/re-allocated
>           A5. end_exclusive exits critical section
> 
>   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
>   B3. tcg_tb_alloc reallocates TB from B2
> 
>       C4. start_exclusive critical section entered
>       C5. cpu_tb_exec executes the TB code that was free in A4
> 
> The simplest fix is to widen the exclusive period to include the TB
> lookup. As a result we can drop the complication of checking we are in
> the exclusive region before we end it.

I'm not 100% keen on having the tb_gen_code within the exclusive region.  It
implies a much larger delay on (at least) the first execution of the atomic
operation.

But I suppose until recently we had a global lock around code generation, and
this is only slightly worse.  Plus, it has the advantage of being dead simple,
and without the races vs tb_ctx.tb_flush_count that exist in Yifan's patch.

Applied to tcg-next.


r~


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025)
  2020-02-14 23:31   ` Richard Henderson
@ 2020-02-15  0:01     ` Yifan Lu
  2020-02-15  0:01       ` [Bug 1863025] " Yifan
  0 siblings, 1 reply; 12+ messages in thread
From: Yifan Lu @ 2020-02-15  0:01 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Bug 1863025, Richard Henderson, Alex Bennée, qemu-devel,
	Paolo Bonzini

What race are you thinking of in my patch? The obvious race I can
think of is benign:

Case 1:
A: does TB flush
B: read tb_flush_count
A: increment tb_flush_count
A: end_exclusive
B: tb_lookup__cpu_state/tb_gen_code
B: start_exclusive
B: read tb_flush_count again (increment seen)
B: retries

Case 2:
B: read tb_flush_count
A: does TB flush
A: increment tb_flush_count
A: end_exclusive
B: tb_lookup__cpu_state/tb_gen_code
B: start_exclusive
B: read tb_flush_count again (increment seen)
B: retries

Case 3:
A: does TB flush
A: increment tb_flush_count
A: end_exclusive
B: read tb_flush_count
B: tb_lookup__cpu_state/tb_gen_code
B: start_exclusive
B: read tb_flush_count again (no increment seen)
B: proceeds

Case 1 is the expected case. Case 2, we thought TB was stale but it
wasn't so we get it again with tb_lookup__cpu_state with minimal extra
overhead.

Case 3 seems to be bad because we could read tb_flush_count and find
it already incremented. But if so that means thread A is at the end of
do_tb_flush and the lookup tables are already cleared and the TCG
context is already reset. So it should be safe for thread B to call
tb_lookup__cpu_state or tb_gen_code.

Yifan

On Fri, Feb 14, 2020 at 3:31 PM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 2/14/20 6:49 AM, Alex Bennée wrote:
> > The bug describes a race whereby cpu_exec_step_atomic can acquire a TB
> > which is invalidated by a tb_flush before we execute it. This doesn't
> > affect the other cpu_exec modes as a tb_flush by it's nature can only
> > occur on a quiescent system. The race was described as:
> >
> >   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
> >   B3. tcg_tb_alloc obtains a new TB
> >
> >       C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
> >           (same TB as B2)
> >
> >           A3. start_exclusive critical section entered
> >           A4. do_tb_flush is called, TB memory freed/re-allocated
> >           A5. end_exclusive exits critical section
> >
> >   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
> >   B3. tcg_tb_alloc reallocates TB from B2
> >
> >       C4. start_exclusive critical section entered
> >       C5. cpu_tb_exec executes the TB code that was free in A4
> >
> > The simplest fix is to widen the exclusive period to include the TB
> > lookup. As a result we can drop the complication of checking we are in
> > the exclusive region before we end it.
>
> I'm not 100% keen on having the tb_gen_code within the exclusive region.  It
> implies a much larger delay on (at least) the first execution of the atomic
> operation.
>
> But I suppose until recently we had a global lock around code generation, and
> this is only slightly worse.  Plus, it has the advantage of being dead simple,
> and without the races vs tb_ctx.tb_flush_count that exist in Yifan's patch.
>
> Applied to tcg-next.
>
>
> r~


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Bug 1863025] Re: [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025)
  2020-02-15  0:01     ` Yifan Lu
@ 2020-02-15  0:01       ` " Yifan
  0 siblings, 0 replies; 12+ messages in thread
From: Yifan @ 2020-02-15  0:01 UTC (permalink / raw)
  To: qemu-devel

What race are you thinking of in my patch? The obvious race I can
think of is benign:

Case 1:
A: does TB flush
B: read tb_flush_count
A: increment tb_flush_count
A: end_exclusive
B: tb_lookup__cpu_state/tb_gen_code
B: start_exclusive
B: read tb_flush_count again (increment seen)
B: retries

Case 2:
B: read tb_flush_count
A: does TB flush
A: increment tb_flush_count
A: end_exclusive
B: tb_lookup__cpu_state/tb_gen_code
B: start_exclusive
B: read tb_flush_count again (increment seen)
B: retries

Case 3:
A: does TB flush
A: increment tb_flush_count
A: end_exclusive
B: read tb_flush_count
B: tb_lookup__cpu_state/tb_gen_code
B: start_exclusive
B: read tb_flush_count again (no increment seen)
B: proceeds

Case 1 is the expected case. Case 2, we thought TB was stale but it
wasn't so we get it again with tb_lookup__cpu_state with minimal extra
overhead.

Case 3 seems to be bad because we could read tb_flush_count and find
it already incremented. But if so that means thread A is at the end of
do_tb_flush and the lookup tables are already cleared and the TCG
context is already reset. So it should be safe for thread B to call
tb_lookup__cpu_state or tb_gen_code.

Yifan

On Fri, Feb 14, 2020 at 3:31 PM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 2/14/20 6:49 AM, Alex Bennée wrote:
> > The bug describes a race whereby cpu_exec_step_atomic can acquire a TB
> > which is invalidated by a tb_flush before we execute it. This doesn't
> > affect the other cpu_exec modes as a tb_flush by it's nature can only
> > occur on a quiescent system. The race was described as:
> >
> >   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
> >   B3. tcg_tb_alloc obtains a new TB
> >
> >       C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
> >           (same TB as B2)
> >
> >           A3. start_exclusive critical section entered
> >           A4. do_tb_flush is called, TB memory freed/re-allocated
> >           A5. end_exclusive exits critical section
> >
> >   B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
> >   B3. tcg_tb_alloc reallocates TB from B2
> >
> >       C4. start_exclusive critical section entered
> >       C5. cpu_tb_exec executes the TB code that was free in A4
> >
> > The simplest fix is to widen the exclusive period to include the TB
> > lookup. As a result we can drop the complication of checking we are in
> > the exclusive region before we end it.
>
> I'm not 100% keen on having the tb_gen_code within the exclusive region.  It
> implies a much larger delay on (at least) the first execution of the atomic
> operation.
>
> But I suppose until recently we had a global lock around code generation, and
> this is only slightly worse.  Plus, it has the advantage of being dead simple,
> and without the races vs tb_ctx.tb_flush_count that exist in Yifan's patch.
>
> Applied to tcg-next.
>
>
> r~

-- 
You received this bug notification because you are a member of qemu-
devel-ml, which is subscribed to QEMU.
https://bugs.launchpad.net/bugs/1863025

Title:
  Use-after-free after flush in TCG accelerator

Status in QEMU:
  Confirmed

Bug description:
  I believe I found a UAF in TCG that can lead to a guest VM escape. The
  security list informed me "This can not be treated as a security
  issue." and to post it here. I am looking at the 4.2.0 source code.
  The issue requires a race and I will try to describe it in terms of
  three concurrent threads.

  Thread A:

  A1. qemu_tcg_cpu_thread_fn runs work loop
  A2. qemu_wait_io_event => qemu_wait_io_event_common => process_queued_cpu_work
  A3. start_exclusive critical section entered
  A4. do_tb_flush is called, TB memory freed/re-allocated
  A5. end_exclusive exits critical section

  Thread B:

  B1. qemu_tcg_cpu_thread_fn runs work loop
  B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
  B3. tcg_tb_alloc obtains a new TB

  Thread C:

  C1. qemu_tcg_cpu_thread_fn runs work loop
  C2. cpu_exec_step_atomic executes
  C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
  C4. start_exclusive critical section entered
  C5. cpu_tb_exec executes the TB code
  C6. end_exclusive exits critical section

  Consider the following sequence of events:
    B2 => B3 => C3 (same TB as B2) => A3 => A4 (TB freed) => A5 => B2 =>
    B3 (re-allocates TB from B2) => C4 => C5 (freed/reused TB now executing) => C6

  In short, because thread C uses the TB in the critical section, there
  is no guarantee that the pointer has not been "freed" (rather the
  memory is marked as re-usable) and therefore a use-after-free occurs.

  Since the TCG generated code can be in the same memory as the TB data
  structure, it is possible for an attacker to overwrite the UAF pointer
  with code generated from TCG. This can overwrite key pointer values
  and could lead to code execution on the host outside of the TCG
  sandbox.

To manage notifications about this bug go to:
https://bugs.launchpad.net/qemu/+bug/1863025/+subscriptions


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, back to index

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-12 22:01 [Bug 1863025] [NEW] Use-after-free after flush in TCG accelerator Yifan
2020-02-14 14:23 ` [Bug 1863025] " Alex Bennée
2020-02-14 14:29 ` Alex Bennée
2020-02-14 14:49 ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Alex Bennée
2020-02-14 14:49   ` [Bug 1863025] Re: Use-after-free after flush in TCG accelerator Alex Bennée
2020-02-14 15:22   ` [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025) Paolo Bonzini
2020-02-14 23:31   ` Richard Henderson
2020-02-15  0:01     ` Yifan Lu
2020-02-15  0:01       ` [Bug 1863025] " Yifan
2020-02-14 14:51 ` [Bug 1863025] Re: Use-after-free after flush in TCG accelerator Alex Bennée
2020-02-14 18:09 ` Yifan
2020-02-14 18:18 ` Yifan

QEMU-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/qemu-devel/0 qemu-devel/git/0.git
	git clone --mirror https://lore.kernel.org/qemu-devel/1 qemu-devel/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 qemu-devel qemu-devel/ https://lore.kernel.org/qemu-devel \
		qemu-devel@nongnu.org
	public-inbox-index qemu-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.nongnu.qemu-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git