Re: [PATCH v10 1/9] x86emul: address x86_insn_is_mem_{access, write}() omissions

2020-05-29 Thread Jan Beulich
On 29.05.2020 17:03, Andrew Cooper wrote:
> On 29/05/2020 14:29, Jan Beulich wrote:
>> On 29.05.2020 14:18, Andrew Cooper wrote:
>>> On 25/05/2020 15:26, Jan Beulich wrote:
 --- a/xen/arch/x86/x86_emulate/x86_emulate.c
 +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
 @@ -11474,25 +11474,87 @@ x86_insn_operand_ea(const struct x86_emu
  return state->ea.mem.off;
  }
  
 +/*
 + * This function means to return 'true' for all supported insns with 
 explicit
 + * accesses to memory.  This means also insns which don't have an explicit
 + * memory operand (like POP), but it does not mean e.g. segment selector
 + * loads, where the descriptor table access is considered an implicit one.
 + */
  bool
  x86_insn_is_mem_access(const struct x86_emulate_state *state,
 const struct x86_emulate_ctxt *ctxt)
  {
 +if ( mode_64bit() && state->not_64bit )
 +return false;
>>> Is this path actually used?
>> Yes, it is. It's only x86_emulate() which has
>>
>> generate_exception_if(state->not_64bit && mode_64bit(), EXC_UD);
>>
>> right now.
> 
> Oh.  That is a bit awkward.
> 
>>> state->not_64bit ought to fail instruction
>>> decode, at which point we wouldn't have a valid state to be used here.
>> x86_decode() currently doesn't have much raising of #UD at all, I
>> think. If it wasn't like this, the not_64bit field wouldn't be
>> needed - it's used only to communicate from decode to execute.
>> We're not entirely consistent with this though, seeing in
>> x86_decode_onebyte(), a few lines below the block of case labels
>> setting that field,
>>
>> case 0x9a: /* call (far, absolute) */
>> case 0xea: /* jmp (far, absolute) */
>> generate_exception_if(mode_64bit(), EXC_UD);
> 
> This is because there is no legitimate way to determine the end of the
> instruction.
> 
> Most of the not_64bit instructions do have a well-defined end, even if
> they aren't permitted for use.

I wouldn't bet on that. Just look at the re-use of opcode D6
(salc in 32-bit) by L1OM for an extreme example. There's nothing
we can say on how any of the reserved opcodes may get re-used.
Prior to AVX / AVX512 we'd have estimated C4, C5, and 62 wrongly
as well (but that's true independent of execution mode).

>> We could certainly drop the field and raise #UD during decode
>> already, but don't you think we then should do so for all
>> encodings that ultimately lead to #UD, e.g. also for AVX insns
>> without AVX available to the guest? This would make
>> x86_decode() quite a bit larger, as it would then also need to
>> have a giant switch() (or something else that's suitable to
>> cover all cases).
> 
> I think there is a semantic difference between "we can't parse the
> instruction", and "we can parse it, but it's not legitimate in this
> context".
> 
> I don't think our exact split is correct, but I don't think moving all
> #UD checking into x86_decode() is correct either.

Do you have any clear, sufficiently simple rule in mind for where
we could draw the boundary?

Jan



Re: [PATCH v10 1/9] x86emul: address x86_insn_is_mem_{access, write}() omissions

2020-05-29 Thread Andrew Cooper
On 29/05/2020 14:29, Jan Beulich wrote:
> On 29.05.2020 14:18, Andrew Cooper wrote:
>> On 25/05/2020 15:26, Jan Beulich wrote:
>>> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
>>> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
>>> @@ -11474,25 +11474,87 @@ x86_insn_operand_ea(const struct x86_emu
>>>  return state->ea.mem.off;
>>>  }
>>>  
>>> +/*
>>> + * This function means to return 'true' for all supported insns with 
>>> explicit
>>> + * accesses to memory.  This means also insns which don't have an explicit
>>> + * memory operand (like POP), but it does not mean e.g. segment selector
>>> + * loads, where the descriptor table access is considered an implicit one.
>>> + */
>>>  bool
>>>  x86_insn_is_mem_access(const struct x86_emulate_state *state,
>>> const struct x86_emulate_ctxt *ctxt)
>>>  {
>>> +if ( mode_64bit() && state->not_64bit )
>>> +return false;
>> Is this path actually used?
> Yes, it is. It's only x86_emulate() which has
>
> generate_exception_if(state->not_64bit && mode_64bit(), EXC_UD);
>
> right now.

Oh.  That is a bit awkward.

>> state->not_64bit ought to fail instruction
>> decode, at which point we wouldn't have a valid state to be used here.
> x86_decode() currently doesn't have much raising of #UD at all, I
> think. If it wasn't like this, the not_64bit field wouldn't be
> needed - it's used only to communicate from decode to execute.
> We're not entirely consistent with this though, seeing in
> x86_decode_onebyte(), a few lines below the block of case labels
> setting that field,
>
> case 0x9a: /* call (far, absolute) */
> case 0xea: /* jmp (far, absolute) */
> generate_exception_if(mode_64bit(), EXC_UD);

This is because there is no legitimate way to determine the end of the
instruction.

Most of the not_64bit instructions do have a well-defined end, even if
they aren't permitted for use.

> We could certainly drop the field and raise #UD during decode
> already, but don't you think we then should do so for all
> encodings that ultimately lead to #UD, e.g. also for AVX insns
> without AVX available to the guest? This would make
> x86_decode() quite a bit larger, as it would then also need to
> have a giant switch() (or something else that's suitable to
> cover all cases).

I think there is a semantic difference between "we can't parse the
instruction", and "we can parse it, but it's not legitimate in this
context".

I don't think our exact split is correct, but I don't think moving all
#UD checking into x86_decode() is correct either.

~Andrew



Re: [PATCH v10 1/9] x86emul: address x86_insn_is_mem_{access, write}() omissions

2020-05-29 Thread Jan Beulich
On 29.05.2020 14:18, Andrew Cooper wrote:
> On 25/05/2020 15:26, Jan Beulich wrote:
>> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
>> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
>> @@ -11474,25 +11474,87 @@ x86_insn_operand_ea(const struct x86_emu
>>  return state->ea.mem.off;
>>  }
>>  
>> +/*
>> + * This function means to return 'true' for all supported insns with 
>> explicit
>> + * accesses to memory.  This means also insns which don't have an explicit
>> + * memory operand (like POP), but it does not mean e.g. segment selector
>> + * loads, where the descriptor table access is considered an implicit one.
>> + */
>>  bool
>>  x86_insn_is_mem_access(const struct x86_emulate_state *state,
>> const struct x86_emulate_ctxt *ctxt)
>>  {
>> +if ( mode_64bit() && state->not_64bit )
>> +return false;
> 
> Is this path actually used?

Yes, it is. It's only x86_emulate() which has

generate_exception_if(state->not_64bit && mode_64bit(), EXC_UD);

right now.

> state->not_64bit ought to fail instruction
> decode, at which point we wouldn't have a valid state to be used here.

x86_decode() currently doesn't have much raising of #UD at all, I
think. If it wasn't like this, the not_64bit field wouldn't be
needed - it's used only to communicate from decode to execute.
We're not entirely consistent with this though, seeing in
x86_decode_onebyte(), a few lines below the block of case labels
setting that field,

case 0x9a: /* call (far, absolute) */
case 0xea: /* jmp (far, absolute) */
generate_exception_if(mode_64bit(), EXC_UD);

We could certainly drop the field and raise #UD during decode
already, but don't you think we then should do so for all
encodings that ultimately lead to #UD, e.g. also for AVX insns
without AVX available to the guest? This would make
x86_decode() quite a bit larger, as it would then also need to
have a giant switch() (or something else that's suitable to
cover all cases).

> Everything else looks ok, so Acked-by: Andrew Cooper
> 

Thanks.

Jan



Re: [PATCH v10 1/9] x86emul: address x86_insn_is_mem_{access, write}() omissions

2020-05-29 Thread Andrew Cooper
On 25/05/2020 15:26, Jan Beulich wrote:
> First of all explain in comments what the functions' purposes are. Then
> make them actually match their comments.
>
> Note that fc6fa977be54 ("x86emul: extend x86_insn_is_mem_write()
> coverage") didn't actually fix the function's behavior for {,V}STMXCSR:
> Both are covered by generic code higher up in the function, due to
> x86_decode_twobyte() already doing suitable adjustments. And VSTMXCSR
> wouldn't have been covered anyway without a further X86EMUL_OPC_VEX()
> case label. Keep the inner case label in a comment for reference.
>
> Signed-off-by: Jan Beulich 
> ---
> v10: Move ARPL case to earlier switch() x86_insn_is_mem_write(). Make
>  group 5 handling actually work there. Drop VMPTRST case. Also
>  handle CLFLUSH*, CLWB, UDn, and remaining PREFETCH* in
>  x86_insn_is_mem_access().
> v9: New.
>
> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
> @@ -11474,25 +11474,87 @@ x86_insn_operand_ea(const struct x86_emu
>  return state->ea.mem.off;
>  }
>  
> +/*
> + * This function means to return 'true' for all supported insns with explicit
> + * accesses to memory.  This means also insns which don't have an explicit
> + * memory operand (like POP), but it does not mean e.g. segment selector
> + * loads, where the descriptor table access is considered an implicit one.
> + */
>  bool
>  x86_insn_is_mem_access(const struct x86_emulate_state *state,
> const struct x86_emulate_ctxt *ctxt)
>  {
> +if ( mode_64bit() && state->not_64bit )
> +return false;

Is this path actually used?  state->not_64bit ought to fail instruction
decode, at which point we wouldn't have a valid state to be used here.

Everything else looks ok, so Acked-by: Andrew Cooper




[PATCH v10 1/9] x86emul: address x86_insn_is_mem_{access,write}() omissions

2020-05-25 Thread Jan Beulich
First of all explain in comments what the functions' purposes are. Then
make them actually match their comments.

Note that fc6fa977be54 ("x86emul: extend x86_insn_is_mem_write()
coverage") didn't actually fix the function's behavior for {,V}STMXCSR:
Both are covered by generic code higher up in the function, due to
x86_decode_twobyte() already doing suitable adjustments. And VSTMXCSR
wouldn't have been covered anyway without a further X86EMUL_OPC_VEX()
case label. Keep the inner case label in a comment for reference.

Signed-off-by: Jan Beulich 
---
v10: Move ARPL case to earlier switch() x86_insn_is_mem_write(). Make
 group 5 handling actually work there. Drop VMPTRST case. Also
 handle CLFLUSH*, CLWB, UDn, and remaining PREFETCH* in
 x86_insn_is_mem_access().
v9: New.

--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -11474,25 +11474,87 @@ x86_insn_operand_ea(const struct x86_emu
 return state->ea.mem.off;
 }
 
+/*
+ * This function means to return 'true' for all supported insns with explicit
+ * accesses to memory.  This means also insns which don't have an explicit
+ * memory operand (like POP), but it does not mean e.g. segment selector
+ * loads, where the descriptor table access is considered an implicit one.
+ */
 bool
 x86_insn_is_mem_access(const struct x86_emulate_state *state,
const struct x86_emulate_ctxt *ctxt)
 {
+if ( mode_64bit() && state->not_64bit )
+return false;
+
 if ( state->ea.type == OP_MEM )
-return ctxt->opcode != 0x8d /* LEA */ &&
-   (ctxt->opcode != X86EMUL_OPC(0x0f, 0x01) ||
-(state->modrm_reg & 7) != 7) /* INVLPG */;
+{
+switch ( ctxt->opcode )
+{
+case 0x8d: /* LEA */
+case X86EMUL_OPC(0x0f, 0x0d): /* PREFETCH */
+case X86EMUL_OPC(0x0f, 0x18)
+ ... X86EMUL_OPC(0x0f, 0x1f): /* NOP space */
+case X86EMUL_OPC_66(0x0f, 0x18)
+ ... X86EMUL_OPC_66(0x0f, 0x1f): /* NOP space */
+case X86EMUL_OPC_F3(0x0f, 0x18)
+ ... X86EMUL_OPC_F3(0x0f, 0x1f): /* NOP space */
+case X86EMUL_OPC_F2(0x0f, 0x18)
+ ... X86EMUL_OPC_F2(0x0f, 0x1f): /* NOP space */
+case X86EMUL_OPC(0x0f, 0xb9): /* UD1 */
+case X86EMUL_OPC(0x0f, 0xff): /* UD0 */
+return false;
+
+case X86EMUL_OPC(0x0f, 0x01):
+return (state->modrm_reg & 7) != 7; /* INVLPG */
+
+case X86EMUL_OPC(0x0f, 0xae):
+return (state->modrm_reg & 7) != 7; /* CLFLUSH */
+
+case X86EMUL_OPC_66(0x0f, 0xae):
+return (state->modrm_reg & 7) < 6; /* CLWB, CLFLUSHOPT */
+}
+
+return true;
+}
 
 switch ( ctxt->opcode )
 {
+case 0x06 ... 0x07: /* PUSH / POP %es */
+case 0x0e:  /* PUSH %cs */
+case 0x16 ... 0x17: /* PUSH / POP %ss */
+case 0x1e ... 0x1f: /* PUSH / POP %ds */
+case 0x50 ... 0x5f: /* PUSH / POP reg */
+case 0x60 ... 0x61: /* PUSHA / POPA */
+case 0x68: case 0x6a: /* PUSH imm */
 case 0x6c ... 0x6f: /* INS / OUTS */
+case 0x8f:  /* POP r/m */
+case 0x9a:  /* CALL (far, direct) */
+case 0x9c ... 0x9d: /* PUSHF / POPF */
 case 0xa4 ... 0xa7: /* MOVS / CMPS */
 case 0xaa ... 0xaf: /* STOS / LODS / SCAS */
+case 0xc2 ... 0xc3: /* RET (near) */
+case 0xc8 ... 0xc9: /* ENTER / LEAVE */
+case 0xca ... 0xcb: /* RET (far) */
 case 0xd7:  /* XLAT */
+case 0xe8:  /* CALL (near, direct) */
+case X86EMUL_OPC(0x0f, 0xa0): /* PUSH %fs */
+case X86EMUL_OPC(0x0f, 0xa1): /* POP %fs */
+case X86EMUL_OPC(0x0f, 0xa8): /* PUSH %gs */
+case X86EMUL_OPC(0x0f, 0xa9): /* POP %gs */
 CASE_SIMD_PACKED_INT_VEX(0x0f, 0xf7): /* MASKMOV{Q,DQU} */
   /* VMASKMOVDQU */
 return true;
 
+case 0xff:
+switch ( state->modrm_reg & 7 )
+{
+case 2: /* CALL (near, indirect) */
+case 6: /* PUSH r/m */
+return true;
+}
+break;
+
 case X86EMUL_OPC(0x0f, 0x01):
 /* Cover CLZERO. */
 return (state->modrm_rm & 7) == 4 && (state->modrm_reg & 7) == 7;
@@ -11501,10 +11563,20 @@ x86_insn_is_mem_access(const struct x86_
 return false;
 }
 
+/*
+ * This function means to return 'true' for all supported insns with explicit
+ * writes to memory.  This means also insns which don't have an explicit
+ * memory operand (like PUSH), but it does not mean e.g. segment selector
+ * loads, where the (possible) descriptor table write is considered an
+ * implicit access.
+ */
 bool
 x86_insn_is_mem_write(const struct x86_emulate_state *state,
   const struct x86_emulate_ctxt *ctxt)
 {
+if ( mode_64bit() && state->not_64bit )
+return false;
+
 switch ( state->desc & DstMask )
 {
 case DstMem:
@@ -11516,19 +11588,48 @@