i386, x86_64: F6/F7 (group 3) opcode with ModRM.reg == 1 is not recognized as TEST instruction
## Host environment - Operating system: Kali Linux 2026.2 - OS/kernel version: Linux kali 6.19.14+kali-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.19.14-1+kali1 (2026-05-05) x86_64 GNU/Linux - Architecture: x86_64 - QEMU flavor: qemu-i386, qemu-system-i386, qemu-x86_64, qemu-system-x86_64 - QEMU version: 11.0.50 (v11.0.0-2279-gb833716681) - QEMU command line: ``` ./qemu-x86_64 ./poc ``` ## Emulated/Virtualized environment - Operating system: Does not matter - OS/kernel version: Does not matter - Architecture: i386, x86_64 ## Description of problem According to table A-6 in Volume 3 of AMD64 Architecture Programmer's Manual, a group 3 opcode is decoded as TEST instruction when reg field of ModRM byte is either 0 or 1. Intel 64 and IA-32 Architectures Software Developer's Manual leaves the cell 1 of opcode extensions group 3 blank in the opcode table (table A-6, Volume 2D). However, the instruction encoded like that actually behaves like the TEST instruction on Intel CPUs I tested, both in 32-bit compatibility and 64-bit modes: - Intel Core i3-6100 (family 6, model 94, stepping 3, microcode version 0xf0) - Intel Core i7-3770 (family 6, model 58, stepping 9, microcode version 0x21) Currently, QEMU decodes a group 3 instruction as TEST only if reg field of ModRM byte is 0. When reg field of ModRM byte is 1, QEMU raises a #UD exception. This behavior does not match the behavior of the actual Intel and AMD hardware. ## Steps to reproduce 1. Compile the following binary with nasm: ```nasm bits 64 global _start _start: xor eax, eax db 0xf6, 0xc8, 0xff jnz .end not eax db 0xf7, 0xc8, 0x01, 0x02, 0x03, 0x04 jz .end int3 ud2 .end: hlt ``` ```bash $ nasm -f elf64 poc.asm # or elf32, if building for qemu-i386 $ ld.lld -o poc poc.o ``` 2. Optional: disassemble the binary ```bash $ objdump -d -Mintel ./poc ./poc: file format elf64-x86-64 Disassembly of section .text: 0000000000201120 <_start>: 201120: 31 c0 xor eax,eax 201122: f6 c8 ff test al,0xff 201125: 75 0d jne 201134 <_start.end> 201127: f7 d0 not eax 201129: f7 c8 01 02 03 04 test eax,0x4030201 20112f: 74 03 je 201134 <_start.end> 201131: cc int3 201132: 0f 0b ud2 0000000000201134 <_start.end>: 201134: f4 hlt $ ``` 3. Run the binary on the real hardware: ```bash $ ./poc Trace/breakpoint trap $ ``` 4. Run the binary in QEMU: ```bash $ ./qemu-x86_64 --version qemu-x86_64 version 11.0.50 (v11.0.0-2279-gb833716681) Copyright (c) 2003-2026 Fabrice Bellard and the QEMU Project developers $ ./qemu-x86_64 -d int,in_asm,op,cpu --one-insn-per-tb ./poc # -d stuff and --one-insn-per-tb is to show what happens inside QEMU ---------------- IN: 0x00201120: 31 c0 xorl %eax, %eax OP: ld_i32 loc4,env,$0xffffffffffffffec brcond_i32 loc4,$0x0,lt,$L0 st8_i32 $0x1,env,$0xfffffffffffffff0 ---- 0000000000201120 000000000000003c 0000000000000000 mov_i64 loc0,rax mov_i64 loc1,rax mov_i64 loc0,$0x0 extract_i64 rax,loc0,$0x0,$0x20 mov_i64 cc_src,$0x44 discard cc_dst discard cc_src2 discard cc_op mov_i32 cc_op,$0x0 mov_i64 rip,$0x201122 exit_tb $0x0 set_label $L0 exit_tb $0x7b14e4000043 RAX=0000000000000000 RBX=0000000000000000 RCX=0000000000000000 RDX=0000000000000000 RSI=0000000000000000 RDI=0000000000000000 RBP=0000000000000000 RSP=00007b14e3ffed30 R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000 R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000 RIP=0000000000201120 RFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0 ES =0000 0000000000000000 00000000 00000000 CS =0033 0000000000000000 ffffffff 00effb00 DPL=3 CS64 [-RA] SS =002b 0000000000000000 ffffffff 00cff300 DPL=3 DS [-WA] DS =0000 0000000000000000 00000000 00000000 FS =0000 0000000000000000 00000000 00000000 GS =0000 0000000000000000 00000000 00000000 LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy GDT= 00007f14f1c92000 0000007f IDT= 00007f14f1c93000 000001ff CR0=80010001 CR2=0000000000000000 CR3=0000000000000000 CR4=00050220 DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 DR6=00000000ffff0ff0 DR7=0000000000000400 CCS=0000000000000000 CCD=0000000000000000 CCO=EFLAGS EFER=0000000000000500 ---------------- IN: 0x00201122: f6 .byte 0xf6 0x00201123: c8 .byte 0xc8 OP: ld_i32 loc4,env,$0xffffffffffffffec brcond_i32 loc4,$0x0,lt,$L0 st8_i32 $0x1,env,$0xfffffffffffffff0 ---- 0000000000201122 000000000000003c 0000000000000000 mov_i64 rip,$0x201122 call raise_exception,$0xa,$0,env,$0x6 set_label $L0 exit_tb $0x7b14e4000183 RAX=0000000000000000 RBX=0000000000000000 RCX=0000000000000000 RDX=0000000000000000 RSI=0000000000000000 RDI=0000000000000000 RBP=0000000000000000 RSP=00007b14e3ffed30 R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000 R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000 RIP=0000000000201122 RFL=00000246 [---Z-P-] CPL=3 II=0 A20=1 SMM=0 HLT=0 ES =0000 0000000000000000 00000000 00000000 CS =0033 0000000000000000 ffffffff 00effb00 DPL=3 CS64 [-RA] SS =002b 0000000000000000 ffffffff 00cff300 DPL=3 DS [-WA] DS =0000 0000000000000000 00000000 00000000 FS =0000 0000000000000000 00000000 00000000 GS =0000 0000000000000000 00000000 00000000 LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy GDT= 00007f14f1c92000 0000007f IDT= 00007f14f1c93000 000001ff CR0=80010001 CR2=0000000000000000 CR3=0000000000000000 CR4=00050220 DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 DR6=00000000ffff0ff0 DR7=0000000000000400 CCS=0000000000000044 CCD=0000000000000000 CCO=EFLAGS EFER=0000000000000500 check_exception old: 0xffffffff new 0x6 qemu: uncaught target signal 4 (Illegal instruction) - core dumped Illegal instruction $ ```
issue