Hi Paolo,

In its current form, this patch makes VROUNDSS (and possibly other
instructions) use 256-bit operand size when VEX.L=1

This happens because the operand sizes are set in `decode_op_size()`,
which is called in `decode_insn()`

`decode_op_size()` reaches the code below (irrelevant parts of pwndbg
UI are omitted):
```
pwndbg> set $decode=&decode
pwndbg> watch $decode->op[0].ot
Hardware watchpoint 3: $decode->op[0].ot
pwndbg> c
Continuing.

Thread 1 hit Hardware watchpoint 3: $decode->op[0].ot

Old value = MO_8
New value = MO_256
decode_op_size (s=0x7b1a3af02020, e=0x7b1a3ae08e20, size=X86_SIZE_x,
ot=0x7b1a3ae08e44) at ../target/i386/tcg/decode-new.c.inc:2289
2289            return true;
=> 0x000055e7af160d5a <decode_op_size+4186>:    c6 45 ff 01
 mov    BYTE PTR [rbp-0x1],0x1
   0x000055e7af160d5e <decode_op_size+4190>:    e9 91 04 00 00
 jmp    0x55e7af1611f4 <decode_op_size+5364>
...
─[ SOURCE (CODE) ]─
In file: .../QEMU/target/i386/tcg/decode-new.c.inc:2289
   2283             return true;
   2284         }
   2285         /* fall through */
   2286     case X86_SIZE_ps: /* SSE/AVX packed single precision */
   2287     case X86_SIZE_pd: /* SSE/AVX packed double precision */
   2288         *ot = s->vex_l ? MO_256 : MO_128;
 ► 2289         return true;
   2290
   2291     case X86_SIZE_xh: /* SSE/AVX packed half register */
   2292         *ot = s->vex_l ? MO_128 : MO_64;
   2293         return true;
   2294
   2295     case X86_SIZE_d64:  /* Default to 64-bit in 64-bit mode */
   2296         *ot = CODE64(s) && s->dflag == MO_32 ? MO_64 : s->dflag;
...
─[ BACKTRACE ]─
 ► 0 0x55e7af160d5a decode_op_size+4186
   1 0x55e7af0b6598 decode_insn+1288
   2 0x55e7af0b1edd disas_insn+14861
   3 0x55e7af0ac8a9 i386_tr_translate_insn+1385
   4 0x55e7aed5a57d translator_loop+5261
   5 0x55e7af0a99d4 x86_translate_code+500
   6 0x55e7aed56cdc setjmp_gen_code+1052
   7 0x55e7aed53f45 tb_gen_code+4933
...
pwndbg> p s->vex_l
$5 = 0x1
pwndbg>
```

Then `validate_vex()` is called, which sets the `s->vex_l` to 0. But
by the time it is called, it is already too late:
```
pwndbg> set $s=s
pwndbg> watch $s->vex_l
Hardware watchpoint 4: $s->vex_l
pwndbg> c
Continuing.

Thread 1 hit Hardware watchpoint 4: $s->vex_l

Old value = 0x1
New value = 0x0
validate_vex (s=0x7b1a3af02020, decode=0x7b1a3ae08e20) at
../target/i386/tcg/decode-new.c.inc:2889
2889            break;
=> 0x000055e7af0ba0ad <validate_vex+1645>:      e9 69 0a 00 00
 jmp    0x55e7af0bab1b <validate_vex+4315>
...
─[ SOURCE (CODE) ]─
In file: /home/user/Projects/QEMU/target/i386/tcg/decode-new.c.inc:2889
   2883     switch (e->vex_special) {
   2884     case X86_VEX_None:
   2885         break;
   2886
   2887     case X86_VEX_LIG:
   2888         s->vex_l = 0;
 ► 2889         break;
   2890
   2891     case X86_VEX_REPScalar:
   2892         /*
   2893          * Instructions which differ between 00/66 and F2/F3 in the
   2894          * exception classification and the size of the memory operand.
   2895          */
   2896         assert(e->vex_class == 1 || e->vex_class == 2 ||
e->vex_class == 4);
...
─[ BACKTRACE ]─
 ► 0 0x55e7af0ba0ad validate_vex+1645
   1 0x55e7af0b2948 disas_insn+17528
   2 0x55e7af0ac8a9 i386_tr_translate_insn+1385
   3 0x55e7aed5a57d translator_loop+5261
   4 0x55e7af0a99d4 x86_translate_code+500
   5 0x55e7aed56cdc setjmp_gen_code+1052
   6 0x55e7aed53f45 tb_gen_code+4933
   7 0x55e7aed294da cpu_exec_loop+1738
...
pwndbg> p *decode
$6 = {
  e = {
    {
      gen = 0x55e7af15a3b0 <gen_VROUNDSS>,
      decode = 0x55e7af15a3b0 <gen_VROUNDSS>
    },
    op0 = X86_TYPE_V,
    s0 = X86_SIZE_x,
    op1 = X86_TYPE_H,
    s1 = X86_SIZE_x,
    op2 = X86_TYPE_W,
    s2 = X86_SIZE_ss,
    op3 = X86_TYPE_I,
    s3 = X86_SIZE_b,
    special = X86_SPECIAL_None,
    ext0 = X86_EXT_None,
    ext1 = X86_EXT_None,
    ext2 = X86_EXT_None,
    cpuid = X86_FEAT_SSE41,
    vex_class = 0x3,
    vex_special = X86_VEX_LIG,
    valid_prefix = 0x100,
    check = 0x0,
    intercept = 0x0,
    has_intercept = 0x0,
    is_decode = 0x0
  },
  op = {{
      n = 0x0,
      ot = MO_256,
      unit = X86_OP_SKIP,
      ext = X86_EXT_None,
      has_ea = 0x0,
      offset = 0x0,
      {
        imm = 0x0,
        v_ptr = 0x0
      }
    }, {
      n = 0x0,
      ot = MO_256,
      unit = X86_OP_SKIP,
      ext = X86_EXT_None,
      has_ea = 0x0,
      offset = 0x0,
      {
        imm = 0x0,
        v_ptr = 0x0
      }
    }, {
      n = 0x0,
      ot = MO_32,
      unit = X86_OP_SKIP,
      ext = X86_EXT_None,
      has_ea = 0x0,
      offset = 0x0,
      {
        imm = 0x0,
        v_ptr = 0x0
      }
    }},
  immediate = 0x0,
  mem = {
    def_seg = 0x0,
    base = 0x0,
    index = 0x0,
    scale = 0x0,
    disp = 0x0
  },
  cc_dst = 0x0,
  cc_src = 0x0,
  cc_src2 = 0x0,
  cc_op_dynamic = 0x0,
  cc_op = 0xff,
  b = 0xa
}
pwndbg> p s->pc_save
$7 = 0x201194
pwndbg> x/i s->pc_save
   0x201194:    vroundss xmm0,xmm0,xmm1,0x0
pwndbg> x/6xb s->pc_save
0x201194:       0xc4    0xe3    0xfd    0x0a    0xc1    0x00
pwndbg>
```

Since the operand size is already set to `MO_256`, that size will
eventually be used by the TCG frontend. This is seen in the TCG ops
dump (annotated a little for better readability):
```
$ .../QEMU/build_asan_ubsan_dbg/qemu-x86_64 -d in_asm,op
--one-insn-per-tb ./vroundss_test
...
# VEX.L = 0
----------------
IN:
0x0020117a:  c4 e3 f9 0a c1 00        vroundss $0, %xmm1, %xmm0, %xmm0

OP:
 ld_i32 loc3,env,$0xffffffffffffffec
 brcond_i32 loc3,$0x0,lt,$L0

 ---- 000000000020117a 000000000000003c 0000000000000000
 add_i64 loc5,env,$0x3c0
 add_i64 loc7,env,$0x3c0
 add_i64 loc8,env,$0x400
 call roundss_xmm,$0x0,$0,env,loc5,loc7,loc8,$0x0
# v128 ops present in VEX.L = 0 case!
mov_vec v128,e8,tmp10,v128$0x0
st_vec v128,e8,tmp10,env,$0x3d0
 mov_i64 rip,$0x201180
 exit_tb $0x0
 set_label $L0
 exit_tb $0x7b1a30a003c3
...
# VEX.L = 1
----------------
IN:
0x00201194:  c4 e3 fd 0a c1 00        vroundss $0, %xmm1, %xmm0, %xmm0

OP:
 ld_i32 loc3,env,$0xffffffffffffffec
 brcond_i32 loc3,$0x0,lt,$L0

 ---- 0000000000201194 000000000000003c 0000000000000000
 add_i64 loc5,env,$0x3c0
 add_i64 loc7,env,$0x3c0
 add_i64 loc8,env,$0x400
 call roundss_xmm,$0x0,$0,env,loc5,loc7,loc8,$0x0
# No v128 ops in VEX.L = 1 case!
 mov_i64 rip,$0x20119a
 exit_tb $0x0
 set_label $L0
 exit_tb $0x7b1a30a00883
...
----------------
IN:
0x002011bf:  f4                       hlt

OP:
 ld_i32 loc3,env,$0xffffffffffffffec
 brcond_i32 loc3,$0x0,lt,$L0

 ---- 00000000002011bf 000000000000003c 0000000000000000
 mov_i64 rip,$0x2011bf
 call raise_exception,$0xa,$0,env,$0xd
 set_label $L0
 exit_tb $0x7b1a30a01003

qemu: uncaught target signal 11 (Segmentation fault) - core dumped
```

Test binary source below:
```nasm
bits 64
default rel

global _start
_start:
mov dword [rounding_mode], 0x40200000

vmovups ymm0, [garbage]
vmovups ymm1, [rounding_mode]
; VEX.L = 0
db 0xc4, 0xe3, 0xf9, 0x0a, 0xc1, 0x00
vmovups ymm3, ymm0

vmovups ymm0, [garbage]
vmovups ymm1, [rounding_mode]
; VEX.L = 1
db 0xc4, 0xe3, 0xfd, 0x0a, 0xc1, 0x00

vmovups [out_vex_l_0], ymm3
vmovups [out_vex_l_1], ymm0

vpxor ymm2, ymm0, ymm3
vptest ymm2, ymm2
jnz .bug_detected

mov rax, 60
xor rdi, rdi
syscall

.bug_detected:
hlt

section .data
align 32
garbage: times 32 db 0xAA
rounding_mode: times 32 db 0xBB

out_vex_l_0: times 32 db 0xff
out_vex_l_1: times 32 db 0xff
```
```
$ nasm -f elf64 vroundss_test.asm
$ ld.lld -o vroundss_test vroundss_test.o
```

And here are the results in the `out_vex_l_0` and `out_vex_l_1`:
```
$ .../QEMU/build_asan_ubsan_dbg/qemu-x86_64 -d in_asm,op
--one-insn-per-tb -g 1234 ./vroundss_test
```
```
pwndbg> x/i $pc
=> 0x2011b3 <_start+83>:        jne    0x2011bf <_start.bug_detected>
pwndbg> info r eflags
eflags         0x203               [ IOPL=0 IF CF ]
pwndbg> x/32xb &out_vex_l_0
0x202200:       0x00    0x00    0x00    0x40    0xaa    0xaa    0xaa    0xaa
0x202208:       0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa
0x202210:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x202218:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
pwndbg> x/32xb &out_vex_l_1
0x202220:       0x00    0x00    0x00    0x40    0xaa    0xaa    0xaa    0xaa
0x202228:       0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa
0x202230:       0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa
0x202238:       0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa    0xaa
pwndbg>
```

(Sorry for the late response, got burned out a little recently. Still,
couldn't let this issue go into the upstream unnoticed)

Reply via email to