From de6a6facdc3abf2e6bb265ddebf9a2c77b6b8fd0 Mon Sep 17 00:00:00 2001 From: Amaan Mujawar Date: Thu, 2 Jul 2026 15:20:51 +0100 Subject: [PATCH 1/3] AArch64: fix LDR literal detail operand truncation --- arch/AArch64/AArch64Mapping.c | 24 +++++++++++++++++++++--- tests/issues/issues.yaml | 23 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/arch/AArch64/AArch64Mapping.c b/arch/AArch64/AArch64Mapping.c index 23da9f026a..dffb071e25 100644 --- a/arch/AArch64/AArch64Mapping.c +++ b/arch/AArch64/AArch64Mapping.c @@ -2493,6 +2493,21 @@ void AArch64_set_detail_op_reg(MCInst *MI, unsigned OpNum, aarch64_reg Reg) AArch64_inc_op_count(MI); } +/// Check if the previous operand is a memory operand +/// with a base register set, but no index or displacement. +/// This indicates the following immediate is part of the +/// same memory operand. +static bool prev_is_membase(MCInst *MI) +{ + return AArch64_get_detail(MI)->op_count > 0 && + AArch64_get_detail_op(MI, -1)->type == AARCH64_OP_MEM && + AArch64_get_detail_op(MI, -1)->mem.base != + AARCH64_REG_INVALID && + AArch64_get_detail_op(MI, -1)->mem.index == + AARCH64_REG_INVALID && + AArch64_get_detail_op(MI, -1)->mem.disp == 0; +} + /// Check if the previous operand is a memory operand /// with only the base register set AND if this base register /// is write-back. @@ -2530,13 +2545,16 @@ void AArch64_set_detail_op_imm(MCInst *MI, unsigned OpNum, } return; } - if (map_get_op_type(MI, OpNum) & CS_OP_MEM || prev_is_membase_wb(MI)) { + cs_op_type OpType = map_get_op_type(MI, OpNum); + + if ((OpType & CS_OP_MEM && prev_is_membase(MI)) || + prev_is_membase_wb(MI)) { AArch64_set_detail_op_mem(MI, OpNum, Imm); return; } - CS_ASSERT_RET(!(map_get_op_type(MI, OpNum) & CS_OP_MEM)); - CS_ASSERT_RET((map_get_op_type(MI, OpNum) & ~CS_OP_BOUND) == CS_OP_IMM); + CS_ASSERT_RET(!(OpType & CS_OP_MEM) || (OpType & CS_OP_IMM)); + CS_ASSERT_RET((OpType & ~(CS_OP_MEM | CS_OP_BOUND)) == CS_OP_IMM); CS_ASSERT_RET(ImmType == AARCH64_OP_IMM || ImmType == AARCH64_OP_CIMM); AArch64_get_detail_op(MI, 0)->type = ImmType; diff --git a/tests/issues/issues.yaml b/tests/issues/issues.yaml index 1ded37ff09..a82f866e6e 100644 --- a/tests/issues/issues.yaml +++ b/tests/issues/issues.yaml @@ -6699,3 +6699,26 @@ test_cases: - asm_text: "ptrue pn10.h" op_str: "pn10.h" + - + input: + name: "issue 2878 AArch64 LDR literal detail operand truncation" + bytes: [ 0xa8,0x79,0x29,0x58 ] + arch: "CS_ARCH_AARCH64" + options: [ CS_MODE_LITTLE_ENDIAN, CS_OPT_DETAIL ] + address: 0x100c894d4 + expected: + insns: + - + asm_text: "ldr x8, 0x100cdc408" + details: + aarch64: + operands: + - + type: AARCH64_OP_REG + reg: x8 + access: CS_AC_WRITE + - + type: AARCH64_OP_IMM + imm: 0x100cdc408 + access: CS_AC_READ + regs_write: [ x8 ] From 000e80baf60cb7f7164376f2373f50e5623867e3 Mon Sep 17 00:00:00 2001 From: Amaan Mujawar Date: Fri, 3 Jul 2026 12:06:00 +0100 Subject: [PATCH 2/3] AArch64: widen memory displacement --- arch/AArch64/AArch64Mapping.c | 24 +++--------------------- cstool/cstool_aarch64.c | 4 ++-- docs/cs_v6_release_guide.md | 1 + include/capstone/aarch64.h | 2 +- tests/issues/issues.yaml | 7 +++++-- 5 files changed, 12 insertions(+), 26 deletions(-) diff --git a/arch/AArch64/AArch64Mapping.c b/arch/AArch64/AArch64Mapping.c index dffb071e25..23da9f026a 100644 --- a/arch/AArch64/AArch64Mapping.c +++ b/arch/AArch64/AArch64Mapping.c @@ -2493,21 +2493,6 @@ void AArch64_set_detail_op_reg(MCInst *MI, unsigned OpNum, aarch64_reg Reg) AArch64_inc_op_count(MI); } -/// Check if the previous operand is a memory operand -/// with a base register set, but no index or displacement. -/// This indicates the following immediate is part of the -/// same memory operand. -static bool prev_is_membase(MCInst *MI) -{ - return AArch64_get_detail(MI)->op_count > 0 && - AArch64_get_detail_op(MI, -1)->type == AARCH64_OP_MEM && - AArch64_get_detail_op(MI, -1)->mem.base != - AARCH64_REG_INVALID && - AArch64_get_detail_op(MI, -1)->mem.index == - AARCH64_REG_INVALID && - AArch64_get_detail_op(MI, -1)->mem.disp == 0; -} - /// Check if the previous operand is a memory operand /// with only the base register set AND if this base register /// is write-back. @@ -2545,16 +2530,13 @@ void AArch64_set_detail_op_imm(MCInst *MI, unsigned OpNum, } return; } - cs_op_type OpType = map_get_op_type(MI, OpNum); - - if ((OpType & CS_OP_MEM && prev_is_membase(MI)) || - prev_is_membase_wb(MI)) { + if (map_get_op_type(MI, OpNum) & CS_OP_MEM || prev_is_membase_wb(MI)) { AArch64_set_detail_op_mem(MI, OpNum, Imm); return; } - CS_ASSERT_RET(!(OpType & CS_OP_MEM) || (OpType & CS_OP_IMM)); - CS_ASSERT_RET((OpType & ~(CS_OP_MEM | CS_OP_BOUND)) == CS_OP_IMM); + CS_ASSERT_RET(!(map_get_op_type(MI, OpNum) & CS_OP_MEM)); + CS_ASSERT_RET((map_get_op_type(MI, OpNum) & ~CS_OP_BOUND) == CS_OP_IMM); CS_ASSERT_RET(ImmType == AARCH64_OP_IMM || ImmType == AARCH64_OP_CIMM); AArch64_get_detail_op(MI, 0)->type = ImmType; diff --git a/cstool/cstool_aarch64.c b/cstool/cstool_aarch64.c index 12af65bd18..8d7d298065 100644 --- a/cstool/cstool_aarch64.c +++ b/cstool/cstool_aarch64.c @@ -63,8 +63,8 @@ void print_insn_detail_aarch64(csh handle, cs_insn *ins) i, cs_reg_name(handle, op->mem.index)); if (op->mem.disp != 0 || op->mem.base == AARCH64_REG_INVALID) - printf("\t\t\toperands[%u].mem.disp: 0x%x\n", i, - op->mem.disp); + printf("\t\t\toperands[%u].mem.disp: 0x%llx\n", i, + (unsigned long long)op->mem.disp); if (ins->detail->aarch64.post_index) printf("\t\t\tpost-indexed: true\n"); diff --git a/docs/cs_v6_release_guide.md b/docs/cs_v6_release_guide.md index f6b0e30f0d..6471595cea 100644 --- a/docs/cs_v6_release_guide.md +++ b/docs/cs_v6_release_guide.md @@ -440,6 +440,7 @@ Such an instruction is ill-defined in LLVM and should be fixed upstream. | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ARM64 -> AArch64 | ARM64 was everywhere renamed to AArch64 to match the LLVM naming. | See below. | | Post-index | Post-index memory access has the disponent now set int the `MEMORY` operand! No longer as separated `reg`/`imm` operand. | See post-index explanation for ARM. | +| `mem.disp` | `cs_aarch64_op.mem.disp` changed from `int32_t` to `int64_t`. | Prevent truncation of large AArch64 memory displacements. | | `SME` operands | `SME` operands contain more detail now and member names are closer to the ISA terminology. | New SVE2, SME extensions required more detail. | | System operands | System Operands are separated into different types now. | System operands follow a special encoding. Some byte sequences match two different operands. Hence, a more detailed concept was necessary. | | `writeback` | `writeback` member was moved to detail. | See ARM explanation. | diff --git a/include/capstone/aarch64.h b/include/capstone/aarch64.h index 8bbb19e270..aeb3923904 100644 --- a/include/capstone/aarch64.h +++ b/include/capstone/aarch64.h @@ -2786,7 +2786,7 @@ typedef enum aarch64_reg { typedef struct aarch64_op_mem { aarch64_reg base; ///< base register aarch64_reg index; ///< index register - int32_t disp; ///< displacement/offset value + int64_t disp; ///< displacement/offset value } aarch64_op_mem; typedef enum { diff --git a/tests/issues/issues.yaml b/tests/issues/issues.yaml index a82f866e6e..f3522a6719 100644 --- a/tests/issues/issues.yaml +++ b/tests/issues/issues.yaml @@ -6718,7 +6718,10 @@ test_cases: reg: x8 access: CS_AC_WRITE - - type: AARCH64_OP_IMM - imm: 0x100cdc408 + type: AARCH64_OP_MEM + mem: + base: invalid + index: invalid + disp: 0x100cdc408 access: CS_AC_READ regs_write: [ x8 ] From 8e3dbf3828ae51eb70c38f61d95f6da6733bbf10 Mon Sep 17 00:00:00 2001 From: Amaan Mujawar Date: Fri, 3 Jul 2026 20:31:08 +0100 Subject: [PATCH 3/3] AArch64: fix mem.disp comparison to int64_t --- suite/cstest/src/test_detail_aarch64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/suite/cstest/src/test_detail_aarch64.c b/suite/cstest/src/test_detail_aarch64.c index edd56f1a9d..aeec2a94d9 100644 --- a/suite/cstest/src/test_detail_aarch64.c +++ b/suite/cstest/src/test_detail_aarch64.c @@ -204,7 +204,7 @@ bool test_expected_aarch64(csh *handle, cs_aarch64 *actual, false); compare_reg_ret(*handle, op->mem.index, eop->mem_index, false); - compare_int32_ret(op->mem.disp, eop->mem_disp, false); + compare_int64_ret(op->mem.disp, eop->mem_disp, false); break; case AARCH64_OP_PRED: compare_reg_ret(*handle, op->pred.reg, eop->pred_reg,