From 3a86916c1ce06c5d2c0f5d40da3c2e09eee2c531 Mon Sep 17 00:00:00 2001 From: Polykarpos Vergos Date: Tue, 11 Aug 2026 11:07:26 +0200 Subject: [PATCH] x86: use effective address size for the loop counter register The implicit counter register of loop/loope/loopne (and its regs_read/ regs_write detail) was chosen from the CPU mode alone (CX/ECX/RCX for 16/32/64-bit), ignoring a 0x67 address-size prefix. In 64-bit mode `67 e2 c2` (loop) therefore reported rcx instead of ecx. Select the counter register from the effective address size (insn->detail->x86.addr_size), which the 0x67 prefix already accounts for, while the instruction-pointer register keeps following the mode. Fixes #2823 --- arch/X86/X86Mapping.c | 18 ++++++++++++------ tests/details/x86.yaml | 12 ++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/arch/X86/X86Mapping.c b/arch/X86/X86Mapping.c index ea3dd72848..e26f4dd83e 100644 --- a/arch/X86/X86Mapping.c +++ b/arch/X86/X86Mapping.c @@ -957,6 +957,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_LOOP: case X86_INS_LOOPE: case X86_INS_LOOPNE: + // The instruction pointer register follows the mode. switch (h->mode) { default: break; @@ -969,24 +970,30 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_EIP, X86_REG_IP); + break; + case CS_MODE_64: arr_replace( insn->detail->regs_read, insn->detail->regs_read_count, - X86_REG_ECX, X86_REG_CX); + X86_REG_EIP, X86_REG_RIP); arr_replace( insn->detail->regs_write, insn->detail->regs_write_count, - X86_REG_ECX, X86_REG_CX); + X86_REG_EIP, X86_REG_RIP); break; - case CS_MODE_64: + } + // The loop counter register follows the effective address + // size, which a 0x67 address-size prefix can override. + if (insn->detail->x86.addr_size == 2) { arr_replace( insn->detail->regs_read, insn->detail->regs_read_count, - X86_REG_EIP, X86_REG_RIP); + X86_REG_ECX, X86_REG_CX); arr_replace( insn->detail->regs_write, insn->detail->regs_write_count, - X86_REG_EIP, X86_REG_RIP); + X86_REG_ECX, X86_REG_CX); + } else if (insn->detail->x86.addr_size == 8) { arr_replace( insn->detail->regs_read, insn->detail->regs_read_count, @@ -995,7 +1002,6 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_ECX, X86_REG_RCX); - break; } } diff --git a/tests/details/x86.yaml b/tests/details/x86.yaml index c9e44ef3c5..9d9879690a 100644 --- a/tests/details/x86.yaml +++ b/tests/details/x86.yaml @@ -6053,8 +6053,8 @@ test_cases: x86: prefix: [ X86_PREFIX_0, X86_PREFIX_0, X86_PREFIX_0, X86_PREFIX_ADDRSIZE ] opcode: [ 0xe2, 0x00, 0x00, 0x00 ] - regs_read: [ rip, rcx ] - regs_write: [ rip, rcx ] # BUG: should be ecx + regs_read: [ rip, ecx ] + regs_write: [ rip, ecx ] - asm_text: "loope 0x34" details: @@ -6081,8 +6081,8 @@ test_cases: prefix: [ X86_PREFIX_0, X86_PREFIX_0, X86_PREFIX_0, X86_PREFIX_ADDRSIZE ] opcode: [ 0xe1, 0x00, 0x00, 0x00 ] eflags: [ X86_EFLAGS_TEST_ZF ] - regs_read: [ rip, rcx, rflags ] - regs_write: [ rip, rcx ] # BUG: should be ecx + regs_read: [ rip, ecx, rflags ] + regs_write: [ rip, ecx ] - asm_text: "loopne 0x34" details: @@ -6109,8 +6109,8 @@ test_cases: prefix: [ X86_PREFIX_0, X86_PREFIX_0, X86_PREFIX_0, X86_PREFIX_ADDRSIZE ] opcode: [ 0xe0, 0x00, 0x00, 0x00 ] eflags: [ X86_EFLAGS_TEST_ZF ] - regs_read: [ rip, rcx, rflags ] - regs_write: [ rip, rcx ] # BUG: should be ecx + regs_read: [ rip, ecx, rflags ] + regs_write: [ rip, ecx ] - input: