diff mbox

[20/21] KVM: x86: MOVNTI emulation min opsize is not respected

Message ID 1414922101-17626-21-git-send-email-namit@cs.technion.ac.il (mailing list archive)
State New, archived
Headers show

Commit Message

Nadav Amit Nov. 2, 2014, 9:55 a.m. UTC
Commit 3b32004a66e9 ("KVM: x86: movnti minimum op size of 32-bit is not kept")
did not fully fix the minimum operand size of MONTI emulation. Still, MOVNTI
may be mistakenly performed using 16-bit opsize.

This patch add No16 flag to mark an instruction does not support 16-bits
operand size.

Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
---
 arch/x86/kvm/emulate.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

Comments

Paolo Bonzini Nov. 5, 2014, 12:18 p.m. UTC | #1
On 02/11/2014 10:55, Nadav Amit wrote:
> Commit 3b32004a66e9 ("KVM: x86: movnti minimum op size of 32-bit is not kept")
> did not fully fix the minimum operand size of MONTI emulation. Still, MOVNTI
> may be mistakenly performed using 16-bit opsize.
> 
> This patch add No16 flag to mark an instruction does not support 16-bits
> operand size.

So a

    .byte 0x66
    movntiw (%esi), %eax

will zero the higher two bytes of %eax before this patch, and load 4
bytes from (%esi) after?

Paolo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nadav Amit Nov. 5, 2014, 7:58 p.m. UTC | #2
> On Nov 5, 2014, at 14:18, Paolo Bonzini <pbonzini@redhat.com> wrote:
> 
> 
> 
> On 02/11/2014 10:55, Nadav Amit wrote:
>> Commit 3b32004a66e9 ("KVM: x86: movnti minimum op size of 32-bit is not kept")
>> did not fully fix the minimum operand size of MONTI emulation. Still, MOVNTI
>> may be mistakenly performed using 16-bit opsize.
>> 
>> This patch add No16 flag to mark an instruction does not support 16-bits
>> operand size.
> 
> So a
> 
>    .byte 0x66
>    movntiw (%esi), %eax
> 
> will zero the higher two bytes of %eax before this patch, and load 4
> bytes from (%esi) after?
> 
Well, actually the 0x66 prefix is an illegal prefix for this instruction, so it will cause #UD.
But if the default operand size is 16 (e.g., CS.D = 0), then yes - after this patch it will load 4 bytes from (%esi), and this is the expected behaviour.

Here is a small test to show the behaviour (build with -m32 ).
We set CS to 16-bit segment, so default operand size is 16-bit, but 32-bits are assigned.
If you replace movntil with movl, you’ll see only 16-bits are stored, as you would expect from mov.

---
#include <sys/types.h>
#include <asm/ldt.h>
#include <stdio.h>

int main()
{
	unsigned int a = 0;
	unsigned int b = 0x87654321u;

	struct user_desc d = {
		.entry_number = 0,
		.base_addr = 0,
		.limit = 0xfffffu,
		.seg_32bit = 0, 
		.contents = 2,
		.read_exec_only = 1,
		.limit_in_pages = 1,
		.seg_not_present = 0,
		.useable = 1,
	};

	modify_ldt(1, &d, sizeof(d));
	asm volatile (  "lcall $0x7, $1f\n\t"
			"jmp 2f\n\t"
			"1: .byte 0x67\n\t"
			"movntil %%ebx, (%%eax)\n\t"
			".byte 0x66\n\t"
			"lret\n\t"
			"2:\n\t"
			: : "a"(&a), "b"(b) : "memory");
	printf("result %x\n", a);
	return 0;
}
---

Nadav

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nadav Amit Nov. 5, 2014, 7:58 p.m. UTC | #3
> On Nov 5, 2014, at 14:18, Paolo Bonzini <pbonzini@redhat.com> wrote:
> 
> 
> 
> On 02/11/2014 10:55, Nadav Amit wrote:
>> Commit 3b32004a66e9 ("KVM: x86: movnti minimum op size of 32-bit is not kept")
>> did not fully fix the minimum operand size of MONTI emulation. Still, MOVNTI
>> may be mistakenly performed using 16-bit opsize.
>> 
>> This patch add No16 flag to mark an instruction does not support 16-bits
>> operand size.
> 
> So a
> 
>   .byte 0x66
>   movntiw (%esi), %eax
> 
> will zero the higher two bytes of %eax before this patch, and load 4
> bytes from (%esi) after?
> 
Well, actually the 0x66 prefix is an illegal prefix for this instruction, so it will cause #UD.
But if the default operand size is 16 (e.g., CS.D = 0), then yes - after this patch it will load 4 bytes from (%esi), and this is the expected behaviour.

Here is a small test to show the behaviour (build with -m32 ).
We set CS to 16-bit segment, so default operand size is 16-bit, but 32-bits are assigned.
If you replace movntil with movl, you’ll see only 16-bits are stored, as you would expect from mov.

---
#include <sys/types.h>
#include <asm/ldt.h>
#include <stdio.h>

int main()
{
	unsigned int a = 0;
	unsigned int b = 0x87654321u;

	struct user_desc d = {
		.entry_number = 0,
		.base_addr = 0,
		.limit = 0xfffffu,
		.seg_32bit = 0, 
		.contents = 2,
		.read_exec_only = 1,
		.limit_in_pages = 1,
		.seg_not_present = 0,
		.useable = 1,
	};

	modify_ldt(1, &d, sizeof(d));
	asm volatile (  "lcall $0x7, $1f\n\t"
			"jmp 2f\n\t"
			"1: .byte 0x67\n\t"
			"movntil %%ebx, (%%eax)\n\t"
			".byte 0x66\n\t"
			"lret\n\t"
			"2:\n\t"
			: : "a"(&a), "b"(b) : "memory");
	printf("result %x\n", a);
	return 0;
}
---

Nadav

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paolo Bonzini Nov. 6, 2014, 9:23 a.m. UTC | #4
On 02/11/2014 10:55, Nadav Amit wrote:
> Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
> ---

Applied, thanks.

Paolo

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 24b0df7..84a83dc 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -167,6 +167,7 @@ 
 #define NoBigReal   ((u64)1 << 50)  /* No big real mode */
 #define PrivUD      ((u64)1 << 51)  /* #UD instead of #GP on CPL > 0 */
 #define NearBranch  ((u64)1 << 52)  /* Near branches */
+#define No16	    ((u64)1 << 53)  /* No 16 bit operand */
 
 #define DstXacc     (DstAccLo | SrcAccHi | SrcWrite)
 
@@ -4116,7 +4117,7 @@  static const struct opcode twobyte_table[256] = {
 	D(DstReg | SrcMem8 | ModRM | Mov), D(DstReg | SrcMem16 | ModRM | Mov),
 	/* 0xC0 - 0xC7 */
 	F2bv(DstMem | SrcReg | ModRM | SrcWrite | Lock, em_xadd),
-	N, D(DstMem | SrcReg | ModRM | Mov),
+	N, I(DstMem | SrcReg | ModRM | No16 | Mov, em_mov),
 	N, N, N, GD(0, &group9),
 	/* 0xC8 - 0xCF */
 	X8(I(DstReg, em_bswap)),
@@ -4561,7 +4562,8 @@  done_prefixes:
 		return EMULATION_FAILED;
 
 	if (unlikely(ctxt->d &
-	    (NotImpl|Stack|Op3264|Sse|Mmx|Intercept|CheckPerm|NearBranch))) {
+	    (NotImpl|Stack|Op3264|Sse|Mmx|Intercept|CheckPerm|NearBranch|
+	     No16))) {
 		/*
 		 * These are copied unconditionally here, and checked unconditionally
 		 * in x86_emulate_insn.
@@ -4586,6 +4588,9 @@  done_prefixes:
 				ctxt->op_bytes = 4;
 		}
 
+		if ((ctxt->d & No16) && ctxt->op_bytes == 2)
+			ctxt->op_bytes = 4;
+
 		if (ctxt->d & Sse)
 			ctxt->op_bytes = 16;
 		else if (ctxt->d & Mmx)
@@ -5051,11 +5056,6 @@  twobyte_insn:
 		ctxt->dst.val = (ctxt->src.bytes == 1) ? (s8) ctxt->src.val :
 							(s16) ctxt->src.val;
 		break;
-	case 0xc3:		/* movnti */
-		ctxt->dst.bytes = ctxt->op_bytes;
-		ctxt->dst.val = (ctxt->op_bytes == 8) ? (u64) ctxt->src.val :
-							(u32) ctxt->src.val;
-		break;
 	default:
 		goto cannot_emulate;
 	}