All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator
@ 2022-01-20  9:33 Hou Wenlong
  2022-01-20  9:33 ` [PATCH 1/2] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Hou Wenlong @ 2022-01-20  9:33 UTC (permalink / raw)
  To: kvm

Per Intel's SDM on "Instruction Set Reference", code segment
can be loaded by far jmp/call/ret, iret and int. For all those
instructions, not-present segment check should be after type and
privilege checks. But the emulator checks it first, so #NP is
triggered instead of #GP if privilege check fails and the segment
is not present.

When loading code segment above realmode, RPL/CPL/DPL should be
checked, but the privilege checks are different between those
instructions. Since iret and int are only implemented for realmode
in emulator, no checks ared needed.

The current implement only checks if DPL > CPL for conforming
code or (RPL > CPL or DPL != CPL) for non-conforming code. Since
far call/jump to call gate, task gate and task state segment are
not implemented for in emulator, the current checks are enough.

As for far return, outer level return is not implemented above
virtual-8086 mode in emulator, so RPL <= CPL. Per Intel's SDM,
if RPL < CPL, it should trigger #GP, but it is missing in
emulator. Other checks are satisfied in current implementation.

When vmexit for task switch, code segment would also be loaded
from tss. Since segment selector is loaded before segment descriptor
when load state from tss, it implies that RPL = CPL, the checks
are satisfied too.

I add some tests in kvm-unit-tests[*] for the wrong checks in
emulator. Enable kvm.force_enable_emulation to test them on emulator.

[*] https://lore.kernel.org/kvm/cover.1642669912.git.houwenlong.hwl@antgroup.com

Hou Wenlong (2):
  KVM: x86/emulator: Defer not-present segment check in
    __load_segment_descriptor()
  KVM: x86: Fix wrong privilege check for code segment in
    __load_segment_descriptor()

 arch/x86/kvm/emulate.c | 44 +++++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 13 deletions(-)

--
2.31.1


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

* [PATCH 1/2] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor()
  2022-01-20  9:33 [PATCH 0/2] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
@ 2022-01-20  9:33 ` Hou Wenlong
  2022-02-07 19:21   ` Sean Christopherson
  2022-01-20  9:33 ` [PATCH 2/2] KVM: x86: Fix wrong privilege check for code segment " Hou Wenlong
  2022-02-08  9:34 ` [PATCH v2 0/3] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
  2 siblings, 1 reply; 10+ messages in thread
From: Hou Wenlong @ 2022-01-20  9:33 UTC (permalink / raw)
  To: kvm
  Cc: Paolo Bonzini, Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li,
	Jim Mattson, Joerg Roedel, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Marcelo Tosatti, Gleb Natapov, linux-kernel

Per Intel's SDM on the "Instruction Set Reference", when
loading segment descriptor, not-present segment check should
be after all type and privilege checks. But the emulator checks
it first, then #NP is triggered instead of #GP if privilege fails
and segment is not present. Put not-present segment check after
type and privilege checks in __load_segment_descriptor().

Fixes: 38ba30ba51a00 (KVM: x86 emulator: Emulate task switch in emulator.c)
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 arch/x86/kvm/emulate.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 166a145fc1e6..864db6fbe8db 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1616,11 +1616,6 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 		goto exception;
 	}

-	if (!seg_desc.p) {
-		err_vec = (seg == VCPU_SREG_SS) ? SS_VECTOR : NP_VECTOR;
-		goto exception;
-	}
-
 	dpl = seg_desc.dpl;

 	switch (seg) {
@@ -1660,6 +1655,10 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 	case VCPU_SREG_TR:
 		if (seg_desc.s || (seg_desc.type != 1 && seg_desc.type != 9))
 			goto exception;
+		if (!seg_desc.p) {
+			err_vec = NP_VECTOR;
+			goto exception;
+		}
 		old_desc = seg_desc;
 		seg_desc.type |= 2; /* busy */
 		ret = ctxt->ops->cmpxchg_emulated(ctxt, desc_addr, &old_desc, &seg_desc,
@@ -1684,6 +1683,11 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 		break;
 	}

+	if (!seg_desc.p) {
+		err_vec = (seg == VCPU_SREG_SS) ? SS_VECTOR : NP_VECTOR;
+		goto exception;
+	}
+
 	if (seg_desc.s) {
 		/* mark segment as accessed */
 		if (!(seg_desc.type & 1)) {
--
2.31.1


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

* [PATCH 2/2] KVM: x86: Fix wrong privilege check for code segment in __load_segment_descriptor()
  2022-01-20  9:33 [PATCH 0/2] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
  2022-01-20  9:33 ` [PATCH 1/2] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
@ 2022-01-20  9:33 ` Hou Wenlong
  2022-02-07 19:51   ` Sean Christopherson
  2022-02-08  9:34 ` [PATCH v2 0/3] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
  2 siblings, 1 reply; 10+ messages in thread
From: Hou Wenlong @ 2022-01-20  9:33 UTC (permalink / raw)
  To: kvm
  Cc: Paolo Bonzini, Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li,
	Jim Mattson, Joerg Roedel, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, linux-kernel

Code segment descriptor can be loaded by jmp/call/ret, iret
and int. The privilege checks are different between those
instructions above realmode. Although, the emulator has
use x86_transfer_type enumerate to differentiate them, but
it is not really used in __load_segment_descriptor(). Note,
far jump/call to call gate, task gate or task state segment
are not implemented in emulator.

As for far jump/call to code segment, if DPL > CPL for conforming
code or (RPL > CPL or DPL != CPL) for non-conforming code, it
should trigger #GP. The current checks are ok.

As for far return, if RPL < CPL or DPL > RPL for conforming
code or DPL != RPL for non-conforming code, it should trigger #GP.
Outer level return is not implemented above virtual-8086 mode in
emulator. So it implies that RPL <= CPL, but the current checks
wouldn't trigger #GP if RPL < CPL.

As for code segment loading in task switch, if DPL > RPL for conforming
code or DPL != RPL for non-conforming code, it should trigger #TS. Since
segment selector is loaded before segment descriptor when load state from
tss, it implies that RPL = CPL, so the current checks are ok.

The only problem in current implementation is mssing RPL < CPL check for
far return. However, change code to follow the manual is better.

Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 arch/x86/kvm/emulate.c | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 864db6fbe8db..b7ce2a85e58e 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1631,14 +1631,28 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 		if (!(seg_desc.type & 8))
 			goto exception;
 
-		if (seg_desc.type & 4) {
-			/* conforming */
-			if (dpl > cpl)
-				goto exception;
-		} else {
-			/* nonconforming */
-			if (rpl > cpl || dpl != cpl)
-				goto exception;
+		if (transfer == X86_TRANSFER_RET && rpl < cpl)
+			goto exception;
+		if (transfer == X86_TRANSFER_RET || X86_TRANSFER_TASK_SWITCH) {
+			if (seg_desc.type & 4) {
+				/* conforming */
+				if (dpl > rpl)
+					goto exception;
+			} else {
+				/* nonconforming */
+				if (dpl != rpl)
+					goto exception;
+			}
+		} else { /* X86_TRANSFER_CALL_JMP */
+			if (seg_desc.type & 4) {
+				/* conforming */
+				if (dpl > cpl)
+					goto exception;
+			} else {
+				/* nonconforming */
+				if (rpl > cpl || dpl != cpl)
+					goto exception;
+			}
 		}
 		/* in long-mode d/b must be clear if l is set */
 		if (seg_desc.d && seg_desc.l) {
-- 
2.31.1


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

* Re: [PATCH 1/2] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor()
  2022-01-20  9:33 ` [PATCH 1/2] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
@ 2022-02-07 19:21   ` Sean Christopherson
  0 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2022-02-07 19:21 UTC (permalink / raw)
  To: Hou Wenlong
  Cc: kvm, Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson,
	Joerg Roedel, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Marcelo Tosatti, Gleb Natapov,
	linux-kernel

On Thu, Jan 20, 2022, Hou Wenlong wrote:
> Per Intel's SDM on the "Instruction Set Reference", when
> loading segment descriptor, not-present segment check should
> be after all type and privilege checks. But the emulator checks
> it first, then #NP is triggered instead of #GP if privilege fails
> and segment is not present. Put not-present segment check after
> type and privilege checks in __load_segment_descriptor().

For posterity, KVM doesn't support CALL GATES or TASK GATES, so the "early" #NP
check for those is missing.

> Fixes: 38ba30ba51a00 (KVM: x86 emulator: Emulate task switch in emulator.c)
> Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
> ---

Reviewed-by: Sean Christopherson <seanjc@google.com>

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

* Re: [PATCH 2/2] KVM: x86: Fix wrong privilege check for code segment in __load_segment_descriptor()
  2022-01-20  9:33 ` [PATCH 2/2] KVM: x86: Fix wrong privilege check for code segment " Hou Wenlong
@ 2022-02-07 19:51   ` Sean Christopherson
  0 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2022-02-07 19:51 UTC (permalink / raw)
  To: Hou Wenlong
  Cc: kvm, Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson,
	Joerg Roedel, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, linux-kernel

On Thu, Jan 20, 2022, Hou Wenlong wrote:
> Code segment descriptor can be loaded by jmp/call/ret, iret
> and int. The privilege checks are different between those
> instructions above realmode. Although, the emulator has
> use x86_transfer_type enumerate to differentiate them, but
> it is not really used in __load_segment_descriptor(). Note,
> far jump/call to call gate, task gate or task state segment
> are not implemented in emulator.
> 
> As for far jump/call to code segment, if DPL > CPL for conforming
> code or (RPL > CPL or DPL != CPL) for non-conforming code, it
> should trigger #GP. The current checks are ok.
> 
> As for far return, if RPL < CPL or DPL > RPL for conforming
> code or DPL != RPL for non-conforming code, it should trigger #GP.
> Outer level return is not implemented above virtual-8086 mode in
> emulator. So it implies that RPL <= CPL, but the current checks
> wouldn't trigger #GP if RPL < CPL.
> 
> As for code segment loading in task switch, if DPL > RPL for conforming
> code or DPL != RPL for non-conforming code, it should trigger #TS. Since
> segment selector is loaded before segment descriptor when load state from
> tss, it implies that RPL = CPL, so the current checks are ok.
> 
> The only problem in current implementation is mssing RPL < CPL check for
> far return. However, change code to follow the manual is better.
> 
> Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
> ---

Reviewed-by: Sean Christopherson <seanjc@google.com>

>  arch/x86/kvm/emulate.c | 30 ++++++++++++++++++++++--------
>  1 file changed, 22 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index 864db6fbe8db..b7ce2a85e58e 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -1631,14 +1631,28 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
>  		if (!(seg_desc.type & 8))
>  			goto exception;
>  
> -		if (seg_desc.type & 4) {
> -			/* conforming */
> -			if (dpl > cpl)
> -				goto exception;
> -		} else {
> -			/* nonconforming */
> -			if (rpl > cpl || dpl != cpl)
> -				goto exception;

A comment here would be mildly helpful, e.g.

		/* RET can never return to an inner privilege level. */
> +		if (transfer == X86_TRANSFER_RET && rpl < cpl)
> +			goto exception;

And then as a follow-up patch, I think we can/should move the unhandled outer
privilege level logic here to make it easier to understand why the checks for RET
are incomplete, e.g.

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index a885b53dc7cc..a7cecd7beb91 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1631,8 +1631,15 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
                if (!(seg_desc.type & 8))
                        goto exception;

-               if (transfer == X86_TRANSFER_RET && rpl < cpl)
-                       goto exception;
+               if (transfer == X86_TRANSFER_RET) {
+                       /* RET can never return to an inner privilege level. */
+                       if (rpl < cpl)
+                               goto exception;
+                       /* Outer-privilege level return is not implemented */
+                       if (rpl > cpl)
+                               return X86EMUL_UNHANDLEABLE;
+               }
+
                if (transfer == X86_TRANSFER_RET || X86_TRANSFER_TASK_SWITCH) {
                        if (seg_desc.type & 4) {
                                /* conforming */
@@ -2227,9 +2234,6 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
        rc = emulate_pop(ctxt, &cs, ctxt->op_bytes);
        if (rc != X86EMUL_CONTINUE)
                return rc;
-       /* Outer-privilege level return is not implemented */
-       if (ctxt->mode >= X86EMUL_MODE_PROT16 && (cs & 3) > cpl)
-               return X86EMUL_UNHANDLEABLE;
        rc = __load_segment_descriptor(ctxt, (u16)cs, VCPU_SREG_CS, cpl,
                                       X86_TRANSFER_RET,
                                       &new_desc);

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

* [PATCH v2 0/3] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator
  2022-01-20  9:33 [PATCH 0/2] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
  2022-01-20  9:33 ` [PATCH 1/2] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
  2022-01-20  9:33 ` [PATCH 2/2] KVM: x86: Fix wrong privilege check for code segment " Hou Wenlong
@ 2022-02-08  9:34 ` Hou Wenlong
  2022-02-08  9:34   ` [PATCH v2 1/3] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
                     ` (2 more replies)
  2 siblings, 3 replies; 10+ messages in thread
From: Hou Wenlong @ 2022-02-08  9:34 UTC (permalink / raw)
  To: kvm

Per Intel's SDM on "Instruction Set Reference", code segment
can be loaded by far jmp/call/ret, iret and int. For all those
instructions, not-present segment check should be after type and
privilege checks. But the emulator checks it first, so #NP is
triggered instead of #GP if privilege check fails and the segment
is not present.

When loading code segment above realmode, RPL/CPL/DPL should be
checked, but the privilege checks are different between those
instructions. Since iret and int are only implemented for realmode
in emulator, no checks ared needed.

The current implement only checks if DPL > CPL for conforming
code or (RPL > CPL or DPL != CPL) for non-conforming code. Since
far call/jump to call gate, task gate and task state segment are
not implemented for in emulator, the current checks are enough.

As for far return, outer level return is not implemented above
virtual-8086 mode in emulator, so RPL <= CPL. Per Intel's SDM,
if RPL < CPL, it should trigger #GP, but it is missing in
emulator. Other checks are satisfied in current implementation.

When vmexit for task switch, code segment would also be loaded
from tss. Since segment selector is loaded before segment descriptor
when load state from tss, it implies that RPL = CPL, the checks
are satisfied too.

I add some tests in kvm-unit-tests[*] for the wrong checks in
emulator. Enable kvm.force_enable_emulation to test them on emulator.

[*] https://lore.kernel.org/kvm/cover.1644311445.git.houwenlong.hwl@antgroup.com

Changed from v1:
- Add a comment about RPL < CPL check for far return in patch 2.
- Fix a mistake when judge transfer type in patch 2.
- As Sean suggested, add a new patch to move the unhandled outer
  privilege level logic of far return into __load_segment_descriptor().

Hou Wenlong (3):
  KVM: x86/emulator: Defer not-present segment check in
    __load_segment_descriptor()
  KVM: x86/emulator: Fix wrong privilege check for code segment in
    __load_segment_descriptor()
  KVM: x86/emulator: Move the unhandled outer privilege level logic of
    far return into __load_segment_descriptor()

 arch/x86/kvm/emulate.c | 51 +++++++++++++++++++++++++++++-------------
 1 file changed, 36 insertions(+), 15 deletions(-)

--
2.31.1


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

* [PATCH v2 1/3] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor()
  2022-02-08  9:34 ` [PATCH v2 0/3] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
@ 2022-02-08  9:34   ` Hou Wenlong
  2022-02-08  9:34   ` [PATCH v2 2/3] KVM: x86/emulator: Fix wrong privilege check for code segment " Hou Wenlong
  2022-02-08  9:34   ` [PATCH v2 3/3] KVM: x86/emulator: Move the unhandled outer privilege level logic of far return into __load_segment_descriptor() Hou Wenlong
  2 siblings, 0 replies; 10+ messages in thread
From: Hou Wenlong @ 2022-02-08  9:34 UTC (permalink / raw)
  To: kvm
  Cc: Sean Christopherson, Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li,
	Jim Mattson, Joerg Roedel, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Marcelo Tosatti, Gleb Natapov, linux-kernel

Per Intel's SDM on the "Instruction Set Reference", when
loading segment descriptor, not-present segment check should
be after all type and privilege checks. But the emulator checks
it first, then #NP is triggered instead of #GP if privilege fails
and segment is not present. Put not-present segment check after
type and privilege checks in __load_segment_descriptor().

Fixes: 38ba30ba51a00 (KVM: x86 emulator: Emulate task switch in emulator.c)
Reviewed-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 arch/x86/kvm/emulate.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 9e4a00d04532..b7ee7de9f8cd 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1616,11 +1616,6 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 		goto exception;
 	}

-	if (!seg_desc.p) {
-		err_vec = (seg == VCPU_SREG_SS) ? SS_VECTOR : NP_VECTOR;
-		goto exception;
-	}
-
 	dpl = seg_desc.dpl;

 	switch (seg) {
@@ -1660,6 +1655,10 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 	case VCPU_SREG_TR:
 		if (seg_desc.s || (seg_desc.type != 1 && seg_desc.type != 9))
 			goto exception;
+		if (!seg_desc.p) {
+			err_vec = NP_VECTOR;
+			goto exception;
+		}
 		old_desc = seg_desc;
 		seg_desc.type |= 2; /* busy */
 		ret = ctxt->ops->cmpxchg_emulated(ctxt, desc_addr, &old_desc, &seg_desc,
@@ -1684,6 +1683,11 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 		break;
 	}

+	if (!seg_desc.p) {
+		err_vec = (seg == VCPU_SREG_SS) ? SS_VECTOR : NP_VECTOR;
+		goto exception;
+	}
+
 	if (seg_desc.s) {
 		/* mark segment as accessed */
 		if (!(seg_desc.type & 1)) {
--
2.31.1


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

* [PATCH v2 2/3] KVM: x86/emulator: Fix wrong privilege check for code segment in __load_segment_descriptor()
  2022-02-08  9:34 ` [PATCH v2 0/3] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
  2022-02-08  9:34   ` [PATCH v2 1/3] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
@ 2022-02-08  9:34   ` Hou Wenlong
  2022-02-08  9:34   ` [PATCH v2 3/3] KVM: x86/emulator: Move the unhandled outer privilege level logic of far return into __load_segment_descriptor() Hou Wenlong
  2 siblings, 0 replies; 10+ messages in thread
From: Hou Wenlong @ 2022-02-08  9:34 UTC (permalink / raw)
  To: kvm
  Cc: Sean Christopherson, Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li,
	Jim Mattson, Joerg Roedel, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, linux-kernel

Code segment descriptor can be loaded by jmp/call/ret, iret
and int. The privilege checks are different between those
instructions above realmode. Although, the emulator has
use x86_transfer_type enumerate to differentiate them, but
it is not really used in __load_segment_descriptor(). Note,
far jump/call to call gate, task gate or task state segment
are not implemented in emulator.

As for far jump/call to code segment, if DPL > CPL for conforming
code or (RPL > CPL or DPL != CPL) for non-conforming code, it
should trigger #GP. The current checks are ok.

As for far return, if RPL < CPL or DPL > RPL for conforming
code or DPL != RPL for non-conforming code, it should trigger #GP.
Outer level return is not implemented above virtual-8086 mode in
emulator. So it implies that RPL <= CPL, but the current checks
wouldn't trigger #GP if RPL < CPL.

As for code segment loading in task switch, if DPL > RPL for conforming
code or DPL != RPL for non-conforming code, it should trigger #TS. Since
segment selector is loaded before segment descriptor when load state from
tss, it implies that RPL = CPL, so the current checks are ok.

The only problem in current implementation is missing RPL < CPL check for
far return. However, change code to follow the manual is better.

Reviewed-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 arch/x86/kvm/emulate.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index b7ee7de9f8cd..37c4213bdcc1 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1631,14 +1631,29 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 		if (!(seg_desc.type & 8))
 			goto exception;

-		if (seg_desc.type & 4) {
-			/* conforming */
-			if (dpl > cpl)
-				goto exception;
-		} else {
-			/* nonconforming */
-			if (rpl > cpl || dpl != cpl)
-				goto exception;
+		/* RET can never return to an inner privilege level. */
+		if (transfer == X86_TRANSFER_RET && rpl < cpl)
+			goto exception;
+		if (transfer == X86_TRANSFER_RET || transfer == X86_TRANSFER_TASK_SWITCH) {
+			if (seg_desc.type & 4) {
+				/* conforming */
+				if (dpl > rpl)
+					goto exception;
+			} else {
+				/* nonconforming */
+				if (dpl != rpl)
+					goto exception;
+			}
+		} else { /* X86_TRANSFER_CALL_JMP */
+			if (seg_desc.type & 4) {
+				/* conforming */
+				if (dpl > cpl)
+					goto exception;
+			} else {
+				/* nonconforming */
+				if (rpl > cpl || dpl != cpl)
+					goto exception;
+			}
 		}
 		/* in long-mode d/b must be clear if l is set */
 		if (seg_desc.d && seg_desc.l) {
--
2.31.1


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

* [PATCH v2 3/3] KVM: x86/emulator: Move the unhandled outer privilege level logic of far return into __load_segment_descriptor()
  2022-02-08  9:34 ` [PATCH v2 0/3] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
  2022-02-08  9:34   ` [PATCH v2 1/3] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
  2022-02-08  9:34   ` [PATCH v2 2/3] KVM: x86/emulator: Fix wrong privilege check for code segment " Hou Wenlong
@ 2022-02-08  9:34   ` Hou Wenlong
  2022-02-25 15:07     ` Paolo Bonzini
  2 siblings, 1 reply; 10+ messages in thread
From: Hou Wenlong @ 2022-02-08  9:34 UTC (permalink / raw)
  To: kvm
  Cc: Sean Christopherson, Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li,
	Jim Mattson, Joerg Roedel, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, linux-kernel

Outer-privilege level return is not implemented in emulator,
move the unhandled logic into __load_segment_descriptor to
make it easier to understand why the checks for RET are
incomplete.

Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 arch/x86/kvm/emulate.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 37c4213bdcc1..bd91b952eb0a 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1631,9 +1631,14 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 		if (!(seg_desc.type & 8))
 			goto exception;
 
-		/* RET can never return to an inner privilege level. */
-		if (transfer == X86_TRANSFER_RET && rpl < cpl)
-			goto exception;
+		if (transfer == X86_TRANSFER_RET) {
+			/* RET can never return to an inner privilege level. */
+			if (rpl < cpl)
+				goto exception;
+			/* Outer-privilege level return is not implemented */
+			if (rpl > cpl)
+				return X86EMUL_UNHANDLEABLE;
+		}
 		if (transfer == X86_TRANSFER_RET || transfer == X86_TRANSFER_TASK_SWITCH) {
 			if (seg_desc.type & 4) {
 				/* conforming */
@@ -2228,9 +2233,6 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
 	rc = emulate_pop(ctxt, &cs, ctxt->op_bytes);
 	if (rc != X86EMUL_CONTINUE)
 		return rc;
-	/* Outer-privilege level return is not implemented */
-	if (ctxt->mode >= X86EMUL_MODE_PROT16 && (cs & 3) > cpl)
-		return X86EMUL_UNHANDLEABLE;
 	rc = __load_segment_descriptor(ctxt, (u16)cs, VCPU_SREG_CS, cpl,
 				       X86_TRANSFER_RET,
 				       &new_desc);
-- 
2.31.1


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

* Re: [PATCH v2 3/3] KVM: x86/emulator: Move the unhandled outer privilege level logic of far return into __load_segment_descriptor()
  2022-02-08  9:34   ` [PATCH v2 3/3] KVM: x86/emulator: Move the unhandled outer privilege level logic of far return into __load_segment_descriptor() Hou Wenlong
@ 2022-02-25 15:07     ` Paolo Bonzini
  0 siblings, 0 replies; 10+ messages in thread
From: Paolo Bonzini @ 2022-02-25 15:07 UTC (permalink / raw)
  To: Hou Wenlong, kvm
  Cc: Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson,
	Joerg Roedel, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, linux-kernel

On 2/8/22 10:34, Hou Wenlong wrote:
> Outer-privilege level return is not implemented in emulator,
> move the unhandled logic into __load_segment_descriptor to
> make it easier to understand why the checks for RET are
> incomplete.
> 
> Suggested-by: Sean Christopherson <seanjc@google.com>
> Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
> ---
>   arch/x86/kvm/emulate.c | 14 ++++++++------
>   1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index 37c4213bdcc1..bd91b952eb0a 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -1631,9 +1631,14 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
>   		if (!(seg_desc.type & 8))
>   			goto exception;
>   
> -		/* RET can never return to an inner privilege level. */
> -		if (transfer == X86_TRANSFER_RET && rpl < cpl)
> -			goto exception;
> +		if (transfer == X86_TRANSFER_RET) {
> +			/* RET can never return to an inner privilege level. */
> +			if (rpl < cpl)
> +				goto exception;
> +			/* Outer-privilege level return is not implemented */
> +			if (rpl > cpl)
> +				return X86EMUL_UNHANDLEABLE;
> +		}
>   		if (transfer == X86_TRANSFER_RET || transfer == X86_TRANSFER_TASK_SWITCH) {
>   			if (seg_desc.type & 4) {
>   				/* conforming */
> @@ -2228,9 +2233,6 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
>   	rc = emulate_pop(ctxt, &cs, ctxt->op_bytes);
>   	if (rc != X86EMUL_CONTINUE)
>   		return rc;
> -	/* Outer-privilege level return is not implemented */
> -	if (ctxt->mode >= X86EMUL_MODE_PROT16 && (cs & 3) > cpl)
> -		return X86EMUL_UNHANDLEABLE;
>   	rc = __load_segment_descriptor(ctxt, (u16)cs, VCPU_SREG_CS, cpl,
>   				       X86_TRANSFER_RET,
>   				       &new_desc);

Queued all three, thanks!

Paolo


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

end of thread, other threads:[~2022-02-25 15:07 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-20  9:33 [PATCH 0/2] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
2022-01-20  9:33 ` [PATCH 1/2] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
2022-02-07 19:21   ` Sean Christopherson
2022-01-20  9:33 ` [PATCH 2/2] KVM: x86: Fix wrong privilege check for code segment " Hou Wenlong
2022-02-07 19:51   ` Sean Christopherson
2022-02-08  9:34 ` [PATCH v2 0/3] KVM: x86/emulator: Fix wrong checks when loading code segment in emulator Hou Wenlong
2022-02-08  9:34   ` [PATCH v2 1/3] KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() Hou Wenlong
2022-02-08  9:34   ` [PATCH v2 2/3] KVM: x86/emulator: Fix wrong privilege check for code segment " Hou Wenlong
2022-02-08  9:34   ` [PATCH v2 3/3] KVM: x86/emulator: Move the unhandled outer privilege level logic of far return into __load_segment_descriptor() Hou Wenlong
2022-02-25 15:07     ` Paolo Bonzini

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.