Skip to content

Commit

Permalink
Fix segfault in cs_option() on s390x
Browse files Browse the repository at this point in the history
capstone-rs crashes on s390x (also knows as SystemZ in the LLVM world)
as follows:

    Thread 2 received signal SIGSEGV, Segmentation fault.
    0x000002aa00a6a604 in cs_option (ud=140702726002672, type=CS_OPT_SYNTAX, value=2) at capstone/cs.c:782
    782             return arch_configs[handle->arch].arch_option(handle, type, value);

    (gdb) p/x handle->arch
    $7 = 0x7ffb

handle->arch is clearly corrupted. It is assigned as follows in
cs_open():

       0x000002aa00a6a44a <+146>:   lgr     %r9,%r3
    [...]
       0x000002aa00a6a468 <+176>:   rosbg   %r9,%r10,0,31,32
    [...]
    458                     ud->arch = arch;
    459                     ud->mode = mode;
       0x000002aa00a6a47a <+194>:   stg     %r9,0(%r13)

Here the 32-bit arch value and the 32-bit mode value are placed into a
64-bit register and stored with one instruction.

The important part is that cs_mode is a 32-bit value passed in %r3, and
the code uses instructions with mnemonics ending with "g", which
operate on full 64-bit registers [1]. This is allowed because of the
s390x ABI [1]: it requires zero-extension of 4-byte int arguments.

Bitfield enums are currently generated like this:

    #[repr(C)]
    [...]
    pub struct cs_mode(pub libc::c_uint);

which causes the problem above, because the ABI does not require
zero-extension of 4-byte struct arguments.

Starting from RustTarget::Stable_1_28, bindgen
generates #[repr(transparent)] instead of #[repr(C)], which is
designed to solve exactly this kind of problems [3].

capstone-rs' MSRV is 1.70, so it should be possible to go even further,
but be conservative and use the minimum version necessary to fix the
issue.

[1] https://publibfp.dhe.ibm.com/epubs/pdf/a227832d.pdf
[2] https://github.com/IBM/s390x-abi/releases
[3] https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent
  • Loading branch information
iii-i committed Jan 14, 2025
1 parent 62c7ec5 commit 0658209
Show file tree
Hide file tree
Showing 4 changed files with 340 additions and 623 deletions.
76 changes: 38 additions & 38 deletions capstone-rs/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,69 +232,69 @@ define_cs_enum_wrapper!(
=> Mode = cs_mode
]
/// 32-bit ARM
=> Arm = CS_MODE_ARM;
=> Arm = { cs_mode::CS_MODE_ARM };
/// 16-bit mode (X86)
=> Mode16 = CS_MODE_16;
=> Mode16 = { cs_mode::CS_MODE_16 };
/// 32-bit mode (X86)
=> Mode32 = CS_MODE_32;
=> Mode32 = { cs_mode::CS_MODE_32 };
/// 64-bit mode (X86, PPC)
=> Mode64 = CS_MODE_64;
=> Mode64 = { cs_mode::CS_MODE_64 };
/// ARM's Thumb mode, including Thumb-2
=> Thumb = CS_MODE_THUMB;
=> Thumb = { cs_mode::CS_MODE_THUMB };
/// Mips II ISA
=> Mips2 = CS_MODE_MIPS2;
=> Mips2 = { cs_mode::CS_MODE_MIPS2 };
/// Mips III ISA
=> Mips3 = CS_MODE_MIPS3;
=> Mips3 = { cs_mode::CS_MODE_MIPS3 };
/// Mips32r6 ISA
=> Mips32R6 = CS_MODE_MIPS32R6;
=> Mips32R6 = { cs_mode::CS_MODE_MIPS32R6 };
/// Mips32 ISA (Mips)
=> Mips32 = CS_MODE_MIPS32;
=> Mips32 = { cs_mode::CS_MODE_MIPS32 };
/// Mips64 ISA (Mips)
=> Mips64 = CS_MODE_MIPS64;
=> Mips64 = { cs_mode::CS_MODE_MIPS64 };
/// SparcV9 mode (Sparc)
=> V9 = CS_MODE_V9;
=> V9 = { cs_mode::CS_MODE_V9 };
/// Quad Processing eXtensions mode (PPC)
=> Qpx = CS_MODE_QPX;
=> Qpx = { cs_mode::CS_MODE_QPX };
/// M68K 68000 mode
=> M68k000 = CS_MODE_M68K_000;
=> M68k000 = { cs_mode::CS_MODE_M68K_000 };
/// M68K 68010 mode
=> M68k010 = CS_MODE_M68K_010;
=> M68k010 = { cs_mode::CS_MODE_M68K_010 };
/// M68K 68020 mode
=> M68k020 = CS_MODE_M68K_020;
=> M68k020 = { cs_mode::CS_MODE_M68K_020 };
/// M68K 68030 mode
=> M68k030 = CS_MODE_M68K_030;
=> M68k030 = { cs_mode::CS_MODE_M68K_030 };
/// M68K 68040 mode
=> M68k040 = CS_MODE_M68K_040;
=> M68k040 = { cs_mode::CS_MODE_M68K_040 };
/// M680X Hitachi 6301,6303 mode
=> M680x6301 = CS_MODE_M680X_6301;
=> M680x6301 = { cs_mode::CS_MODE_M680X_6301 };
/// M680X Hitachi 6309 mode
=> M680x6309 = CS_MODE_M680X_6309;
=> M680x6309 = { cs_mode::CS_MODE_M680X_6309 };
/// M680X Motorola 6800,6802 mode
=> M680x6800 = CS_MODE_M680X_6800;
=> M680x6800 = { cs_mode::CS_MODE_M680X_6800 };
/// M680X Motorola 6801,6803 mode
=> M680x6801 = CS_MODE_M680X_6801;
=> M680x6801 = { cs_mode::CS_MODE_M680X_6801 };
/// M680X Motorola/Freescale 6805 mode
=> M680x6805 = CS_MODE_M680X_6805;
=> M680x6805 = { cs_mode::CS_MODE_M680X_6805 };
/// M680X Motorola/Freescale/NXP 68HC08 mode
=> M680x6808 = CS_MODE_M680X_6808;
=> M680x6808 = { cs_mode::CS_MODE_M680X_6808 };
/// M680X Motorola 6809 mode
=> M680x6809 = CS_MODE_M680X_6809;
=> M680x6809 = { cs_mode::CS_MODE_M680X_6809 };
/// M680X Motorola/Freescale/NXP 68HC11 mode
=> M680x6811 = CS_MODE_M680X_6811;
=> M680x6811 = { cs_mode::CS_MODE_M680X_6811 };
/// M680X Motorola/Freescale/NXP CPU12
=> M680xCpu12 = CS_MODE_M680X_CPU12;
=> M680xCpu12 = { cs_mode::CS_MODE_M680X_CPU12 };
/// M680X Freescale/NXP HCS08 mode
=> M680xHcs08 = CS_MODE_M680X_HCS08;
=> M680xHcs08 = { cs_mode::CS_MODE_M680X_HCS08 };
/// RISC-V 32-bit mode
=> RiscV32 = CS_MODE_RISCV32;
=> RiscV32 = { cs_mode::CS_MODE_RISCV32 };
/// RISC-V 64-bit mode
=> RiscV64 = CS_MODE_RISCV64;
=> RiscV64 = { cs_mode::CS_MODE_RISCV64 };
/// Classic BPF mode
=> Cbpf = CS_MODE_BPF_CLASSIC;
=> Cbpf = { cs_mode::CS_MODE_BPF_CLASSIC };
/// Extended BPF mode
=> Ebpf = CS_MODE_BPF_EXTENDED;
=> Ebpf = { cs_mode::CS_MODE_BPF_EXTENDED };
/// Default mode for little-endian
=> Default = CS_MODE_LITTLE_ENDIAN;
=> Default = { cs_mode::CS_MODE_LITTLE_ENDIAN };
);

define_cs_enum_wrapper!(
Expand All @@ -303,13 +303,13 @@ define_cs_enum_wrapper!(
=> ExtraMode = cs_mode
]
/// ARM's Cortex-M series. Works with `Arm` mode.
=> MClass = CS_MODE_MCLASS;
=> MClass = { cs_mode::CS_MODE_MCLASS };
/// ARMv8 A32 encodings for ARM. Works with `Arm` and `Thumb` modes.
=> V8 = CS_MODE_V8;
=> V8 = { cs_mode::CS_MODE_V8 };
/// MicroMips mode. Works in `MIPS` mode.
=> Micro = CS_MODE_MICRO;
=> Micro = { cs_mode::CS_MODE_MICRO };
/// RISC-V compressed instruction mode
=> RiscVC = CS_MODE_RISCVC;
=> RiscVC = { cs_mode::CS_MODE_RISCVC };
);

define_cs_enum_wrapper!(
Expand All @@ -318,9 +318,9 @@ define_cs_enum_wrapper!(
=> Endian = cs_mode
]
/// Little-endian mode
=> Little = CS_MODE_LITTLE_ENDIAN;
=> Little = { cs_mode::CS_MODE_LITTLE_ENDIAN };
/// Big-endian mode
=> Big = CS_MODE_BIG_ENDIAN;
=> Big = { cs_mode::CS_MODE_BIG_ENDIAN };
);

define_cs_enum_wrapper!(
Expand Down
6 changes: 3 additions & 3 deletions capstone-rs/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ impl TryFrom<cs_ac_type> for RegAccessType {

fn try_from(access: cs_ac_type) -> Result<Self, Self::Error> {
// Check for flags other than CS_AC_READ or CS_AC_WRITE.
let unknown_flag_mask = !(CS_AC_READ | CS_AC_WRITE).0;
let unknown_flag_mask = !(cs_ac_type::CS_AC_READ | cs_ac_type::CS_AC_WRITE).0;
if (access.0 & unknown_flag_mask) != 0 {
return Err(());
}

let is_readable = (access & CS_AC_READ).0 != 0;
let is_writable = (access & CS_AC_WRITE).0 != 0;
let is_readable = (access & cs_ac_type::CS_AC_READ).0 != 0;
let is_writable = (access & cs_ac_type::CS_AC_WRITE).0 != 0;
match (is_readable, is_writable) {
(true, false) => Ok(RegAccessType::ReadOnly),
(false, true) => Ok(RegAccessType::WriteOnly),
Expand Down
2 changes: 1 addition & 1 deletion capstone-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ fn write_bindgen_bindings(
) {
#[allow(deprecated)]
let mut builder = bindgen::Builder::default()
.rust_target(bindgen::RustTarget::Stable_1_19)
.rust_target(bindgen::RustTarget::Stable_1_28)
.size_t_is_usize(true)
.use_core()
.ctypes_prefix("libc")
Expand Down
Loading

0 comments on commit 0658209

Please sign in to comment.