[PATCH] target/i386: allow transition to virtual-8086 mode only if CPL == 0 and CPU is not in long mode

Andrey Polivoda posted 1 patch 1 month ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260622082119.11903-1-apolivodaa433@gmail.com
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Richard Henderson <richard.henderson@linaro.org>
target/i386/tcg/seg_helper.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
[PATCH] target/i386: allow transition to virtual-8086 mode only if CPL == 0 and CPU is not in long mode
Posted by Andrey Polivoda 1 month ago
According to the pseudocode for the IRET instruction in both the Intel 64 
and IA-32 Architectures Software Developer's Manual and the AMD64 Architecture 
Programmer's Manual, a transition to virtual-8086 mode is allowed only if all 
of the following conditions are met:

1. The new EFLAGS.VM bit is set to 1.
2. The Current Privilege Level (CPL) is 0.
3. The CPU is in protected mode (and not in long mode).

Currently, QEMU performs only the first check. This omission allows a 
transition to virtual-8086 mode from long mode, and also enables the guest's 
userspace to trigger this switch.

During a legitimate transition, the EFLAGS register is updated in a way that 
allows modification of sensitive fields, such as IOPL and IF (which is expected, 
as only privileged code should be able to initiate this transition). 

However, due to the lack of appropriate checks, an unprivileged guest userspace 
process can now force this transition and freely modify these fields.
This allows the userspace to:

1. Disable interrupts, preventing other processes from running on the CPU.
2. Gain direct hardware I/O access by elevating EFLAGS.IOPL to 3.
3. Crash the guest kernel by setting CS and SS to resemble segments with RPL = 0 
   and triggering an exception. Since the kernel is unaware that the process 
   entered virtual-8086 mode, it will misinterpret the exception as originating 
   from kernel space.

This patch fixes this bug by adding the missing CPL and long mode checks before
jumping to the `return_to_vm86` label.

Fixes: 90a9fdae1f1a ("more ring 0 operations")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3583
Signed-off-by: Andrey Polivoda <apolivodaa433@gmail.com>
Cc: qemu-devel@nongnu.org
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Richard Henderson <richard.henderson@linaro.org>
---
 target/i386/tcg/seg_helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c
index 58aac72011..8bab2db00a 100644
--- a/target/i386/tcg/seg_helper.c
+++ b/target/i386/tcg/seg_helper.c
@@ -2068,7 +2068,8 @@ static inline void helper_ret_protected(CPUX86State *env, int shift,
             new_cs = popl(&sa) & 0xffff;
             if (is_iret) {
                 new_eflags = popl(&sa);
-                if (new_eflags & VM_MASK) {
+                bool allow_vm86 = (cpl == 0) && !(env->hflags & HF_LMA_MASK);
+                if ((new_eflags & VM_MASK) && allow_vm86) {
                     goto return_to_vm86;
                 }
             }
-- 
2.53.0
Re: [PATCH] target/i386: allow transition to virtual-8086 mode only if CPL == 0 and CPU is not in long mode
Posted by Andrey Polivoda 3 weeks, 6 days ago
ping
https://lore.kernel.org/qemu-devel/20260622082119.11903-1-apolivodaa433@gmail.com/