diff mbox

[v2,16/16] x86emul: don't assume a memory operand

Message ID 57EB99320200007800113213@prv-mh.provo.novell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Beulich Sept. 28, 2016, 8:19 a.m. UTC
Especially for x86_insn_operand_ea() to return dependable segment
information even when the caller didn't consider applicability, we
shouldn't have ea.type start out as OP_MEM. Make it OP_NONE instead,
and set it to OP_MEM when we actually encounter memory like operands.

This requires to eliminate the XSA-123 fix, which has been no longer
necessary since the elimination of the union in commit dd766684e7. That
in turn allows restricting the scope of override_seg to x86_decode().
At this occasion also make it have a proper type, instead of plain int.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
x86emul: don't assume a memory operand

Especially for x86_insn_operand_ea() to return dependable segment
information even when the caller didn't consider applicability, we
shouldn't have ea.type start out as OP_MEM. Make it OP_NONE instead,
and set it to OP_MEM when we actually encounter memory like operands.

This requires to eliminate the XSA-123 fix, which has been no longer
necessary since the elimination of the union in commit dd766684e7. That
in turn allows restricting the scope of override_seg to x86_decode().
At this occasion also make it have a proper type, instead of plain int.

Signed-off-by: Jan Beulich <jbeulich@suse.com>

--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1647,7 +1647,6 @@ struct x86_emulate_state {
     opcode_desc_t desc;
     union vex vex;
     union evex evex;
-    int override_seg;
 
     /*
      * Data operand effective address (usually computed from ModRM).
@@ -1683,7 +1682,6 @@ struct x86_emulate_state {
 #define lock_prefix (state->lock_prefix)
 #define vex (state->vex)
 #define evex (state->evex)
-#define override_seg (state->override_seg)
 #define ea (state->ea)
 
 static int
@@ -1712,6 +1710,7 @@ x86_decode_onebyte(
     case 0xa0: case 0xa1: /* mov mem.offs,{%al,%ax,%eax,%rax} */
     case 0xa2: case 0xa3: /* mov {%al,%ax,%eax,%rax},mem.offs */
         /* Source EA is not encoded via ModRM. */
+        ea.type = OP_MEM;
         ea.mem.off = insn_fetch_bytes(ad_bytes);
         break;
 
@@ -1802,11 +1801,11 @@ x86_decode(
 {
     uint8_t b, d, sib, sib_index, sib_base;
     unsigned int def_op_bytes, def_ad_bytes, opcode;
+    enum x86_segment override_seg = x86_seg_none;
     int rc = X86EMUL_OKAY;
 
     memset(state, 0, sizeof(*state));
-    override_seg = -1;
-    ea.type = OP_MEM;
+    ea.type = OP_NONE;
     ea.mem.seg = x86_seg_ds;
     ea.reg = PTR_POISON;
     state->regs = ctxt->regs;
@@ -2102,6 +2101,7 @@ x86_decode(
         else if ( ad_bytes == 2 )
         {
             /* 16-bit ModR/M decode. */
+            ea.type = OP_MEM;
             switch ( modrm_rm )
             {
             case 0:
@@ -2152,6 +2152,7 @@ x86_decode(
         else
         {
             /* 32/64-bit ModR/M decode. */
+            ea.type = OP_MEM;
             if ( modrm_rm == 4 )
             {
                 sib = insn_fetch_type(uint8_t);
@@ -2216,7 +2217,7 @@ x86_decode(
         }
     }
 
-    if ( override_seg != -1 && ea.type == OP_MEM )
+    if ( override_seg != x86_seg_none )
         ea.mem.seg = override_seg;
 
     /* Fetch the immediate operand, if present. */
@@ -4253,13 +4254,11 @@ x86_emulate(
             generate_exception_if(limit < sizeof(long) ||
                                   (limit & (limit - 1)), EXC_UD, -1);
             base &= ~(limit - 1);
-            if ( override_seg == -1 )
-                override_seg = x86_seg_ds;
             if ( ops->rep_stos )
             {
                 unsigned long nr_reps = limit / sizeof(zero);
 
-                rc = ops->rep_stos(&zero, override_seg, base, sizeof(zero),
+                rc = ops->rep_stos(&zero, ea.mem.seg, base, sizeof(zero),
                                    &nr_reps, ctxt);
                 if ( rc == X86EMUL_OKAY )
                 {
@@ -4271,7 +4270,7 @@ x86_emulate(
             }
             while ( limit )
             {
-                rc = ops->write(override_seg, base, &zero, sizeof(zero), ctxt);
+                rc = ops->write(ea.mem.seg, base, &zero, sizeof(zero), ctxt);
                 if ( rc != X86EMUL_OKAY )
                     goto done;
                 base += sizeof(zero);
@@ -5257,7 +5256,6 @@ x86_emulate(
 #undef rex_prefix
 #undef lock_prefix
 #undef vex
-#undef override_seg
 #undef ea
 
 #ifdef __XEN__

Comments

Andrew Cooper Sept. 29, 2016, 9:12 p.m. UTC | #1
On 28/09/16 09:19, Jan Beulich wrote:
> Especially for x86_insn_operand_ea() to return dependable segment
> information even when the caller didn't consider applicability, we
> shouldn't have ea.type start out as OP_MEM. Make it OP_NONE instead,
> and set it to OP_MEM when we actually encounter memory like operands.
>
> This requires to eliminate the XSA-123 fix, which has been no longer
> necessary since the elimination of the union in commit dd766684e7. That
> in turn allows restricting the scope of override_seg to x86_decode().
> At this occasion also make it have a proper type, instead of plain int.
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
>
> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
> @@ -1647,7 +1647,6 @@ struct x86_emulate_state {
>      opcode_desc_t desc;
>      union vex vex;
>      union evex evex;
> -    int override_seg;
>  
>      /*
>       * Data operand effective address (usually computed from ModRM).
> @@ -1683,7 +1682,6 @@ struct x86_emulate_state {
>  #define lock_prefix (state->lock_prefix)
>  #define vex (state->vex)
>  #define evex (state->evex)
> -#define override_seg (state->override_seg)
>  #define ea (state->ea)
>  
>  static int
> @@ -1712,6 +1710,7 @@ x86_decode_onebyte(
>      case 0xa0: case 0xa1: /* mov mem.offs,{%al,%ax,%eax,%rax} */
>      case 0xa2: case 0xa3: /* mov {%al,%ax,%eax,%rax},mem.offs */
>          /* Source EA is not encoded via ModRM. */
> +        ea.type = OP_MEM;
>          ea.mem.off = insn_fetch_bytes(ad_bytes);
>          break;
>  
> @@ -1802,11 +1801,11 @@ x86_decode(
>  {
>      uint8_t b, d, sib, sib_index, sib_base;
>      unsigned int def_op_bytes, def_ad_bytes, opcode;
> +    enum x86_segment override_seg = x86_seg_none;
>      int rc = X86EMUL_OKAY;
>  
>      memset(state, 0, sizeof(*state));
> -    override_seg = -1;
> -    ea.type = OP_MEM;
> +    ea.type = OP_NONE;
>      ea.mem.seg = x86_seg_ds;
>      ea.reg = PTR_POISON;
>      state->regs = ctxt->regs;
> @@ -2102,6 +2101,7 @@ x86_decode(
>          else if ( ad_bytes == 2 )
>          {
>              /* 16-bit ModR/M decode. */
> +            ea.type = OP_MEM;
>              switch ( modrm_rm )
>              {
>              case 0:
> @@ -2152,6 +2152,7 @@ x86_decode(
>          else
>          {
>              /* 32/64-bit ModR/M decode. */
> +            ea.type = OP_MEM;
>              if ( modrm_rm == 4 )
>              {
>                  sib = insn_fetch_type(uint8_t);
> @@ -2216,7 +2217,7 @@ x86_decode(
>          }
>      }
>  
> -    if ( override_seg != -1 && ea.type == OP_MEM )
> +    if ( override_seg != x86_seg_none )

I don't see why the "ea.type == OP_MEM" should be dropped at this
point.  We have already set ea.type appropriately for memory
instructions by this point, and it does open up the case where
instructions which would have triggered XSA-123 get incorrect
information reported if queried with x86_insn_operand_ea()

~Andrew

>          ea.mem.seg = override_seg;
>  
>      /* Fetch the immediate operand, if present. */
> @@ -4253,13 +4254,11 @@ x86_emulate(
>              generate_exception_if(limit < sizeof(long) ||
>                                    (limit & (limit - 1)), EXC_UD, -1);
>              base &= ~(limit - 1);
> -            if ( override_seg == -1 )
> -                override_seg = x86_seg_ds;
>              if ( ops->rep_stos )
>              {
>                  unsigned long nr_reps = limit / sizeof(zero);
>  
> -                rc = ops->rep_stos(&zero, override_seg, base, sizeof(zero),
> +                rc = ops->rep_stos(&zero, ea.mem.seg, base, sizeof(zero),
>                                     &nr_reps, ctxt);
>                  if ( rc == X86EMUL_OKAY )
>                  {
> @@ -4271,7 +4270,7 @@ x86_emulate(
>              }
>              while ( limit )
>              {
> -                rc = ops->write(override_seg, base, &zero, sizeof(zero), ctxt);
> +                rc = ops->write(ea.mem.seg, base, &zero, sizeof(zero), ctxt);
>                  if ( rc != X86EMUL_OKAY )
>                      goto done;
>                  base += sizeof(zero);
> @@ -5257,7 +5256,6 @@ x86_emulate(
>  #undef rex_prefix
>  #undef lock_prefix
>  #undef vex
> -#undef override_seg
>  #undef ea
>  
>  #ifdef __XEN__
>
>
>
Jan Beulich Sept. 30, 2016, 8:25 a.m. UTC | #2
>>> On 29.09.16 at 23:12, <andrew.cooper3@citrix.com> wrote:
> On 28/09/16 09:19, Jan Beulich wrote:
>> @@ -2216,7 +2217,7 @@ x86_decode(
>>          }
>>      }
>>  
>> -    if ( override_seg != -1 && ea.type == OP_MEM )
>> +    if ( override_seg != x86_seg_none )
> 
> I don't see why the "ea.type == OP_MEM" should be dropped at this
> point.  We have already set ea.type appropriately for memory
> instructions by this point, and it does open up the case where
> instructions which would have triggered XSA-123 get incorrect
> information reported if queried with x86_insn_operand_ea()

The need to remove this actually became apparent with the
testing I did for the priv-op handling, namely for OUTS with a
segment override: When we had (before the patch here)
ea.type start out as OP_MEM, the conditional above was true
_unless_ ea.type got changed later on. With it now (properly
imo) starting out as OP_NONE, instructions not changing it to
OP_MEM (like all the string ones) would not get the segment
override applied anymore.

And no, x86_insn_operand_ea() returns x86_seg_none when
ea.type is anything other than OP_MEM.

Jan
diff mbox

Patch

--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1647,7 +1647,6 @@  struct x86_emulate_state {
     opcode_desc_t desc;
     union vex vex;
     union evex evex;
-    int override_seg;
 
     /*
      * Data operand effective address (usually computed from ModRM).
@@ -1683,7 +1682,6 @@  struct x86_emulate_state {
 #define lock_prefix (state->lock_prefix)
 #define vex (state->vex)
 #define evex (state->evex)
-#define override_seg (state->override_seg)
 #define ea (state->ea)
 
 static int
@@ -1712,6 +1710,7 @@  x86_decode_onebyte(
     case 0xa0: case 0xa1: /* mov mem.offs,{%al,%ax,%eax,%rax} */
     case 0xa2: case 0xa3: /* mov {%al,%ax,%eax,%rax},mem.offs */
         /* Source EA is not encoded via ModRM. */
+        ea.type = OP_MEM;
         ea.mem.off = insn_fetch_bytes(ad_bytes);
         break;
 
@@ -1802,11 +1801,11 @@  x86_decode(
 {
     uint8_t b, d, sib, sib_index, sib_base;
     unsigned int def_op_bytes, def_ad_bytes, opcode;
+    enum x86_segment override_seg = x86_seg_none;
     int rc = X86EMUL_OKAY;
 
     memset(state, 0, sizeof(*state));
-    override_seg = -1;
-    ea.type = OP_MEM;
+    ea.type = OP_NONE;
     ea.mem.seg = x86_seg_ds;
     ea.reg = PTR_POISON;
     state->regs = ctxt->regs;
@@ -2102,6 +2101,7 @@  x86_decode(
         else if ( ad_bytes == 2 )
         {
             /* 16-bit ModR/M decode. */
+            ea.type = OP_MEM;
             switch ( modrm_rm )
             {
             case 0:
@@ -2152,6 +2152,7 @@  x86_decode(
         else
         {
             /* 32/64-bit ModR/M decode. */
+            ea.type = OP_MEM;
             if ( modrm_rm == 4 )
             {
                 sib = insn_fetch_type(uint8_t);
@@ -2216,7 +2217,7 @@  x86_decode(
         }
     }
 
-    if ( override_seg != -1 && ea.type == OP_MEM )
+    if ( override_seg != x86_seg_none )
         ea.mem.seg = override_seg;
 
     /* Fetch the immediate operand, if present. */
@@ -4253,13 +4254,11 @@  x86_emulate(
             generate_exception_if(limit < sizeof(long) ||
                                   (limit & (limit - 1)), EXC_UD, -1);
             base &= ~(limit - 1);
-            if ( override_seg == -1 )
-                override_seg = x86_seg_ds;
             if ( ops->rep_stos )
             {
                 unsigned long nr_reps = limit / sizeof(zero);
 
-                rc = ops->rep_stos(&zero, override_seg, base, sizeof(zero),
+                rc = ops->rep_stos(&zero, ea.mem.seg, base, sizeof(zero),
                                    &nr_reps, ctxt);
                 if ( rc == X86EMUL_OKAY )
                 {
@@ -4271,7 +4270,7 @@  x86_emulate(
             }
             while ( limit )
             {
-                rc = ops->write(override_seg, base, &zero, sizeof(zero), ctxt);
+                rc = ops->write(ea.mem.seg, base, &zero, sizeof(zero), ctxt);
                 if ( rc != X86EMUL_OKAY )
                     goto done;
                 base += sizeof(zero);
@@ -5257,7 +5256,6 @@  x86_emulate(
 #undef rex_prefix
 #undef lock_prefix
 #undef vex
-#undef override_seg
 #undef ea
 
 #ifdef __XEN__