diff --git a/Mapping.c b/Mapping.c index 1fa4d3a54b..b5f0dac913 100644 --- a/Mapping.c +++ b/Mapping.c @@ -348,6 +348,7 @@ DEFINE_get_detail_op(mips, Mips); DEFINE_get_detail_op(riscv, RISCV); DEFINE_get_detail_op(systemz, SystemZ); DEFINE_get_detail_op(xtensa, Xtensa); +DEFINE_get_detail_op(bpf, BPF); /// Returns true if for this architecture the /// alias operands should be filled. diff --git a/Mapping.h b/Mapping.h index d191c7a03c..20206b83a2 100644 --- a/Mapping.h +++ b/Mapping.h @@ -145,6 +145,7 @@ DECL_get_detail_op(mips, Mips); DECL_get_detail_op(riscv, RISCV); DECL_get_detail_op(systemz, SystemZ); DECL_get_detail_op(xtensa, Xtensa); +DECL_get_detail_op(bpf, BPF); /// Increments the detail->arch.op_count by one. #define DEFINE_inc_detail_op_count(arch, ARCH) \ @@ -182,6 +183,8 @@ DEFINE_inc_detail_op_count(systemz, SystemZ); DEFINE_dec_detail_op_count(systemz, SystemZ); DEFINE_inc_detail_op_count(xtensa, Xtensa); DEFINE_dec_detail_op_count(xtensa, Xtensa); +DEFINE_inc_detail_op_count(bpf, BPF); +DEFINE_dec_detail_op_count(bpf, BPF); /// Returns true if a memory operand is currently edited. static inline bool doing_mem(const MCInst *MI) @@ -214,6 +217,7 @@ DEFINE_get_arch_detail(mips, Mips); DEFINE_get_arch_detail(riscv, RISCV); DEFINE_get_arch_detail(systemz, SystemZ); DEFINE_get_arch_detail(xtensa, Xtensa); +DEFINE_get_arch_detail(bpf, BPF); #define DEFINE_check_safe_inc(Arch, ARCH) \ static inline void Arch##_check_safe_inc(const MCInst *MI) { \ @@ -230,6 +234,7 @@ DEFINE_check_safe_inc(LoongArch, LOONGARCH); DEFINE_check_safe_inc(RISCV, RISCV); DEFINE_check_safe_inc(SystemZ, SYSTEMZ); DEFINE_check_safe_inc(Mips, MIPS); +DEFINE_check_safe_inc(BPF, BPF); static inline bool detail_is_set(const MCInst *MI) { diff --git a/SStream.c b/SStream.c index 5ccbf53f9f..b18cd4715e 100644 --- a/SStream.c +++ b/SStream.c @@ -331,6 +331,22 @@ void printInt16(SStream *ss, int16_t val) } } +void printInt16HexOffset(SStream *ss, int16_t val) +{ + assert(ss); + SSTREAM_RETURN_IF_CLOSED(ss); + if (val >= 0) { + SStream_concat(ss, "+0x%" PRIx16, val); + } else { + if (val == INT16_MIN) + SStream_concat(ss, "-0x%" PRIx16, + (uint16_t)INT16_MAX + 1); + else + SStream_concat(ss, "-0x%" PRIx16, (int16_t)-val); + } +} + + void printInt32(SStream *ss, int32_t val) { assert(ss); @@ -352,6 +368,36 @@ void printInt32(SStream *ss, int32_t val) } } +void printInt32HexOffset(SStream *ss, int32_t val) +{ + assert(ss); + SSTREAM_RETURN_IF_CLOSED(ss); + if (val >= 0) { + SStream_concat(ss, "+0x%" PRIx32, val); + } else { + if (val == INT32_MIN) + SStream_concat(ss, "-0x%" PRIx32, + (uint32_t)INT32_MAX + 1); + else + SStream_concat(ss, "-0x%" PRIx32, (int32_t)-val); + } +} + +void printInt32Hex(SStream *ss, int32_t val) +{ + assert(ss); + SSTREAM_RETURN_IF_CLOSED(ss); + if (val >= 0) { + SStream_concat(ss, "0x%" PRIx32, val); + } else { + if (val == INT32_MIN) + SStream_concat(ss, "-0x%" PRIx32, + (uint32_t)INT32_MAX + 1); + else + SStream_concat(ss, "-0x%" PRIx32, (int32_t)-val); + } +} + void printUInt32Bang(SStream *ss, uint32_t val) { assert(ss); diff --git a/SStream.h b/SStream.h index 8789ea6cc6..5ed98f82bc 100644 --- a/SStream.h +++ b/SStream.h @@ -73,7 +73,11 @@ void printInt32Bang(SStream *O, int32_t val); void printInt8(SStream *O, int8_t val); void printInt16(SStream *O, int16_t val); +void printInt16HexOffset(SStream *O, int16_t val); + void printInt32(SStream *O, int32_t val); +void printInt32Hex(SStream *ss, int32_t val); +void printInt32HexOffset(SStream *ss, int32_t val); void printUInt32Bang(SStream *O, uint32_t val); diff --git a/arch/BPF/BPFConstants.h b/arch/BPF/BPFConstants.h index d12590460c..49bc1fb30d 100644 --- a/arch/BPF/BPFConstants.h +++ b/arch/BPF/BPFConstants.h @@ -1,5 +1,7 @@ /* Capstone Disassembly Engine */ /* BPF Backend by david942j , 2019 */ +/* SPDX-FileCopyrightText: 2024 Roee Toledano */ +/* SPDX-License-Identifier: BSD-3 */ /* This file defines constants and macros used for parsing a BPF instruction */ @@ -9,80 +11,93 @@ #define BPF_CLASS(code) ((code) & 0x7) ///< Instruction classes -#define BPF_CLASS_LD 0x00 -#define BPF_CLASS_LDX 0x01 -#define BPF_CLASS_ST 0x02 -#define BPF_CLASS_STX 0x03 -#define BPF_CLASS_ALU 0x04 -#define BPF_CLASS_JMP 0x05 -#define BPF_CLASS_RET 0x06 ///< cBPF only -#define BPF_CLASS_MISC 0x07 ///< cBPF only -#define BPF_CLASS_ALU64 0x07 ///< eBPF only +#define BPF_CLASS_LD 0x00 +#define BPF_CLASS_LDX 0x01 +#define BPF_CLASS_ST 0x02 +#define BPF_CLASS_STX 0x03 +#define BPF_CLASS_ALU 0x04 +#define BPF_CLASS_JMP 0x05 +#define BPF_CLASS_RET 0x06 ///< cBPF only +#define BPF_CLASS_JMP32 0x06 ///< eBPF only +#define BPF_CLASS_MISC 0x07 ///< cBPF only +#define BPF_CLASS_ALU64 0x07 ///< eBPF only #define BPF_OP(code) ((code) & 0xf0) ///< Types of ALU instruction -#define BPF_ALU_ADD 0x00 -#define BPF_ALU_SUB 0x10 -#define BPF_ALU_MUL 0x20 -#define BPF_ALU_DIV 0x30 -#define BPF_ALU_OR 0x40 -#define BPF_ALU_AND 0x50 -#define BPF_ALU_LSH 0x60 -#define BPF_ALU_RSH 0x70 -#define BPF_ALU_NEG 0x80 -#define BPF_ALU_MOD 0x90 -#define BPF_ALU_XOR 0xa0 -#define BPF_ALU_MOV 0xb0 ///< eBPF only: mov reg to reg -#define BPF_ALU_ARSH 0xc0 ///< eBPF only: sign extending shift right -#define BPF_ALU_END 0xd0 ///< eBPF only: endianness conversion +#define BPF_ALU_ADD 0x00 +#define BPF_ALU_SUB 0x10 +#define BPF_ALU_MUL 0x20 +#define BPF_ALU_DIV 0x30 +#define BPF_ALU_OR 0x40 +#define BPF_ALU_AND 0x50 +#define BPF_ALU_LSH 0x60 +#define BPF_ALU_RSH 0x70 +#define BPF_ALU_NEG 0x80 +#define BPF_ALU_MOD 0x90 +#define BPF_ALU_XOR 0xa0 +#define BPF_ALU_MOV 0xb0 ///< eBPF only: mov reg to reg +#define BPF_ALU_ARSH 0xc0 ///< eBPF only: sign extending shift right +#define BPF_ALU_END 0xd0 ///< eBPF only: endianness conversion ///< Types of jmp instruction -#define BPF_JUMP_JA 0x00 ///< goto -#define BPF_JUMP_JEQ 0x10 ///< '==' -#define BPF_JUMP_JGT 0x20 ///< unsigned '>' -#define BPF_JUMP_JGE 0x30 ///< unsigned '>=' -#define BPF_JUMP_JSET 0x40 ///< '&' -#define BPF_JUMP_JNE 0x50 ///< eBPF only: '!=' */ -#define BPF_JUMP_JSGT 0x60 ///< eBPF only: signed '>' -#define BPF_JUMP_JSGE 0x70 ///< eBPF only: signed '>=' -#define BPF_JUMP_CALL 0x80 ///< eBPF only: function call -#define BPF_JUMP_EXIT 0x90 ///< eBPF only: exit -#define BPF_JUMP_JLT 0xa0 ///< eBPF only: unsigned '<' -#define BPF_JUMP_JLE 0xb0 ///< eBPF only: unsigned '<=' -#define BPF_JUMP_JSLT 0xc0 ///< eBPF only: signed '<' -#define BPF_JUMP_JSLE 0xd0 ///< eBPF only: signed '<=' +#define BPF_JUMP_JA 0x00 ///< goto +#define BPF_JUMP_JEQ 0x10 ///< '==' +#define BPF_JUMP_JGT 0x20 ///< unsigned '>' +#define BPF_JUMP_JGE 0x30 ///< unsigned '>=' +#define BPF_JUMP_JSET 0x40 ///< '&' +#define BPF_JUMP_JNE 0x50 ///< eBPF only: '!=' */ +#define BPF_JUMP_JSGT 0x60 ///< eBPF only: signed '>' +#define BPF_JUMP_JSGE 0x70 ///< eBPF only: signed '>=' +#define BPF_JUMP_CALL 0x80 ///< eBPF only: function call +#define BPF_JUMP_EXIT 0x90 ///< eBPF only: exit +#define BPF_JUMP_JLT 0xa0 ///< eBPF only: unsigned '<' +#define BPF_JUMP_JLE 0xb0 ///< eBPF only: unsigned '<=' +#define BPF_JUMP_JSLT 0xc0 ///< eBPF only: signed '<' +#define BPF_JUMP_JSLE 0xd0 ///< eBPF only: signed '<=' + +///< Types of complex atomic instructions +#define BPF_ATOMIC_XCHG 0xe0 ///< eBPF only: atomic exchange +#define BPF_ATOMIC_CMPXCHG 0xf0 ///< eBPF only: atomic compare and exchange #define BPF_SRC(code) ((code) & 0x08) #define BPF_RVAL(code) ((code) & 0x18) /* cBPF only: for return types */ ///< Source operand -#define BPF_SRC_K 0x00 -#define BPF_SRC_X 0x08 -#define BPF_SRC_A 0x10 /* cBPF only */ +#define BPF_SRC_K 0x00 +#define BPF_SRC_X 0x08 +#define BPF_SRC_A 0x10 /* cBPF only */ -#define BPF_SRC_LITTLE BPF_SRC_K -#define BPF_SRC_BIG BPF_SRC_X +#define BPF_SRC_LITTLE BPF_SRC_K +#define BPF_SRC_BIG BPF_SRC_X #define BPF_SIZE(code) ((code) & 0x18) ///< Size modifier -#define BPF_SIZE_W 0x00 ///< word -#define BPF_SIZE_H 0x08 ///< half word -#define BPF_SIZE_B 0x10 ///< byte -#define BPF_SIZE_DW 0x18 ///< eBPF only: double word +#define BPF_SIZE_W 0x00 ///< word +#define BPF_SIZE_H 0x08 ///< half word +#define BPF_SIZE_B 0x10 ///< byte +#define BPF_SIZE_DW 0x18 ///< eBPF only: double word #define BPF_MODE(code) ((code) & 0xe0) ///< Mode modifier -#define BPF_MODE_IMM 0x00 ///< used for 32-bit mov in cBPF and 64-bit in eBPF -#define BPF_MODE_ABS 0x20 -#define BPF_MODE_IND 0x40 -#define BPF_MODE_MEM 0x60 -#define BPF_MODE_LEN 0x80 ///< cBPF only, reserved in eBPF -#define BPF_MODE_MSH 0xa0 ///< cBPF only, reserved in eBPF -#define BPF_MODE_XADD 0xc0 ///< eBPF only: exclusive add +#define BPF_MODE_IMM 0x00 ///< used for 32-bit mov in cBPF and 64-bit in eBPF +#define BPF_MODE_ABS \ + 0x20 ///< absolute indexing of socket buffer. eBPF only, but deprecated in new versions +#define BPF_MODE_IND \ + 0x40 ///< indirect indexing of socket buffer. eBPF only, but deprecated in new versions +#define BPF_MODE_MEM 0x60 +#define BPF_MODE_LEN 0x80 ///< cBPF only, reserved in eBPF +#define BPF_MODE_MSH 0xa0 ///< cBPF only, reserved in eBPF +#define BPF_MODE_ATOMIC \ + 0xc0 ///< eBPF only: atomic operations. Originally BPF_MODE_XADD + +#define BPF_MODE_FETCH \ + 0x01 /* eBPF only: overwrite 'src' with what was in the modified mem address before it was modified. + NOTE: in contrast to regular modifiers, this one is encoded in the 'imm' field, not opcode! + Must be used for BPF_XCHG and BPF_CMPXCHG. Optional for the other atomic operations. */ #define BPF_MISCOP(code) ((code) & 0x80) ///< Operation of misc -#define BPF_MISCOP_TAX 0x00 -#define BPF_MISCOP_TXA 0x80 +#define BPF_MISCOP_TAX 0x00 +#define BPF_MISCOP_TXA 0x80 #endif diff --git a/arch/BPF/BPFDisassembler.c b/arch/BPF/BPFDisassembler.c index 6b3e00bdac..f6d46bae75 100644 --- a/arch/BPF/BPFDisassembler.c +++ b/arch/BPF/BPFDisassembler.c @@ -1,5 +1,7 @@ /* Capstone Disassembly Engine */ /* BPF Backend by david942j , 2019 */ +/* SPDX-FileCopyrightText: 2024 Roee Toledano */ +/* SPDX-License-Identifier: BSD-3 */ #ifdef CAPSTONE_HAS_BPF @@ -9,26 +11,12 @@ #include "BPFConstants.h" #include "BPFDisassembler.h" #include "BPFMapping.h" +#include "../../Mapping.h" #include "../../cs_priv.h" - -static uint16_t read_u16(cs_struct *ud, const uint8_t *code) -{ - if (MODE_IS_BIG_ENDIAN(ud->mode)) - return (((uint16_t)code[0] << 8) | code[1]); - else - return (((uint16_t)code[1] << 8) | code[0]); -} - -static uint32_t read_u32(cs_struct *ud, const uint8_t *code) -{ - if (MODE_IS_BIG_ENDIAN(ud->mode)) - return ((uint32_t)read_u16(ud, code) << 16) | read_u16(ud, code + 2); - else - return ((uint32_t)read_u16(ud, code + 2) << 16) | read_u16(ud, code); -} +#include "../../utils.h" ///< Malloc bpf_internal, also checks if code_len is large enough. -static bpf_internal *alloc_bpf_internal(size_t code_len) +static bpf_internal *alloc_bpf_internal(const size_t code_len) { bpf_internal *bpf; @@ -43,8 +31,8 @@ static bpf_internal *alloc_bpf_internal(size_t code_len) } ///< Fetch a cBPF structure from code -static bpf_internal* fetch_cbpf(cs_struct *ud, const uint8_t *code, - size_t code_len) +static bpf_internal *fetch_cbpf(MCInst *instr, const uint8_t *code, + const size_t code_len) { bpf_internal *bpf; @@ -52,16 +40,16 @@ static bpf_internal* fetch_cbpf(cs_struct *ud, const uint8_t *code, if (bpf == NULL) return NULL; - bpf->op = read_u16(ud, code); + bpf->op = readBytes16(instr, code); bpf->jt = code[2]; bpf->jf = code[3]; - bpf->k = read_u32(ud, code + 4); + bpf->k = readBytes32(instr, code + 4); return bpf; } ///< Fetch an eBPF structure from code -static bpf_internal* fetch_ebpf(cs_struct *ud, const uint8_t *code, - size_t code_len) +static bpf_internal *fetch_ebpf(MCInst *instr, const uint8_t *code, + const size_t code_len) { bpf_internal *bpf; @@ -80,39 +68,43 @@ static bpf_internal* fetch_ebpf(cs_struct *ud, const uint8_t *code, cs_mem_free(bpf); return NULL; } - bpf->k = read_u32(ud, code + 4) | (((uint64_t)read_u32(ud, code + 12)) << 32); + bpf->k = readBytes32(instr, code + 4) | + (((uint64_t)readBytes32(instr, code + 12)) << 32); bpf->insn_size = 16; - } - else { - bpf->offset = read_u16(ud, code + 2); - bpf->k = read_u32(ud, code + 4); + } else { + bpf->offset = readBytes16(instr, code + 2); + bpf->k = readBytes32(instr, code + 4); } return bpf; } -#define CHECK_READABLE_REG(ud, reg) do { \ - if (! ((reg) >= BPF_REG_R0 && (reg) <= BPF_REG_R10)) \ +#define CHECK_READABLE_REG(ud, reg) \ + do { \ + if (!((reg) >= BPF_REG_R0 && (reg) <= BPF_REG_R10)) \ return false; \ } while (0) -#define CHECK_WRITABLE_REG(ud, reg) do { \ - if (! ((reg) >= BPF_REG_R0 && (reg) < BPF_REG_R10)) \ +#define CHECK_WRITEABLE_REG(ud, reg) \ + do { \ + if (!((reg) >= BPF_REG_R0 && (reg) < BPF_REG_R10)) \ return false; \ } while (0) -#define CHECK_READABLE_AND_PUSH(ud, MI, r) do { \ +#define CHECK_READABLE_AND_PUSH(ud, MI, r) \ + do { \ CHECK_READABLE_REG(ud, r + BPF_REG_R0); \ MCOperand_CreateReg0(MI, r + BPF_REG_R0); \ } while (0) -#define CHECK_WRITABLE_AND_PUSH(ud, MI, r) do { \ - CHECK_WRITABLE_REG(ud, r + BPF_REG_R0); \ +#define CHECK_WRITABLE_AND_PUSH(ud, MI, r) \ + do { \ + CHECK_WRITEABLE_REG(ud, r + BPF_REG_R0); \ MCOperand_CreateReg0(MI, r + BPF_REG_R0); \ } while (0) -static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) +static bool decodeLoad(MCInst *MI, bpf_internal *bpf) { - if (!EBPF_MODE(ud)) { + if (!EBPF_MODE(MI->csh->mode)) { /* * +-----+-----------+--------------------+ * | ldb | [k] | [x+k] | @@ -121,7 +113,8 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) */ if (BPF_SIZE(bpf->op) == BPF_SIZE_DW) return false; - if (BPF_SIZE(bpf->op) == BPF_SIZE_B || BPF_SIZE(bpf->op) == BPF_SIZE_H) { + if (BPF_SIZE(bpf->op) == BPF_SIZE_B || + BPF_SIZE(bpf->op) == BPF_SIZE_H) { /* no ldx */ if (BPF_CLASS(bpf->op) != BPF_CLASS_LD) return false; @@ -129,8 +122,7 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) if (BPF_MODE(bpf->op) == BPF_MODE_ABS) { MCOperand_CreateImm0(MI, bpf->k); return true; - } - else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { + } else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { MCOperand_CreateReg0(MI, BPF_REG_X); MCOperand_CreateImm0(MI, bpf->k); return true; @@ -160,14 +152,12 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) if (BPF_MODE(bpf->op) == BPF_MODE_ABS) { MCOperand_CreateImm0(MI, bpf->k); return true; - } - else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { + } else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { MCOperand_CreateReg0(MI, BPF_REG_X); MCOperand_CreateImm0(MI, bpf->k); return true; } - } - else { /* LDX */ + } else { /* LDX */ if (BPF_MODE(bpf->op) == BPF_MODE_MSH) { MCOperand_CreateImm0(MI, bpf->k); return true; @@ -179,14 +169,15 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) /* eBPF mode */ /* * - IMM: lddw dst, imm64 - * - ABS: ld{w,h,b,dw} [k] - * - IND: ld{w,h,b,dw} [src+k] + * - ABS: ld{w,h,b} [k] + * - IND: ld{w,h,b} [src] * - MEM: ldx{w,h,b,dw} dst, [src+off] */ if (BPF_CLASS(bpf->op) == BPF_CLASS_LD) { switch (BPF_MODE(bpf->op)) { case BPF_MODE_IMM: - if (bpf->op != (BPF_CLASS_LD | BPF_SIZE_DW | BPF_MODE_IMM)) + if (bpf->op != + (BPF_CLASS_LD | BPF_SIZE_DW | BPF_MODE_IMM)) return false; CHECK_WRITABLE_AND_PUSH(ud, MI, bpf->dst); MCOperand_CreateImm0(MI, bpf->k); @@ -196,11 +187,9 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) return true; case BPF_MODE_IND: CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); - MCOperand_CreateImm0(MI, bpf->k); return true; } return false; - } /* LDX */ if (BPF_MODE(bpf->op) == BPF_MODE_MEM) { @@ -212,7 +201,7 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) return false; } -static bool decodeStore(cs_struct *ud, MCInst *MI, bpf_internal *bpf) +static bool decodeStore(MCInst *MI, bpf_internal *bpf) { /* in cBPF, only BPF_ST* | BPF_MEM | BPF_W is valid * while in eBPF: @@ -220,7 +209,7 @@ static bool decodeStore(cs_struct *ud, MCInst *MI, bpf_internal *bpf) * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW} * are valid */ - if (!EBPF_MODE(ud)) { + if (!EBPF_MODE(MI->csh->mode)) { /* can only store to M[] */ if (bpf->op != (BPF_CLASS(bpf->op) | BPF_MODE_MEM | BPF_SIZE_W)) return false; @@ -229,11 +218,11 @@ static bool decodeStore(cs_struct *ud, MCInst *MI, bpf_internal *bpf) } /* eBPF */ - - if (BPF_MODE(bpf->op) == BPF_MODE_XADD) { + if (BPF_MODE(bpf->op) == BPF_MODE_ATOMIC) { if (BPF_CLASS(bpf->op) != BPF_CLASS_STX) return false; - if (BPF_SIZE(bpf->op) != BPF_SIZE_W && BPF_SIZE(bpf->op) != BPF_SIZE_DW) + if (BPF_SIZE(bpf->op) != BPF_SIZE_W && + BPF_SIZE(bpf->op) != BPF_SIZE_DW) return false; /* xadd [dst + off], src */ CHECK_READABLE_AND_PUSH(ud, MI, bpf->dst); @@ -255,12 +244,12 @@ static bool decodeStore(cs_struct *ud, MCInst *MI, bpf_internal *bpf) return true; } -static bool decodeALU(cs_struct *ud, MCInst *MI, bpf_internal *bpf) +static bool decodeALU(MCInst *MI, bpf_internal *bpf) { /* Set MI->Operands */ /* cBPF */ - if (!EBPF_MODE(ud)) { + if (!EBPF_MODE(MI->csh->mode)) { if (BPF_OP(bpf->op) > BPF_ALU_XOR) return false; /* cBPF's NEG has no operands */ @@ -281,7 +270,8 @@ static bool decodeALU(cs_struct *ud, MCInst *MI, bpf_internal *bpf) if (BPF_OP(bpf->op) == BPF_ALU_END) { if (bpf->k != 16 && bpf->k != 32 && bpf->k != 64) return false; - if (BPF_CLASS(bpf->op) == BPF_CLASS_ALU64 && BPF_SRC(bpf->op) != BPF_SRC_LITTLE) + if (BPF_CLASS(bpf->op) == BPF_CLASS_ALU64 && + BPF_SRC(bpf->op) != BPF_SRC_LITTLE) return false; } @@ -298,24 +288,23 @@ static bool decodeALU(cs_struct *ud, MCInst *MI, bpf_internal *bpf) return true; if (BPF_OP(bpf->op) == BPF_ALU_END) { /* bpf->k must be one of 16, 32, 64 */ - MCInst_setOpcode(MI, MCInst_getOpcode(MI) | ((uint32_t)bpf->k << 4)); + bpf->op |= ((uint32_t)bpf->k << 4); return true; } /* normal cases */ if (BPF_SRC(bpf->op) == BPF_SRC_K) { MCOperand_CreateImm0(MI, bpf->k); - } - else { /* BPF_SRC_X */ + } else { /* BPF_SRC_X */ CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); } return true; } -static bool decodeJump(cs_struct *ud, MCInst *MI, bpf_internal *bpf) +static bool decodeJump(MCInst *MI, bpf_internal *bpf) { /* cBPF and eBPF are very different in class jump */ - if (!EBPF_MODE(ud)) { + if (!EBPF_MODE(MI->csh->mode)) { if (BPF_OP(bpf->op) > BPF_JUMP_JSET) return false; @@ -331,11 +320,11 @@ static bool decodeJump(cs_struct *ud, MCInst *MI, bpf_internal *bpf) MCOperand_CreateReg0(MI, BPF_REG_X); MCOperand_CreateImm0(MI, bpf->jt); MCOperand_CreateImm0(MI, bpf->jf); - } - else { + } else { if (BPF_OP(bpf->op) > BPF_JUMP_JSLE) return false; + /* JMP32 has no CALL/EXIT instruction */ /* No operands for exit */ if (BPF_OP(bpf->op) == BPF_JUMP_EXIT) return bpf->op == (BPF_CLASS_JMP | BPF_JUMP_EXIT); @@ -344,7 +333,8 @@ static bool decodeJump(cs_struct *ud, MCInst *MI, bpf_internal *bpf) MCOperand_CreateImm0(MI, bpf->k); return true; } - if (bpf->op == (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) { + if (bpf->op == + (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) { CHECK_READABLE_AND_PUSH(ud, MI, bpf->k); return true; } @@ -355,7 +345,11 @@ static bool decodeJump(cs_struct *ud, MCInst *MI, bpf_internal *bpf) if (BPF_OP(bpf->op) == BPF_JUMP_JA) { if (BPF_SRC(bpf->op) != BPF_SRC_K) return false; - MCOperand_CreateImm0(MI, bpf->offset); + if (BPF_CLASS(bpf->op) == BPF_CLASS_JMP) + MCOperand_CreateImm0(MI, bpf->offset); + else + MCOperand_CreateImm0(MI, bpf->k); + return true; } @@ -370,7 +364,7 @@ static bool decodeJump(cs_struct *ud, MCInst *MI, bpf_internal *bpf) return true; } -static bool decodeReturn(cs_struct *ud, MCInst *MI, bpf_internal *bpf) +static bool decodeReturn(MCInst *MI, bpf_internal *bpf) { /* Here only handles the BPF_RET class in cBPF */ switch (BPF_RVAL(bpf->op)) { @@ -387,7 +381,7 @@ static bool decodeReturn(cs_struct *ud, MCInst *MI, bpf_internal *bpf) return false; } -static bool decodeMISC(cs_struct *ud, MCInst *MI, bpf_internal *bpf) +static bool decodeMISC(MCInst *MI, bpf_internal *bpf) { uint16_t op = bpf->op ^ BPF_CLASS_MISC; return op == BPF_MISCOP_TAX || op == BPF_MISCOP_TXA; @@ -396,7 +390,7 @@ static bool decodeMISC(cs_struct *ud, MCInst *MI, bpf_internal *bpf) ///< 1. Check if the instruction is valid ///< 2. Set MI->opcode ///< 3. Set MI->Operands -static bool getInstruction(cs_struct *ud, MCInst *MI, bpf_internal *bpf) +static bool getInstruction(MCInst *MI, bpf_internal *bpf) { cs_detail *detail; @@ -407,52 +401,462 @@ static bool getInstruction(cs_struct *ud, MCInst *MI, bpf_internal *bpf) } MCInst_clear(MI); - MCInst_setOpcode(MI, bpf->op); switch (BPF_CLASS(bpf->op)) { default: /* should never happen */ return false; case BPF_CLASS_LD: case BPF_CLASS_LDX: - return decodeLoad(ud, MI, bpf); + return decodeLoad(MI, bpf); case BPF_CLASS_ST: case BPF_CLASS_STX: - return decodeStore(ud, MI, bpf); + return decodeStore(MI, bpf); case BPF_CLASS_ALU: - return decodeALU(ud, MI, bpf); + return decodeALU(MI, bpf); case BPF_CLASS_JMP: - return decodeJump(ud, MI, bpf); + return decodeJump(MI, bpf); case BPF_CLASS_RET: - /* eBPF doesn't have this class */ - if (EBPF_MODE(ud)) - return false; - return decodeReturn(ud, MI, bpf); + /* case BPF_CLASS_JMP32: */ + if (EBPF_MODE(MI->csh->mode)) + return decodeJump(MI, bpf); + else + return decodeReturn(MI, bpf); case BPF_CLASS_MISC: - /* case BPF_CLASS_ALU64: */ - if (EBPF_MODE(ud)) - return decodeALU(ud, MI, bpf); + /* case BPF_CLASS_ALU64: */ + if (EBPF_MODE(MI->csh->mode)) + return decodeALU(MI, bpf); else - return decodeMISC(ud, MI, bpf); + return decodeMISC(MI, bpf); } } +// Check for regular load instructions +#define REG_LOAD_CASE(c) \ + case BPF_SIZE_##c: \ + if (BPF_CLASS(opcode) == BPF_CLASS_LD) \ + return BPF_INS_LD##c; \ + else \ + return BPF_INS_LDX##c; + +static bpf_insn op2insn_ld_cbpf(unsigned opcode) +{ + switch (BPF_SIZE(opcode)) { + REG_LOAD_CASE(W); + REG_LOAD_CASE(H); + REG_LOAD_CASE(B); + REG_LOAD_CASE(DW); + } + + return BPF_INS_INVALID; +} +#undef REG_LOAD_CASE + +// Check for packet load instructions +#define PACKET_LOAD_CASE(c) \ + case BPF_SIZE_##c: \ + if (BPF_MODE(opcode) == BPF_MODE_ABS) \ + return BPF_INS_LDABS##c; \ + else if (BPF_MODE(opcode) == BPF_MODE_IND) \ + return BPF_INS_LDIND##c; \ + else \ + return BPF_INS_INVALID; + +static bpf_insn op2insn_ld_ebpf(unsigned opcode) +{ + if (BPF_CLASS(opcode) == BPF_CLASS_LD) { + switch (BPF_SIZE(opcode)) { + PACKET_LOAD_CASE(W); + PACKET_LOAD_CASE(H); + PACKET_LOAD_CASE(B); + } + } + + // If it's not a packet load instruction, it must be a regular load instruction + return op2insn_ld_cbpf(opcode); +} +#undef PACKET_LOAD_CASE + +/* During parsing we already checked to make sure the size is D/DW and + * mode is STX and not ST, so we don't need to check again*/ +#define ALU_CASE_REG(c) \ + case BPF_ALU_##c: \ + if (BPF_SIZE(opcode) == BPF_SIZE_W) \ + return BPF_INS_A##c; \ + else \ + return BPF_INS_A##c##64; + +#define ALU_CASE_FETCH(c) \ + case BPF_ALU_##c | BPF_MODE_FETCH: \ + if (BPF_SIZE(opcode) == BPF_SIZE_W) \ + return BPF_INS_AF##c; \ + else \ + return BPF_INS_AF##c##64; + +#define COMPLEX_CASE(c) \ + case BPF_ATOMIC_##c | BPF_MODE_FETCH: \ + if (BPF_SIZE(opcode) == BPF_SIZE_DW) \ + return BPF_INS_A##c##64; + +#define CASE(c) \ + case BPF_SIZE_##c: \ + if (BPF_CLASS(opcode) == BPF_CLASS_ST) \ + return BPF_INS_ST##c; \ + else \ + return BPF_INS_STX##c; + +static bpf_insn op2insn_st(unsigned opcode, const uint32_t imm) +{ + /* + * - BPF_STX | ALU atomic operations | BPF_{W,DW} + * - BPF_STX | Complex atomic operations | BPF_{DW} + * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW} + */ + + if (BPF_MODE(opcode) == BPF_MODE_ATOMIC) { + switch (imm) { + ALU_CASE_REG(ADD); + ALU_CASE_REG(OR); + ALU_CASE_REG(AND); + ALU_CASE_REG(XOR); + ALU_CASE_FETCH(ADD); + ALU_CASE_FETCH(OR); + ALU_CASE_FETCH(AND); + ALU_CASE_FETCH(XOR); + COMPLEX_CASE(XCHG); + COMPLEX_CASE(CMPXCHG); + default: // Could only be reached if complex atomic operation is used without fetch modifier, or not as DW + return BPF_INS_INVALID; + } + } + + /* should be BPF_MEM */ + switch (BPF_SIZE(opcode)) { + CASE(W); + CASE(H); + CASE(B); + CASE(DW); + } + + CS_ASSERT_RET_VAL( + false && "Malformed atomic BPF instruction", + BPF_INS_INVALID); +} +#undef CASE + +#define CASE(c) \ + case BPF_ALU_##c: \ + CASE_IF(c) + +#define CASE_IF(c) \ + do { \ + if (BPF_CLASS(opcode) == BPF_CLASS_ALU) \ + return BPF_INS_##c; \ + else \ + return BPF_INS_##c##64; \ + } while (0) + +static bpf_insn op2insn_alu(unsigned opcode, const uint16_t off, + const bool is_ebpf) +{ + /* Endian is a special case */ + if (BPF_OP(opcode) == BPF_ALU_END) { + if (BPF_CLASS(opcode) == BPF_CLASS_ALU64) { + switch (opcode ^ BPF_CLASS_ALU64 ^ BPF_ALU_END ^ + BPF_SRC_LITTLE) { + case (16 << 4): + return BPF_INS_BSWAP16; + case (32 << 4): + return BPF_INS_BSWAP32; + case (64 << 4): + return BPF_INS_BSWAP64; + default: + return BPF_INS_INVALID; + } + } + + switch (opcode ^ BPF_CLASS_ALU ^ BPF_ALU_END) { + case BPF_SRC_LITTLE | (16 << 4): + return BPF_INS_LE16; + case BPF_SRC_LITTLE | (32 << 4): + return BPF_INS_LE32; + case BPF_SRC_LITTLE | (64 << 4): + return BPF_INS_LE64; + case BPF_SRC_BIG | (16 << 4): + return BPF_INS_BE16; + case BPF_SRC_BIG | (32 << 4): + return BPF_INS_BE32; + case BPF_SRC_BIG | (64 << 4): + return BPF_INS_BE64; + } + return BPF_INS_INVALID; + } + + switch (BPF_OP(opcode)) { + CASE(ADD); + CASE(SUB); + CASE(MUL); + CASE(OR); + CASE(AND); + CASE(LSH); + CASE(RSH); + CASE(NEG); + CASE(XOR); + CASE(ARSH); + case BPF_ALU_DIV: + if (!is_ebpf || off == 0) + CASE_IF(DIV); + else if (off == 1) + CASE_IF(SDIV); + else + return BPF_INS_INVALID; + case BPF_ALU_MOD: + if (!is_ebpf || off == 0) + CASE_IF(MOD); + else if (off == 1) + CASE_IF(SMOD); + else + return BPF_INS_INVALID; + case BPF_ALU_MOV: + /* BPF_CLASS_ALU can have: mov, mov8s, mov16s + * BPF_CLASS_ALU64 can have: mov, mov8s, mov16s, mov32s + * */ + if (off == 0) + CASE_IF(MOV); + else if (off == 8) + CASE_IF(MOVSB); + else if (off == 16) + CASE_IF(MOVSH); + else if (off == 32 && BPF_CLASS(opcode) == BPF_CLASS_ALU64) + return BPF_INS_MOVSW64; + else + return BPF_INS_INVALID; + } + + return BPF_INS_INVALID; +} +#undef CASE_IF +#undef CASE + +#define BPF_CALLX (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X) + +#define CASE(c) \ + case BPF_JUMP_##c: \ + if (BPF_CLASS(opcode) == BPF_CLASS_JMP) \ + return BPF_INS_##c; \ + else \ + return BPF_INS_##c##32; + +#define SPEC_CASE(c) \ + case BPF_JUMP_##c: \ + if (BPF_CLASS(opcode) == BPF_CLASS_JMP) \ + return BPF_INS_##c; \ + else \ + return BPF_INS_INVALID; + +static bpf_insn op2insn_jmp(unsigned opcode) +{ + if (opcode == BPF_CALLX) { + return BPF_INS_CALLX; + } + + switch (BPF_OP(opcode)) { + case BPF_JUMP_JA: + if (BPF_CLASS(opcode) == BPF_CLASS_JMP) + return BPF_INS_JA; + else + return BPF_INS_JAL; + CASE(JEQ); + CASE(JGT); + CASE(JGE); + CASE(JSET); + CASE(JNE); + CASE(JSGT); + CASE(JSGE); + SPEC_CASE(CALL); + SPEC_CASE(EXIT); + CASE(JLT); + CASE(JLE); + CASE(JSLT); + CASE(JSLE); + } + + return BPF_INS_INVALID; +} +#undef SPEC_CASE +#undef CASE +#undef BPF_CALLX + +#ifndef CAPSTONE_DIET + +static void update_regs_access(MCInst *MI, cs_detail *detail, + bpf_insn insn_id, unsigned int opcode) +{ + if (insn_id == BPF_INS_INVALID) + return; + /* + * In eBPF mode, only these instructions have implicit registers access: + * - legacy ld{w,h,b,dw} * // w: r0 + * - exit // r: r0 + */ + if (EBPF_MODE(MI->csh->mode)) { + switch (insn_id) { + default: + break; + case BPF_INS_LDABSW: + case BPF_INS_LDABSH: + case BPF_INS_LDABSB: + case BPF_INS_LDINDW: + case BPF_INS_LDINDH: + case BPF_INS_LDINDB: + case BPF_INS_LDDW: + if (BPF_MODE(opcode) == BPF_MODE_ABS || + BPF_MODE(opcode) == BPF_MODE_IND) + map_add_implicit_write(MI, BPF_REG_R0); + break; + case BPF_INS_EXIT: + map_add_implicit_read(MI, BPF_REG_R0); + break; + } + return; + } + + /* cBPF mode */ + switch (BPF_CLASS(opcode)) { + default: + break; + case BPF_CLASS_LD: + map_add_implicit_write(MI, BPF_REG_A); + break; + case BPF_CLASS_LDX: + map_add_implicit_write(MI, BPF_REG_X); + break; + case BPF_CLASS_ST: + map_add_implicit_read(MI, BPF_REG_A); + break; + case BPF_CLASS_STX: + map_add_implicit_read(MI, BPF_REG_X); + break; + case BPF_CLASS_ALU: + map_add_implicit_read(MI, BPF_REG_A); + map_add_implicit_write(MI, BPF_REG_A); + break; + case BPF_CLASS_JMP: + if (insn_id != BPF_INS_JA) // except the unconditional jump + map_add_implicit_read(MI, BPF_REG_A); + break; + /* case BPF_CLASS_RET: */ + case BPF_CLASS_MISC: + if (insn_id == BPF_INS_TAX) { + map_add_implicit_read(MI, BPF_REG_A); + map_add_implicit_write(MI, BPF_REG_X); + } else { + map_add_implicit_read(MI, BPF_REG_X); + map_add_implicit_write(MI, BPF_REG_A); + } + break; + } +} +#endif + +static bool setFinalOpcode(MCInst *MI, const bpf_internal *bpf) +{ + bpf_insn id = BPF_INS_INVALID; +#ifndef CAPSTONE_DIET + cs_detail *detail; + + detail = get_detail(MI); +#endif + + const uint16_t opcode = bpf->op; + switch (BPF_CLASS(opcode)) { + default: // will never happen + break; + case BPF_CLASS_LD: + case BPF_CLASS_LDX: + if (EBPF_MODE(MI->csh->mode)) + id = op2insn_ld_ebpf(opcode); + else + id = op2insn_ld_cbpf(opcode); + add_group(MI, BPF_GRP_LOAD); + break; + case BPF_CLASS_ST: + case BPF_CLASS_STX: + id = op2insn_st(opcode, bpf->k); + add_group(MI, BPF_GRP_STORE); + break; + case BPF_CLASS_ALU: + id = op2insn_alu(opcode, bpf->offset, EBPF_MODE(MI->csh->mode)); + add_group(MI, BPF_GRP_ALU); + break; + case BPF_CLASS_JMP: + id = op2insn_jmp(opcode); +#ifndef CAPSTONE_DIET + if (id == BPF_INS_CALL || id == BPF_INS_CALLX) + add_group(MI, BPF_GRP_CALL); + else if (id == BPF_INS_EXIT) + add_group(MI, BPF_GRP_RETURN); + else + add_group(MI, BPF_GRP_JUMP); +#endif + break; + case BPF_CLASS_RET: + /* case BPF_CLASS_JMP32: */ + if (EBPF_MODE(MI->csh->mode)) { + id = op2insn_jmp(opcode); + add_group(MI, BPF_GRP_JUMP); + } else { + id = BPF_INS_RET; + add_group(MI, BPF_GRP_RETURN); + } + break; + // BPF_CLASS_MISC and BPF_CLASS_ALU64 have exactly same value + case BPF_CLASS_MISC: + /* case BPF_CLASS_ALU64: */ + if (EBPF_MODE(MI->csh->mode)) { + // ALU64 in eBPF + id = op2insn_alu(opcode, bpf->offset, true); + add_group(MI, BPF_GRP_ALU); + } else { + if (BPF_MISCOP(opcode) == BPF_MISCOP_TXA) + id = BPF_INS_TXA; + else + id = BPF_INS_TAX; + add_group(MI, BPF_GRP_MISC); + } + break; + } + + if (id == BPF_INS_INVALID) + return false; + + MCInst_setOpcodePub(MI, id); +#undef PUSH_GROUP + +#ifndef CAPSTONE_DIET + if (detail) { + update_regs_access(MI, detail, id, opcode); + } +#endif + return true; +} + bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len, - MCInst *instr, uint16_t *size, uint64_t address, void *info) + MCInst *instr, uint16_t *size, uint64_t address, + void *info) { - cs_struct *cs; bpf_internal *bpf; - cs = (cs_struct*)ud; - if (EBPF_MODE(cs)) - bpf = fetch_ebpf(cs, code, code_len); + if (EBPF_MODE(instr->csh->mode)) + bpf = fetch_ebpf(instr, code, code_len); else - bpf = fetch_cbpf(cs, code, code_len); + bpf = fetch_cbpf(instr, code, code_len); if (bpf == NULL) return false; - if (!getInstruction(cs, instr, bpf)) { + if (!getInstruction(instr, bpf) || !setFinalOpcode(instr, bpf)) { cs_mem_free(bpf); return false; } + MCInst_setOpcode(instr, bpf->op); *size = bpf->insn_size; cs_mem_free(bpf); diff --git a/arch/BPF/BPFDisassembler.h b/arch/BPF/BPFDisassembler.h index 9616b0816a..09ea9400c0 100644 --- a/arch/BPF/BPFDisassembler.h +++ b/arch/BPF/BPFDisassembler.h @@ -22,6 +22,7 @@ typedef struct bpf_internal { } bpf_internal; bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len, - MCInst *instr, uint16_t *size, uint64_t address, void *info); + MCInst *instr, uint16_t *size, uint64_t address, + void *info); #endif diff --git a/arch/BPF/BPFInstPrinter.c b/arch/BPF/BPFInstPrinter.c index 9c65a4adda..de993628ed 100644 --- a/arch/BPF/BPFInstPrinter.c +++ b/arch/BPF/BPFInstPrinter.c @@ -1,11 +1,14 @@ /* Capstone Disassembly Engine */ /* BPF Backend by david942j , 2019 */ +/* SPDX-FileCopyrightText: 2024 Roee Toledano */ +/* SPDX-License-Identifier: BSD-3 */ #include #include "BPFConstants.h" #include "BPFInstPrinter.h" #include "BPFMapping.h" +#include "../../Mapping.h" static cs_bpf_op *expand_bpf_operands(cs_bpf *bpf) { @@ -22,29 +25,34 @@ static void push_op_reg(cs_bpf *bpf, bpf_op_type val, uint8_t ac_mode) op->access = ac_mode; } -static void push_op_imm(cs_bpf *bpf, uint64_t val) +static void push_op_imm(cs_bpf *bpf, uint64_t val, const bool is_signed) { cs_bpf_op *op = expand_bpf_operands(bpf); op->type = BPF_OP_IMM; op->imm = val; + op->is_signed = is_signed; } -static void push_op_off(cs_bpf *bpf, uint32_t val) +static void push_op_off(cs_bpf *bpf, uint32_t val, const bool is_signed) { cs_bpf_op *op = expand_bpf_operands(bpf); op->type = BPF_OP_OFF; op->off = val; + op->is_signed = is_signed; } -static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val) +static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val, + const bool is_signed, const bool is_pkt) { cs_bpf_op *op = expand_bpf_operands(bpf); op->type = BPF_OP_MEM; op->mem.base = reg; op->mem.disp = val; + op->is_signed = is_signed; + op->is_pkt = is_pkt; } static void push_op_mmem(cs_bpf *bpf, uint32_t val) @@ -77,38 +85,61 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) unsigned mc_op_count = MCInst_getNumOperands(MI); MCOperand *op; MCOperand *op2; - unsigned i; bpf->op_count = 0; - if (BPF_CLASS(opcode) == BPF_CLASS_LD || BPF_CLASS(opcode) == BPF_CLASS_LDX) { + if (BPF_CLASS(opcode) == BPF_CLASS_LD || + BPF_CLASS(opcode) == BPF_CLASS_LDX) { switch (BPF_MODE(opcode)) { case BPF_MODE_IMM: - if (EBPF_MODE(MI->csh)) { - push_op_reg(bpf, MCOperand_getReg(MCInst_getOperand(MI, 0)), CS_AC_WRITE); - push_op_imm(bpf, MCOperand_getImm(MCInst_getOperand(MI, 1))); + if (EBPF_MODE(MI->csh->mode)) { + push_op_reg(bpf, + MCOperand_getReg( + MCInst_getOperand(MI, 0)), + CS_AC_WRITE); + push_op_imm(bpf, + MCOperand_getImm( + MCInst_getOperand(MI, 1)), + false); } else { - push_op_imm(bpf, MCOperand_getImm(MCInst_getOperand(MI, 0))); + push_op_imm(bpf, + MCOperand_getImm( + MCInst_getOperand(MI, 0)), + false); } break; case BPF_MODE_ABS: op = MCInst_getOperand(MI, 0); - push_op_mem(bpf, BPF_REG_INVALID, (uint32_t)MCOperand_getImm(op)); + push_op_mem(bpf, BPF_REG_INVALID, + (uint32_t)MCOperand_getImm(op), EBPF_MODE(MI->csh->mode), true); break; case BPF_MODE_IND: op = MCInst_getOperand(MI, 0); - op2 = MCInst_getOperand(MI, 1); - push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); + if (EBPF_MODE(MI->csh->mode)) + push_op_mem(bpf, MCOperand_getReg(op), 0x0, + true, true); + else { + op2 = MCInst_getOperand(MI, 1); + push_op_mem(bpf, MCOperand_getReg(op), + (uint32_t)MCOperand_getImm(op2), + false, true); + } break; case BPF_MODE_MEM: - if (EBPF_MODE(MI->csh)) { + if (EBPF_MODE(MI->csh->mode)) { /* ldx{w,h,b,dw} dst, [src+off] */ - push_op_reg(bpf, MCOperand_getReg(MCInst_getOperand(MI, 0)), CS_AC_WRITE); + push_op_reg(bpf, + MCOperand_getReg( + MCInst_getOperand(MI, 0)), + CS_AC_WRITE); op = MCInst_getOperand(MI, 1); op2 = MCInst_getOperand(MI, 2); - push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); - } - else { - push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0))); + push_op_mem(bpf, MCOperand_getReg(op), + (uint32_t)MCOperand_getImm(op2), + true, false); + } else { + push_op_mmem(bpf, + (uint32_t)MCOperand_getImm( + MCInst_getOperand(MI, 0))); } break; case BPF_MODE_LEN: @@ -118,14 +149,16 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) op = MCInst_getOperand(MI, 0); push_op_msh(bpf, (uint32_t)MCOperand_getImm(op)); break; - /* case BPF_MODE_XADD: // not exists */ + /* case BPF_MODE_XADD: // not exists */ } return; } - if (BPF_CLASS(opcode) == BPF_CLASS_ST || BPF_CLASS(opcode) == BPF_CLASS_STX) { - if (!EBPF_MODE(MI->csh)) { + if (BPF_CLASS(opcode) == BPF_CLASS_ST || + BPF_CLASS(opcode) == BPF_CLASS_STX) { + if (!EBPF_MODE(MI->csh->mode)) { // cBPF has only one case - st* M[k] - push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0))); + push_op_mmem(bpf, (uint32_t)MCOperand_getImm( + MCInst_getOperand(MI, 0))); return; } /* eBPF has two cases: @@ -135,47 +168,66 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) */ op = MCInst_getOperand(MI, 0); op2 = MCInst_getOperand(MI, 1); - push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); + push_op_mem(bpf, MCOperand_getReg(op), + (uint32_t)MCOperand_getImm(op2), true, false); + op = MCInst_getOperand(MI, 2); if (MCOperand_isImm(op)) - push_op_imm(bpf, MCOperand_getImm(op)); + push_op_imm(bpf, MCOperand_getImm(op), false); else if (MCOperand_isReg(op)) push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); return; } - if (BPF_CLASS(opcode) == BPF_CLASS_JMP) { - for (i = 0; i < mc_op_count; i++) { - op = MCInst_getOperand(MI, i); - if (MCOperand_isImm(op)) { - /* decide the imm is BPF_OP_IMM or BPF_OP_OFF type here */ - /* - * 1. ja +off - * 2. j {x,k}, +jt, +jf // cBPF - * 3. j dst_reg, {src_reg, k}, +off // eBPF - */ - if (BPF_OP(opcode) == BPF_JUMP_JA || - (!EBPF_MODE(MI->csh) && i >= 1) || - (EBPF_MODE(MI->csh) && i == 2)) - push_op_off(bpf, (uint32_t)MCOperand_getImm(op)); - else - push_op_imm(bpf, MCOperand_getImm(op)); - } - else if (MCOperand_isReg(op)) { - push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); + { + const bool is_jmp32 = EBPF_MODE(MI->csh->mode) && + (BPF_CLASS(opcode) == BPF_CLASS_JMP32); + if (BPF_CLASS(opcode) == BPF_CLASS_JMP || is_jmp32) { + for (size_t i = 0; i < mc_op_count; i++) { + op = MCInst_getOperand(MI, i); + if (MCOperand_isImm(op)) { + /* Decide if we're using IMM or OFF here (and if OFF, then signed or unsigned): + * + * 1. any jump/jump32 + signed off (not including exit/call and ja on jump32) // eBPF + * 2. exit/call/ja + k // eBPF + * 3. ja + unsigned off // cBPF (cBPF programs can only jump forwards) + * 4. any jump {x,k}, +jt, +jf // cBPF + * */ + + if ((BPF_OP(opcode) == BPF_JUMP_JA && + !is_jmp32) || + (!EBPF_MODE(MI->csh->mode) && + i >= 1) || + (EBPF_MODE(MI->csh->mode) && + i == 2)) + push_op_off( + bpf, + MCOperand_getImm(op), + EBPF_MODE( + MI->csh->mode)); + else + push_op_imm( + bpf, + MCOperand_getImm(op), + true); + } else if (MCOperand_isReg(op)) { + push_op_reg(bpf, MCOperand_getReg(op), + CS_AC_READ); + } } + return; } - return; } - if (!EBPF_MODE(MI->csh)) { + if (!EBPF_MODE(MI->csh->mode)) { /* In cBPF mode, all registers in operands are accessed as read */ - for (i = 0; i < mc_op_count; i++) { + for (size_t i = 0; i < mc_op_count; i++) { op = MCInst_getOperand(MI, i); if (MCOperand_isImm(op)) - push_op_imm(bpf, MCOperand_getImm(op)); + push_op_imm(bpf, MCOperand_getImm(op), false); else if (MCOperand_isReg(op)) - push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); + push_op_reg(bpf, MCOperand_getReg(op), + CS_AC_READ); } return; } @@ -193,15 +245,16 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) */ if (mc_op_count == 1) { op = MCInst_getOperand(MI, 0); - push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE); - } - else { // if (mc_op_count == 2) + push_op_reg(bpf, MCOperand_getReg(op), + CS_AC_READ | CS_AC_WRITE); + } else { // if (mc_op_count == 2) op = MCInst_getOperand(MI, 0); - push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE); + push_op_reg(bpf, MCOperand_getReg(op), + CS_AC_READ | CS_AC_WRITE); op = MCInst_getOperand(MI, 1); if (MCOperand_isImm(op)) - push_op_imm(bpf, MCOperand_getImm(op)); + push_op_imm(bpf, MCOperand_getImm(op), false); else if (MCOperand_isReg(op)) push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); } @@ -217,22 +270,60 @@ static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op) SStream_concat(O, BPF_reg_name((csh)MI->csh, op->reg)); break; case BPF_OP_IMM: - SStream_concat(O, "0x%" PRIx64, op->imm); + if (op->is_signed) + printInt32Hex(O, op->imm); + else + SStream_concat(O, "0x%" PRIx64, op->imm); break; case BPF_OP_OFF: - SStream_concat(O, "+0x%x", op->off); + if (op->is_signed) + printInt16HexOffset(O, op->off); + else + SStream_concat(O, "+0x%" PRIx32, op->off); break; case BPF_OP_MEM: SStream_concat(O, "["); - if (op->mem.base != BPF_REG_INVALID) - SStream_concat(O, BPF_reg_name((csh)MI->csh, op->mem.base)); - if (op->mem.disp != 0) { + + if (op->is_pkt && EBPF_MODE(MI->csh->mode)) { + SStream_concat(O, "skb"); + + if (op->mem.base != BPF_REG_INVALID) + SStream_concat(O, "+%s", + BPF_reg_name((csh)MI->csh, + op->mem.base)); + else { + if (op->is_signed) + printInt32HexOffset(O, op->mem.disp); + else + SStream_concat(O, "+0x%" PRIx32, + op->mem.disp); + } + } else { if (op->mem.base != BPF_REG_INVALID) - SStream_concat(O, "+"); - SStream_concat(O, "0x%x", op->mem.disp); + SStream_concat(O, BPF_reg_name((csh)MI->csh, + op->mem.base)); + if (op->mem.disp != 0) { + if (op->mem.base != BPF_REG_INVALID) { + // if operation is signed, then it always uses off, not k + if (op->is_signed) + printInt16HexOffset( + O, op->mem.disp); + else if (op->is_pkt) + SStream_concat(O, "+0x%" PRIx32, + op->mem.disp); + else + SStream_concat(O, "+0x%" PRIx16, + op->mem.disp); + } else + SStream_concat(O, "0x%" PRIx32, + op->mem.disp); + } + + if (op->mem.base == BPF_REG_INVALID && + op->mem.disp == 0) + SStream_concat(O, "0x0"); } - if (op->mem.base == BPF_REG_INVALID && op->mem.disp == 0) // special case - SStream_concat(O, "0x0"); + SStream_concat(O, "]"); break; case BPF_OP_MMEM: @@ -258,18 +349,12 @@ static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op) * */ void BPF_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) { - int i; - cs_insn insn; - cs_bpf bpf; + cs_bpf bpf = { 0 }; - insn.detail = NULL; /* set pubOpcode as instruction id */ - BPF_get_insn_id((cs_struct*)MI->csh, &insn, MCInst_getOpcode(MI)); - MCInst_setOpcodePub(MI, insn.id); - - SStream_concat(O, BPF_insn_name((csh)MI->csh, insn.id)); + SStream_concat(O, BPF_insn_name((csh)MI->csh, MCInst_getOpcodePub(MI))); convert_operands(MI, &bpf); - for (i = 0; i < bpf.op_count; i++) { + for (size_t i = 0; i < bpf.op_count; i++) { if (i == 0) SStream_concat(O, "\t"); else @@ -278,7 +363,7 @@ void BPF_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) } #ifndef CAPSTONE_DIET - if (MI->flat_insn->detail) { + if (detail_is_set(MI)) { MI->flat_insn->detail->bpf = bpf; } #endif diff --git a/arch/BPF/BPFMapping.c b/arch/BPF/BPFMapping.c index 17d3c55710..6b4e1f6df5 100644 --- a/arch/BPF/BPFMapping.c +++ b/arch/BPF/BPFMapping.c @@ -1,5 +1,7 @@ /* Capstone Disassembly Engine */ /* BPF Backend by david942j , 2019 */ +/* SPDX-FileCopyrightText: 2024 Roee Toledano */ +/* SPDX-License-Identifier: BSD-3 */ #include @@ -12,12 +14,9 @@ static const name_map group_name_maps[] = { { BPF_GRP_INVALID, NULL }, - { BPF_GRP_LOAD, "load" }, - { BPF_GRP_STORE, "store" }, - { BPF_GRP_ALU, "alu" }, - { BPF_GRP_JUMP, "jump" }, - { BPF_GRP_CALL, "call" }, - { BPF_GRP_RETURN, "return" }, + { BPF_GRP_LOAD, "load" }, { BPF_GRP_STORE, "store" }, + { BPF_GRP_ALU, "alu" }, { BPF_GRP_JUMP, "jump" }, + { BPF_GRP_CALL, "call" }, { BPF_GRP_RETURN, "return" }, { BPF_GRP_MISC, "misc" }, }; #endif @@ -35,99 +34,100 @@ const char *BPF_group_name(csh handle, unsigned int id) static const name_map insn_name_maps[BPF_INS_ENDING] = { { BPF_INS_INVALID, NULL }, - { BPF_INS_ADD, "add" }, - { BPF_INS_SUB, "sub" }, - { BPF_INS_MUL, "mul" }, - { BPF_INS_DIV, "div" }, - { BPF_INS_OR, "or" }, - { BPF_INS_AND, "and" }, - { BPF_INS_LSH, "lsh" }, - { BPF_INS_RSH, "rsh" }, - { BPF_INS_NEG, "neg" }, - { BPF_INS_MOD, "mod" }, - { BPF_INS_XOR, "xor" }, - { BPF_INS_MOV, "mov" }, + { BPF_INS_ADD, "add" }, { BPF_INS_SUB, "sub" }, + { BPF_INS_MUL, "mul" }, { BPF_INS_DIV, "div" }, + { BPF_INS_SDIV, "sdiv" }, { BPF_INS_OR, "or" }, + { BPF_INS_AND, "and" }, { BPF_INS_LSH, "lsh" }, + { BPF_INS_RSH, "rsh" }, { BPF_INS_NEG, "neg" }, + { BPF_INS_MOD, "mod" }, { BPF_INS_SMOD, "smod" }, + { BPF_INS_XOR, "xor" }, { BPF_INS_MOV, "mov" }, + { BPF_INS_MOVSB, "movsb" }, { BPF_INS_MOVSH, "movsh" }, { BPF_INS_ARSH, "arsh" }, - { BPF_INS_ADD64, "add64" }, - { BPF_INS_SUB64, "sub64" }, - { BPF_INS_MUL64, "mul64" }, - { BPF_INS_DIV64, "div64" }, - { BPF_INS_OR64, "or64" }, - { BPF_INS_AND64, "and64" }, - { BPF_INS_LSH64, "lsh64" }, - { BPF_INS_RSH64, "rsh64" }, - { BPF_INS_NEG64, "neg64" }, - { BPF_INS_MOD64, "mod64" }, - { BPF_INS_XOR64, "xor64" }, - { BPF_INS_MOV64, "mov64" }, - { BPF_INS_ARSH64, "arsh64" }, - - { BPF_INS_LE16, "le16" }, - { BPF_INS_LE32, "le32" }, - { BPF_INS_LE64, "le64" }, - { BPF_INS_BE16, "be16" }, - { BPF_INS_BE32, "be32" }, - { BPF_INS_BE64, "be64" }, - { BPF_INS_BSWAP16, "bswap16" }, - { BPF_INS_BSWAP32, "bswap32" }, + { BPF_INS_ADD64, "add64" }, { BPF_INS_SUB64, "sub64" }, + { BPF_INS_MUL64, "mul64" }, { BPF_INS_DIV64, "div64" }, + { BPF_INS_SDIV64, "sdiv64" }, { BPF_INS_OR64, "or64" }, + { BPF_INS_AND64, "and64" }, { BPF_INS_LSH64, "lsh64" }, + { BPF_INS_RSH64, "rsh64" }, { BPF_INS_NEG64, "neg64" }, + { BPF_INS_MOD64, "mod64" }, { BPF_INS_SMOD64, "smod64" }, + { BPF_INS_XOR64, "xor64" }, { BPF_INS_MOV64, "mov64" }, + { BPF_INS_MOVSB64, "movsb64" }, { BPF_INS_MOVSH64, "movsh64" }, + { BPF_INS_MOVSW64, "movsw64" }, { BPF_INS_ARSH64, "arsh64" }, + + { BPF_INS_LE16, "le16" }, { BPF_INS_LE32, "le32" }, + { BPF_INS_LE64, "le64" }, { BPF_INS_BE16, "be16" }, + { BPF_INS_BE32, "be32" }, { BPF_INS_BE64, "be64" }, + { BPF_INS_BSWAP16, "bswap16" }, { BPF_INS_BSWAP32, "bswap32" }, { BPF_INS_BSWAP64, "bswap64" }, - { BPF_INS_LDW, "ldw" }, - { BPF_INS_LDH, "ldh" }, - { BPF_INS_LDB, "ldb" }, - { BPF_INS_LDDW, "lddw" }, - { BPF_INS_LDXW, "ldxw" }, - { BPF_INS_LDXH, "ldxh" }, - { BPF_INS_LDXB, "ldxb" }, - { BPF_INS_LDXDW, "ldxdw" }, + { BPF_INS_LDW, "ldw" }, { BPF_INS_LDH, "ldh" }, + { BPF_INS_LDB, "ldb" }, { BPF_INS_LDDW, "lddw" }, + { BPF_INS_LDXW, "ldxw" }, { BPF_INS_LDXH, "ldxh" }, + { BPF_INS_LDXB, "ldxb" }, { BPF_INS_LDXDW, "ldxdw" }, + { BPF_INS_LDABSW, "ldabsw" }, { BPF_INS_LDABSH, "ldabsh" }, + { BPF_INS_LDABSB, "ldabsb" }, { BPF_INS_LDINDW, "ldindw" }, + { BPF_INS_LDINDH, "ldindh" }, { BPF_INS_LDINDB, "ldindb" }, + + { BPF_INS_STW, "stw" }, { BPF_INS_STH, "sth" }, + { BPF_INS_STB, "stb" }, { BPF_INS_STDW, "stdw" }, + { BPF_INS_STXW, "stxw" }, { BPF_INS_STXH, "stxh" }, + { BPF_INS_STXB, "stxb" }, { BPF_INS_STXDW, "stxdw" }, + { BPF_INS_XADDW, "xaddw" }, { BPF_INS_XADDDW, "xadddw" }, + + { BPF_INS_JA, "ja" }, { BPF_INS_JEQ, "jeq" }, + { BPF_INS_JGT, "jgt" }, { BPF_INS_JGE, "jge" }, + { BPF_INS_JSET, "jset" }, { BPF_INS_JNE, "jne" }, + { BPF_INS_JSGT, "jsgt" }, { BPF_INS_JSGE, "jsge" }, + { BPF_INS_CALL, "call" }, { BPF_INS_CALLX, "callx" }, + { BPF_INS_EXIT, "exit" }, { BPF_INS_JLT, "jlt" }, + { BPF_INS_JLE, "jle" }, { BPF_INS_JSLT, "jslt" }, + { BPF_INS_JSLE, "jsle" }, + + { BPF_INS_JAL, "jal" }, { BPF_INS_JEQ32, "jeq32" }, + { BPF_INS_JGT32, "jgt32" }, { BPF_INS_JGE32, "jge32" }, + { BPF_INS_JSET32, "jset32" }, { BPF_INS_JNE32, "jne32" }, + { BPF_INS_JSGT32, "jsgt32" }, { BPF_INS_JSGE32, "jsge32" }, + { BPF_INS_JLT32, "jlt32" }, { BPF_INS_JLE32, "jle32" }, + { BPF_INS_JSLT32, "jslt32" }, { BPF_INS_JSLE32, "jsle32" }, - { BPF_INS_STW, "stw" }, - { BPF_INS_STH, "sth" }, - { BPF_INS_STB, "stb" }, - { BPF_INS_STDW, "stdw" }, - { BPF_INS_STXW, "stxw" }, - { BPF_INS_STXH, "stxh" }, - { BPF_INS_STXB, "stxb" }, - { BPF_INS_STXDW, "stxdw" }, - { BPF_INS_XADDW, "xaddw" }, - { BPF_INS_XADDDW, "xadddw" }, + { BPF_INS_RET, "ret" }, - { BPF_INS_JMP, "jmp" }, - { BPF_INS_JEQ, "jeq" }, - { BPF_INS_JGT, "jgt" }, - { BPF_INS_JGE, "jge" }, - { BPF_INS_JSET, "jset" }, - { BPF_INS_JNE, "jne" }, - { BPF_INS_JSGT, "jsgt" }, - { BPF_INS_JSGE, "jsge" }, - { BPF_INS_CALL, "call" }, - { BPF_INS_CALLX, "callx" }, - { BPF_INS_EXIT, "exit" }, - { BPF_INS_JLT, "jlt" }, - { BPF_INS_JLE, "jle" }, - { BPF_INS_JSLT, "jslt" }, - { BPF_INS_JSLE, "jsle" }, + { BPF_INS_AADD, "aadd" }, { BPF_INS_AOR, "aor" }, + { BPF_INS_AAND, "aand" }, { BPF_INS_AXOR, "axor" }, + { BPF_INS_AFADD, "afadd" }, { BPF_INS_AFOR, "afor" }, + { BPF_INS_AFAND, "afand" }, { BPF_INS_AFXOR, "afxor" }, - { BPF_INS_RET, "ret" }, + { BPF_INS_AXCHG64, "axchg64" }, { BPF_INS_ACMPXCHG64, "acmpxchg64" }, + { BPF_INS_AADD64, "aadd64" }, { BPF_INS_AOR64, "aor64" }, + { BPF_INS_AAND64, "aand64" }, { BPF_INS_AXOR64, "axor64" }, + { BPF_INS_AFADD64, "afadd64" }, { BPF_INS_AFOR64, "afor64" }, + { BPF_INS_AFAND64, "afand64" }, { BPF_INS_AFXOR64, "afxor64" }, - { BPF_INS_TAX, "tax" }, - { BPF_INS_TXA, "txa" }, + { BPF_INS_TAX, "tax" }, { BPF_INS_TXA, "txa" }, }; #endif +inline bool BPF_getFeature(const cs_mode mode, const cs_mode feature) +{ + return (mode & feature); +} + const char *BPF_insn_name(csh handle, unsigned int id) { #ifndef CAPSTONE_DIET /* We have some special cases because 'ld' in cBPF is equivalent to 'ldw' * in eBPF, and we don't want to see 'ldw' appears in cBPF mode. */ - if (!EBPF_MODE(handle)) { + if (!EBPF_MODE(((cs_struct *)handle)->mode)) { switch (id) { - case BPF_INS_LD: return "ld"; - case BPF_INS_LDX: return "ldx"; - case BPF_INS_ST: return "st"; - case BPF_INS_STX: return "stx"; + case BPF_INS_LD: + return "ld"; + case BPF_INS_LDX: + return "ldx"; + case BPF_INS_ST: + return "st"; + case BPF_INS_STX: + return "stx"; } } return id2name(insn_name_maps, ARR_SIZE(insn_name_maps), id); @@ -139,14 +139,12 @@ const char *BPF_insn_name(csh handle, unsigned int id) const char *BPF_reg_name(csh handle, unsigned int reg) { #ifndef CAPSTONE_DIET - if (EBPF_MODE(handle)) { + if (EBPF_MODE(((cs_struct *)handle)->mode)) { if (reg < BPF_REG_R0 || reg > BPF_REG_R10) return NULL; - static const char reg_names[11][4] = { - "r0", "r1", "r2", "r3", "r4", - "r5", "r6", "r7", "r8", "r9", - "r10" - }; + static const char reg_names[11][4] = { "r0", "r1", "r2", "r3", + "r4", "r5", "r6", "r7", + "r8", "r9", "r10" }; return reg_names[reg - BPF_REG_R0]; } @@ -162,326 +160,30 @@ const char *BPF_reg_name(csh handle, unsigned int reg) #endif } -static bpf_insn op2insn_ld(unsigned opcode) +void BPF_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) { -#define CASE(c) case BPF_SIZE_##c: \ - if (BPF_CLASS(opcode) == BPF_CLASS_LD) \ - return BPF_INS_LD##c; \ - else \ - return BPF_INS_LDX##c; - - switch (BPF_SIZE(opcode)) { - CASE(W); - CASE(H); - CASE(B); - CASE(DW); - } -#undef CASE - - return BPF_INS_INVALID; -} - -static bpf_insn op2insn_st(unsigned opcode) -{ - /* - * - BPF_STX | BPF_XADD | BPF_{W,DW} - * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW} - */ - - if (opcode == (BPF_CLASS_STX | BPF_MODE_XADD | BPF_SIZE_W)) - return BPF_INS_XADDW; - if (opcode == (BPF_CLASS_STX | BPF_MODE_XADD | BPF_SIZE_DW)) - return BPF_INS_XADDDW; - - /* should be BPF_MEM */ -#define CASE(c) case BPF_SIZE_##c: \ - if (BPF_CLASS(opcode) == BPF_CLASS_ST) \ - return BPF_INS_ST##c; \ - else \ - return BPF_INS_STX##c; - switch (BPF_SIZE(opcode)) { - CASE(W); - CASE(H); - CASE(B); - CASE(DW); - } -#undef CASE - - return BPF_INS_INVALID; -} - -static bpf_insn op2insn_alu(unsigned opcode) -{ - /* Endian is a special case */ - if (BPF_OP(opcode) == BPF_ALU_END) { - if (BPF_CLASS(opcode) == BPF_CLASS_ALU64) { - switch (opcode ^ BPF_CLASS_ALU64 ^ BPF_ALU_END ^ BPF_SRC_LITTLE) { - case (16 << 4): - return BPF_INS_BSWAP16; - case (32 << 4): - return BPF_INS_BSWAP32; - case (64 << 4): - return BPF_INS_BSWAP64; - default: - return BPF_INS_INVALID; - } - } - - switch (opcode ^ BPF_CLASS_ALU ^ BPF_ALU_END) { - case BPF_SRC_LITTLE | (16 << 4): - return BPF_INS_LE16; - case BPF_SRC_LITTLE | (32 << 4): - return BPF_INS_LE32; - case BPF_SRC_LITTLE | (64 << 4): - return BPF_INS_LE64; - case BPF_SRC_BIG | (16 << 4): - return BPF_INS_BE16; - case BPF_SRC_BIG | (32 << 4): - return BPF_INS_BE32; - case BPF_SRC_BIG | (64 << 4): - return BPF_INS_BE64; - } - return BPF_INS_INVALID; - } - -#define CASE(c) case BPF_ALU_##c: \ - if (BPF_CLASS(opcode) == BPF_CLASS_ALU) \ - return BPF_INS_##c; \ - else \ - return BPF_INS_##c##64; - - switch (BPF_OP(opcode)) { - CASE(ADD); - CASE(SUB); - CASE(MUL); - CASE(DIV); - CASE(OR); - CASE(AND); - CASE(LSH); - CASE(RSH); - CASE(NEG); - CASE(MOD); - CASE(XOR); - CASE(MOV); - CASE(ARSH); - } -#undef CASE - - return BPF_INS_INVALID; -} - -static bpf_insn op2insn_jmp(unsigned opcode) -{ - if (opcode == (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) { - return BPF_INS_CALLX; - } - -#define CASE(c) case BPF_JUMP_##c: return BPF_INS_##c - switch (BPF_OP(opcode)) { - case BPF_JUMP_JA: - return BPF_INS_JMP; - CASE(JEQ); - CASE(JGT); - CASE(JGE); - CASE(JSET); - CASE(JNE); - CASE(JSGT); - CASE(JSGE); - CASE(CALL); - CASE(EXIT); - CASE(JLT); - CASE(JLE); - CASE(JSLT); - CASE(JSLE); - } -#undef CASE - - return BPF_INS_INVALID; -} - -#ifndef CAPSTONE_DIET -static void update_regs_access(cs_struct *ud, cs_detail *detail, - bpf_insn insn_id, unsigned int opcode) -{ - if (insn_id == BPF_INS_INVALID) - return; -#define PUSH_READ(r) do { \ - detail->regs_read[detail->regs_read_count] = r; \ - detail->regs_read_count++; \ - } while (0) -#define PUSH_WRITE(r) do { \ - detail->regs_write[detail->regs_write_count] = r; \ - detail->regs_write_count++; \ - } while (0) - /* - * In eBPF mode, only these instructions have implicit registers access: - * - legacy ld{w,h,b,dw} * // w: r0 - * - exit // r: r0 - */ - if (EBPF_MODE(ud)) { - switch (insn_id) { - default: - break; - case BPF_INS_LDW: - case BPF_INS_LDH: - case BPF_INS_LDB: - case BPF_INS_LDDW: - if (BPF_MODE(opcode) == BPF_MODE_ABS || BPF_MODE(opcode) == BPF_MODE_IND) { - PUSH_WRITE(BPF_REG_R0); - } - break; - case BPF_INS_EXIT: - PUSH_READ(BPF_REG_R0); - break; - } - return; - } - - /* cBPF mode */ - switch (BPF_CLASS(opcode)) { - default: - break; - case BPF_CLASS_LD: - PUSH_WRITE(BPF_REG_A); - break; - case BPF_CLASS_LDX: - PUSH_WRITE(BPF_REG_X); - break; - case BPF_CLASS_ST: - PUSH_READ(BPF_REG_A); - break; - case BPF_CLASS_STX: - PUSH_READ(BPF_REG_X); - break; - case BPF_CLASS_ALU: - PUSH_READ(BPF_REG_A); - PUSH_WRITE(BPF_REG_A); - break; - case BPF_CLASS_JMP: - if (insn_id != BPF_INS_JMP) // except the unconditional jump - PUSH_READ(BPF_REG_A); - break; - /* case BPF_CLASS_RET: */ - case BPF_CLASS_MISC: - if (insn_id == BPF_INS_TAX) { - PUSH_READ(BPF_REG_A); - PUSH_WRITE(BPF_REG_X); - } - else { - PUSH_READ(BPF_REG_X); - PUSH_WRITE(BPF_REG_A); - } - break; - } -} -#endif - -/* - * 1. Convert opcode(id) to BPF_INS_* - * 2. Set regs_read/regs_write/groups - */ -void BPF_get_insn_id(cs_struct *ud, cs_insn *insn, unsigned int opcode) -{ - // No need to care the mode (cBPF or eBPF) since all checks has be done in - // BPF_getInstruction, we can simply map opcode to BPF_INS_*. - bpf_insn id = BPF_INS_INVALID; -#ifndef CAPSTONE_DIET - cs_detail *detail; - bpf_insn_group grp; - - detail = insn->detail; - #define PUSH_GROUP(grp) do { \ - if (detail) { \ - detail->groups[detail->groups_count] = grp; \ - detail->groups_count++; \ - } \ - } while(0) -#else - #define PUSH_GROUP(grp) do {} while(0) -#endif - - switch (BPF_CLASS(opcode)) { - default: // will never happen - break; - case BPF_CLASS_LD: - case BPF_CLASS_LDX: - id = op2insn_ld(opcode); - PUSH_GROUP(BPF_GRP_LOAD); - break; - case BPF_CLASS_ST: - case BPF_CLASS_STX: - id = op2insn_st(opcode); - PUSH_GROUP(BPF_GRP_STORE); - break; - case BPF_CLASS_ALU: - id = op2insn_alu(opcode); - PUSH_GROUP(BPF_GRP_ALU); - break; - case BPF_CLASS_JMP: - id = op2insn_jmp(opcode); -#ifndef CAPSTONE_DIET - grp = BPF_GRP_JUMP; - if (id == BPF_INS_CALL || id == BPF_INS_CALLX) - grp = BPF_GRP_CALL; - else if (id == BPF_INS_EXIT) - grp = BPF_GRP_RETURN; - PUSH_GROUP(grp); -#endif - break; - case BPF_CLASS_RET: - id = BPF_INS_RET; - PUSH_GROUP(BPF_GRP_RETURN); - break; - // BPF_CLASS_MISC and BPF_CLASS_ALU64 have exactly same value - case BPF_CLASS_MISC: - /* case BPF_CLASS_ALU64: */ - if (EBPF_MODE(ud)) { - // ALU64 in eBPF - id = op2insn_alu(opcode); - PUSH_GROUP(BPF_GRP_ALU); - } - else { - if (BPF_MISCOP(opcode) == BPF_MISCOP_TXA) - id = BPF_INS_TXA; - else - id = BPF_INS_TAX; - PUSH_GROUP(BPF_GRP_MISC); - } - break; - } - - insn->id = id; -#undef PUSH_GROUP - -#ifndef CAPSTONE_DIET - if (detail) { - update_regs_access(ud, detail, id, opcode); - } -#endif + // Not used by BPF. Information is set after disassembly. } static void sort_and_uniq(cs_regs arr, uint8_t n, uint8_t *new_n) { /* arr is always a tiny (usually n < 3) array, * a simple O(n^2) sort is efficient enough. */ - int i; - int j; - int iMin; - int tmp; + size_t iMin; + size_t tmp; /* a modified selection sort for sorting and making unique */ - for (j = 0; j < n; j++) { + for (size_t j = 0; j < n; j++) { /* arr[iMin] will be min(arr[j .. n-1]) */ iMin = j; - for (i = j + 1; i < n; i++) { + for (size_t i = j + 1; i < n; i++) { if (arr[i] < arr[iMin]) iMin = i; } if (j != 0 && arr[iMin] == arr[j - 1]) { // duplicate ele found arr[iMin] = arr[n - 1]; --n; - } - else { + } else { tmp = arr[iMin]; arr[iMin] = arr[j]; arr[j] = tmp; @@ -490,9 +192,9 @@ static void sort_and_uniq(cs_regs arr, uint8_t n, uint8_t *new_n) *new_n = n; } -void BPF_reg_access(const cs_insn *insn, - cs_regs regs_read, uint8_t *regs_read_count, - cs_regs regs_write, uint8_t *regs_write_count) +void BPF_reg_access(const cs_insn *insn, cs_regs regs_read, + uint8_t *regs_read_count, cs_regs regs_write, + uint8_t *regs_write_count) { unsigned i; uint8_t read_count, write_count; @@ -502,8 +204,10 @@ void BPF_reg_access(const cs_insn *insn, write_count = insn->detail->regs_write_count; // implicit registers - memcpy(regs_read, insn->detail->regs_read, read_count * sizeof(insn->detail->regs_read[0])); - memcpy(regs_write, insn->detail->regs_write, write_count * sizeof(insn->detail->regs_write[0])); + memcpy(regs_read, insn->detail->regs_read, + read_count * sizeof(insn->detail->regs_read[0])); + memcpy(regs_write, insn->detail->regs_write, + write_count * sizeof(insn->detail->regs_write[0])); for (i = 0; i < bpf->op_count; i++) { const cs_bpf_op *op = &(bpf->operands[i]); diff --git a/arch/BPF/BPFMapping.h b/arch/BPF/BPFMapping.h index 1401ee8655..9e9aa36719 100644 --- a/arch/BPF/BPFMapping.h +++ b/arch/BPF/BPFMapping.h @@ -8,14 +8,16 @@ #include "../../cs_priv.h" -#define EBPF_MODE(ud) (((cs_struct*)ud)->mode & CS_MODE_BPF_EXTENDED) +bool BPF_getFeature(const cs_mode mode, const cs_mode feature); + +#define EBPF_MODE(mode) BPF_getFeature(mode, CS_MODE_BPF_EXTENDED) const char *BPF_group_name(csh handle, unsigned int id); const char *BPF_insn_name(csh handle, unsigned int id); const char *BPF_reg_name(csh handle, unsigned int reg); void BPF_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id); -void BPF_reg_access(const cs_insn *insn, - cs_regs regs_read, uint8_t *regs_read_count, - cs_regs regs_write, uint8_t *regs_write_count); +void BPF_reg_access(const cs_insn *insn, cs_regs regs_read, + uint8_t *regs_read_count, cs_regs regs_write, + uint8_t *regs_write_count); #endif diff --git a/bindings/python/capstone/bpf.py b/bindings/python/capstone/bpf.py index d6263bd3e2..4ea802d617 100644 --- a/bindings/python/capstone/bpf.py +++ b/bindings/python/capstone/bpf.py @@ -7,13 +7,13 @@ class BPFOpMem(ctypes.Structure): _fields_ = ( - ('base', ctypes.c_uint8), + ('base', ctypes.c_int), ('disp', ctypes.c_int32), ) class BPFOpValue(ctypes.Union): _fields_ = ( - ('reg', ctypes.c_uint8), + ('reg', ctypes.c_int), ('imm', ctypes.c_uint64), ('off', ctypes.c_uint32), ('mem', BPFOpMem), @@ -26,6 +26,8 @@ class BPFOp(ctypes.Structure): _fields_ = ( ('type', ctypes.c_uint), ('value', BPFOpValue), + ('is_signed', ctypes.c_bool), + ('is_pkt', ctypes.c_bool), ('access', ctypes.c_uint8), ) @@ -57,7 +59,6 @@ def msh(self): def ext(self): return self.value.ext - class CsBPF(ctypes.Structure): _fields_ = ( ('op_count', ctypes.c_uint8), diff --git a/bindings/python/capstone/bpf_const.py b/bindings/python/capstone/bpf_const.py index 88bc5a45fe..31d782b7b9 100644 --- a/bindings/python/capstone/bpf_const.py +++ b/bindings/python/capstone/bpf_const.py @@ -83,7 +83,7 @@ BPF_INS_STXDW = 51 BPF_INS_XADDW = 52 BPF_INS_XADDDW = 53 -BPF_INS_JMP = 54 +BPF_INS_JA = 54 BPF_INS_JEQ = 55 BPF_INS_JGT = 56 BPF_INS_JGE = 57 diff --git a/bindings/python/cstest_py/src/cstest_py/details.py b/bindings/python/cstest_py/src/cstest_py/details.py index 9069fb5968..18e635eda1 100644 --- a/bindings/python/cstest_py/src/cstest_py/details.py +++ b/bindings/python/cstest_py/src/cstest_py/details.py @@ -1192,6 +1192,10 @@ def test_expected_bpf(actual: CsInsn, expected: dict) -> bool: return False if not compare_enum(aop.access, eop.get("access"), "access"): return False + if not compare_tbool(aop.is_pkt, eop.get("is_pkt"), "is_pkt"): + return False + if not compare_tbool(aop.is_signed, eop.get("is_signed"), "is_signed"): + return False if aop.type == BPF_OP_REG: if not compare_reg(actual, aop.reg, eop.get("reg"), "reg"): diff --git a/docs/cs_v6_release_guide.md b/docs/cs_v6_release_guide.md index 6f470eedc2..edfad5d818 100644 --- a/docs/cs_v6_release_guide.md +++ b/docs/cs_v6_release_guide.md @@ -176,6 +176,14 @@ Nonetheless, we hope this additional information is useful to you. - Architecture support was added (based on LLVM-18). - Support for `LITBASE`. Set the `LITBASE` with `cs_option(handle, CS_OPT_LITBASE, litbase_value)`. +**BPF** + +- Added support for eBPF `ATOMIC` class instructions (using Linux mnemonics, not GNU ones. E.g. `acmpxchg64` instead of `axchg`) +- Added support for eBPF signed `ALU` class instructions (`sdiv`, `smod`, `movs` variants. E.g. `smod r9, 0xc9d1d20b`) +- Added support for eBPF `JMP32` class instructions (E.g. `jslt32 r7, -0xa46e0bd, -0x33f1`) +- Updated the syntax for eBPF legacy packet instructions (similar to LLVM mnemonics, not GNU ones (E.g. `ldabsw [skb-0x8]`). `skb` is the socket buffer. +- Corrected the signedness interpretation of `immidiate` and `offset` operands + **UX** - Instruction alias (see below). diff --git a/include/capstone/bpf.h b/include/capstone/bpf.h index 12c8d1d0ea..713c67778f 100644 --- a/include/capstone/bpf.h +++ b/include/capstone/bpf.h @@ -1,5 +1,7 @@ /* Capstone Disassembly Engine */ /* BPF Backend by david942j , 2019 */ +/* SPDX-FileCopyrightText: 2024 Roee Toledano */ +/* SPDX-License-Identifier: BSD-3 */ #ifndef CAPSTONE_BPF_H #define CAPSTONE_BPF_H @@ -11,9 +13,10 @@ extern "C" { #include "platform.h" #ifdef _MSC_VER -#pragma warning(disable:4201) +#pragma warning(disable : 4201) #endif +#define NUM_BPF_OPS 3 /// Operand type for instruction's operands typedef enum bpf_op_type { BPF_OP_INVALID = 0, @@ -22,9 +25,9 @@ typedef enum bpf_op_type { BPF_OP_IMM, BPF_OP_OFF, BPF_OP_MEM, - BPF_OP_MMEM, ///< M[k] in cBPF - BPF_OP_MSH, ///< corresponds to cBPF's BPF_MSH mode - BPF_OP_EXT, ///< cBPF's extension (not eBPF) + BPF_OP_MMEM, ///< M[k] in cBPF + BPF_OP_MSH, ///< corresponds to cBPF's BPF_MSH mode + BPF_OP_EXT, ///< cBPF's extension (not eBPF) } bpf_op_type; /// BPF registers @@ -54,8 +57,8 @@ typedef enum bpf_reg { /// Instruction's operand referring to memory /// This is associated with BPF_OP_MEM operand type above typedef struct bpf_op_mem { - bpf_reg base; ///< base register - uint32_t disp; ///< offset value + bpf_reg base; ///< base register + uint32_t disp; ///< offset value } bpf_op_mem; typedef enum bpf_ext_type { @@ -71,13 +74,15 @@ typedef struct cs_bpf_op { uint8_t reg; ///< register value for REG operand uint64_t imm; ///< immediate value IMM operand uint32_t off; ///< offset value, used in jump & call - bpf_op_mem mem; ///< base/disp value for MEM operand + bpf_op_mem mem; ///< base/disp value for MEM operand /* cBPF only */ - uint32_t mmem; ///< M[k] in cBPF - uint32_t msh; ///< corresponds to cBPF's BPF_MSH mode - uint32_t ext; ///< cBPF's extension (not eBPF) + uint32_t mmem; ///< M[k] in cBPF + uint32_t msh; ///< corresponds to cBPF's BPF_MSH mode + uint32_t ext; ///< cBPF's extension (not eBPF) }; + bool is_signed; ///< is this operand signed? It is set for memory, immediate and offset operands. + bool is_pkt; ///< is this operand referring to packet data? It is set for memory operands. /// How is this operand accessed? (READ, WRITE or READ|WRITE) /// This field is combined of cs_ac_type. /// NOTE: this field is irrelevant if engine is compiled in DIET mode. @@ -99,29 +104,38 @@ typedef enum bpf_insn { BPF_INS_SUB, BPF_INS_MUL, BPF_INS_DIV, + BPF_INS_SDIV, BPF_INS_OR, BPF_INS_AND, BPF_INS_LSH, BPF_INS_RSH, BPF_INS_NEG, BPF_INS_MOD, + BPF_INS_SMOD, BPF_INS_XOR, - BPF_INS_MOV, ///< eBPF only - BPF_INS_ARSH, ///< eBPF only + BPF_INS_MOV, ///< eBPF only + BPF_INS_MOVSB, ///< eBPF only + BPF_INS_MOVSH, ///< eBPF only + BPF_INS_ARSH, ///< eBPF only ///< ALU64, eBPF only BPF_INS_ADD64, BPF_INS_SUB64, BPF_INS_MUL64, BPF_INS_DIV64, + BPF_INS_SDIV64, BPF_INS_OR64, BPF_INS_AND64, BPF_INS_LSH64, BPF_INS_RSH64, BPF_INS_NEG64, BPF_INS_MOD64, + BPF_INS_SMOD64, BPF_INS_XOR64, BPF_INS_MOV64, + BPF_INS_MOVSB64, + BPF_INS_MOVSH64, + BPF_INS_MOVSW64, BPF_INS_ARSH64, ///< Byteswap, eBPF only @@ -136,14 +150,21 @@ typedef enum bpf_insn { BPF_INS_BSWAP64, ///< Load - BPF_INS_LDW, ///< eBPF only + BPF_INS_LDW, ///< eBPF only BPF_INS_LDH, BPF_INS_LDB, - BPF_INS_LDDW, ///< eBPF only: load 64-bit imm - BPF_INS_LDXW, ///< eBPF only - BPF_INS_LDXH, ///< eBPF only - BPF_INS_LDXB, ///< eBPF only - BPF_INS_LDXDW, ///< eBPF only + BPF_INS_LDDW, ///< eBPF only: load 64-bit imm + BPF_INS_LDXW, ///< eBPF only + BPF_INS_LDXH, ///< eBPF only + BPF_INS_LDXB, ///< eBPF only + BPF_INS_LDXDW, ///< eBPF only + ///< Packet data access + BPF_INS_LDABSW, ///< eBPF only + BPF_INS_LDABSH, ///< eBPF only + BPF_INS_LDABSB, ///< eBPF only + BPF_INS_LDINDW, ///< eBPF only + BPF_INS_LDINDH, ///< eBPF only + BPF_INS_LDINDB, ///< eBPF only ///< Store BPF_INS_STW, ///< eBPF only @@ -155,28 +176,64 @@ typedef enum bpf_insn { BPF_INS_STXB, ///< eBPF only BPF_INS_STXDW, ///< eBPF only BPF_INS_XADDW, ///< eBPF only - BPF_INS_XADDDW, ///< eBPF only + BPF_INS_XADDDW, ///< eBPF only ///< Jump - BPF_INS_JMP, + BPF_INS_JA, BPF_INS_JEQ, BPF_INS_JGT, BPF_INS_JGE, BPF_INS_JSET, - BPF_INS_JNE, ///< eBPF only - BPF_INS_JSGT, ///< eBPF only - BPF_INS_JSGE, ///< eBPF only - BPF_INS_CALL, ///< eBPF only - BPF_INS_CALLX, ///< eBPF only - BPF_INS_EXIT, ///< eBPF only - BPF_INS_JLT, ///< eBPF only - BPF_INS_JLE, ///< eBPF only - BPF_INS_JSLT, ///< eBPF only - BPF_INS_JSLE, ///< eBPF only + BPF_INS_JNE, ///< eBPF only + BPF_INS_JSGT, ///< eBPF only + BPF_INS_JSGE, ///< eBPF only + BPF_INS_CALL, ///< eBPF only + BPF_INS_CALLX, ///< eBPF only + BPF_INS_EXIT, ///< eBPF only + BPF_INS_JLT, ///< eBPF only + BPF_INS_JLE, ///< eBPF only + BPF_INS_JSLT, ///< eBPF only + BPF_INS_JSLE, ///< eBPF only + + ///< Jump32, eBPF only + BPF_INS_JAL, + BPF_INS_JEQ32, + BPF_INS_JGT32, + BPF_INS_JGE32, + BPF_INS_JSET32, + BPF_INS_JNE32, + BPF_INS_JSGT32, + BPF_INS_JSGE32, + BPF_INS_JLT32, + BPF_INS_JLE32, + BPF_INS_JSLT32, + BPF_INS_JSLE32, ///< Return, cBPF only BPF_INS_RET, + ///< Atomic, eBPF only + BPF_INS_AADD, + BPF_INS_AOR, + BPF_INS_AAND, + BPF_INS_AXOR, + BPF_INS_AFADD, + BPF_INS_AFOR, + BPF_INS_AFAND, + BPF_INS_AFXOR, + + ///< Atomic 64-bit, eBPF only + BPF_INS_AXCHG64, + BPF_INS_ACMPXCHG64, + BPF_INS_AADD64, + BPF_INS_AOR64, + BPF_INS_AAND64, + BPF_INS_AXOR64, + BPF_INS_AFADD64, + BPF_INS_AFOR64, + BPF_INS_AFAND64, + BPF_INS_AFXOR64, + ///< Misc, cBPF only BPF_INS_TAX, BPF_INS_TXA, @@ -184,10 +241,10 @@ typedef enum bpf_insn { BPF_INS_ENDING, // alias instructions - BPF_INS_LD = BPF_INS_LDW, ///< cBPF only - BPF_INS_LDX = BPF_INS_LDXW, ///< cBPF only - BPF_INS_ST = BPF_INS_STW, ///< cBPF only - BPF_INS_STX = BPF_INS_STXW, ///< cBPF only + BPF_INS_LD = BPF_INS_LDW, ///< cBPF only + BPF_INS_LDX = BPF_INS_LDXW, ///< cBPF only + BPF_INS_ST = BPF_INS_STW, ///< cBPF only + BPF_INS_STX = BPF_INS_STXW, ///< cBPF only } bpf_insn; /// Group of BPF instructions diff --git a/suite/cstest/include/test_compare.h b/suite/cstest/include/test_compare.h index 8850d2c1b4..516ea9ac42 100644 --- a/suite/cstest/include/test_compare.h +++ b/suite/cstest/include/test_compare.h @@ -18,6 +18,9 @@ /// > 0 => true typedef int32_t tbool; +#define CYAML_FIELD_TBOOL(name, flags, type, member) \ + CYAML_FIELD_INT(name, flags, type, member) + /// Compares the @actual bool against the @expected tbool: /// It returns with @ret_val, if expected is set but the values mismatch. #define compare_tbool_ret(actual, expected, ret_val) \ diff --git a/suite/cstest/include/test_detail_bpf.h b/suite/cstest/include/test_detail_bpf.h index 2dd3658d5e..b15d472442 100644 --- a/suite/cstest/include/test_detail_bpf.h +++ b/suite/cstest/include/test_detail_bpf.h @@ -20,6 +20,8 @@ typedef struct { char *ext; char *mem_base; uint32_t mem_disp; + tbool is_pkt; + tbool is_signed; } TestDetailBPFOp; static const cyaml_schema_field_t test_detail_bpf_op_mapping_schema[] = { @@ -30,17 +32,21 @@ static const cyaml_schema_field_t test_detail_bpf_op_mapping_schema[] = { TestDetailBPFOp, access, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("reg", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, TestDetailBPFOp, reg, 0, CYAML_UNLIMITED), - CYAML_FIELD_INT("imm", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, imm), - CYAML_FIELD_INT("off", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, off), - CYAML_FIELD_INT("mmem", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, mmem), - CYAML_FIELD_INT("msh", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, msh), + CYAML_FIELD_UINT("imm", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, imm), + CYAML_FIELD_UINT("off", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, off), + CYAML_FIELD_UINT("mmem", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, mmem), + CYAML_FIELD_UINT("msh", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, msh), CYAML_FIELD_STRING_PTR("ext", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, TestDetailBPFOp, ext, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("mem_base", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, TestDetailBPFOp, mem_base, 0, CYAML_UNLIMITED), - CYAML_FIELD_INT("mem_disp", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, + CYAML_FIELD_UINT("mem_disp", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, mem_disp), + CYAML_FIELD_TBOOL("is_pkt", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, + is_pkt), + CYAML_FIELD_TBOOL("is_signed", CYAML_FLAG_OPTIONAL, TestDetailBPFOp, + is_signed), CYAML_FIELD_END }; diff --git a/suite/cstest/src/test_detail_bpf.c b/suite/cstest/src/test_detail_bpf.c index e84ee541b6..ad400c9c08 100644 --- a/suite/cstest/src/test_detail_bpf.c +++ b/suite/cstest/src/test_detail_bpf.c @@ -1,9 +1,9 @@ // Copyright © 2024 Rot127 // SPDX-License-Identifier: BSD-3 -#include "capstone/bpf.h" #include "test_compare.h" #include "test_detail_bpf.h" +#include #include #include #include @@ -61,6 +61,8 @@ TestDetailBPFOp *test_detail_bpf_op_clone(const TestDetailBPFOp *op) clone->ext = op->ext ? strdup(op->ext) : NULL; clone->mem_base = op->mem_base ? strdup(op->mem_base) : NULL; clone->mem_disp = op->mem_disp; + clone->is_pkt = op->is_pkt; + clone->is_signed = op->is_signed; return clone; } @@ -89,6 +91,8 @@ bool test_expected_bpf(csh *handle, const cs_bpf *actual, TestDetailBPFOp *eop = expected->operands[i]; compare_enum_ret(op->type, eop->type, false); compare_enum_ret(op->access, eop->access, false); + compare_tbool_ret(op->is_pkt, eop->is_pkt, false); + compare_tbool_ret(op->is_signed, eop->is_signed, false); switch (op->type) { default: fprintf(stderr, diff --git a/tests/MC/BPF/classic-all.yaml b/tests/MC/BPF/classic-all.yaml index 6502492e51..b1861a8bd9 100644 --- a/tests/MC/BPF/classic-all.yaml +++ b/tests/MC/BPF/classic-all.yaml @@ -34,7 +34,7 @@ test_cases: expected: insns: - - asm_text: "jmp +0xd52cc837" + asm_text: "ja +0xd52cc837" - input: bytes: [ 0x06, 0x00, 0xa7, 0x84, 0x25, 0x40, 0x28, 0x1c ] diff --git a/tests/MC/BPF/extended-all.yaml b/tests/MC/BPF/extended-all.yaml index 8140cac332..823aa5bdef 100644 --- a/tests/MC/BPF/extended-all.yaml +++ b/tests/MC/BPF/extended-all.yaml @@ -16,7 +16,7 @@ test_cases: expected: insns: - - asm_text: "jmp +0xb071" + asm_text: "ja -0x4f8f" - input: bytes: [ 0x07, 0x76, 0x01, 0x28, 0xc4, 0x09, 0xfe, 0x8b ] @@ -115,7 +115,7 @@ test_cases: expected: insns: - - asm_text: "ldw [0xca1a41da]" + asm_text: "ldabsw [skb-0x35e5be26]" - input: bytes: [ 0x24, 0xb6, 0x69, 0x66, 0xe3, 0xef, 0xec, 0x25 ] @@ -151,7 +151,7 @@ test_cases: expected: insns: - - asm_text: "ldh [0xd0b48e8]" + asm_text: "ldabsh [skb+0xd0b48e8]" - input: bytes: [ 0x2c, 0x78, 0x03, 0xf6, 0x29, 0x29, 0x15, 0xfc ] @@ -169,7 +169,7 @@ test_cases: expected: insns: - - asm_text: "jgt r8, r1, +0xfd5b" + asm_text: "jgt r8, r1, -0x2a5" - input: bytes: [ 0x2f, 0x77, 0xc7, 0xa4, 0x4c, 0x32, 0x73, 0x2a ] @@ -187,10 +187,10 @@ test_cases: expected: insns: - - asm_text: "ldb [0x4b7c6685]" + asm_text: "ldabsb [skb+0x4b7c6685]" - input: - bytes: [ 0x34, 0x46, 0x49, 0x33, 0xe1, 0x72, 0xd4, 0xcb ] + bytes: [ 0x34, 0x46, 0x00, 0x00, 0xe1, 0x72, 0xd4, 0xcb ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -205,10 +205,10 @@ test_cases: expected: insns: - - asm_text: "jge r5, 0x3da1375b, +0xb942" + asm_text: "jge r5, 0x3da1375b, -0x46be" - input: - bytes: [ 0x37, 0x84, 0xd8, 0xba, 0x3b, 0x84, 0x55, 0x1f ] + bytes: [ 0x37, 0x84, 0x00, 0x00, 0x3b, 0x84, 0x55, 0x1f ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -217,25 +217,7 @@ test_cases: asm_text: "div64 r4, 0x1f55843b" - input: - bytes: [ 0x38, 0x8e, 0x3f, 0xd7, 0x1c, 0x3e, 0x3a, 0x7b ] - arch: "CS_ARCH_BPF" - options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] - expected: - insns: - - - asm_text: "lddw [0x7b3a3e1c]" - - - input: - bytes: [ 0x3d, 0x1a, 0xc3, 0x9b, 0x88, 0xa2, 0x3f, 0x65 ] - arch: "CS_ARCH_BPF" - options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] - expected: - insns: - - - asm_text: "jge r10, r1, +0x9bc3" - - - input: - bytes: [ 0x3f, 0x36, 0x99, 0x32, 0x7e, 0x07, 0x59, 0x7a ] + bytes: [ 0x3f, 0x36, 0x00, 0x00, 0x7e, 0x07, 0x59, 0x7a ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -250,7 +232,7 @@ test_cases: expected: insns: - - asm_text: "ldw [r9+0xc4d7e76b]" + asm_text: "ldindw [skb+r9]" - input: bytes: [ 0x44, 0x16, 0xf7, 0x98, 0xf7, 0x02, 0x92, 0x94 ] @@ -268,7 +250,7 @@ test_cases: expected: insns: - - asm_text: "jset r2, 0x1e2de714, +0xf2a2" + asm_text: "jset r2, 0x1e2de714, -0xd5e" - input: bytes: [ 0x47, 0x36, 0xf4, 0xd5, 0xbe, 0x04, 0x58, 0x4d ] @@ -286,7 +268,7 @@ test_cases: expected: insns: - - asm_text: "ldh [r7+0xd5a0eeb]" + asm_text: "ldindh [skb+r7]" - input: bytes: [ 0x4c, 0x81, 0x0a, 0x66, 0xfc, 0x32, 0x61, 0xc4 ] @@ -322,7 +304,7 @@ test_cases: expected: insns: - - asm_text: "ldb [r3+0x6cd17004]" + asm_text: "ldindb [skb+r3]" - input: bytes: [ 0x54, 0x40, 0x0a, 0x6a, 0x4a, 0xe8, 0xab, 0xfb ] @@ -340,7 +322,7 @@ test_cases: expected: insns: - - asm_text: "jne r9, 0x96c8bc90, +0x80a3" + asm_text: "jne r9, 0x96c8bc90, -0x7f5d" - input: bytes: [ 0x57, 0x30, 0x12, 0xe9, 0x7c, 0x06, 0x82, 0x27 ] @@ -350,15 +332,6 @@ test_cases: insns: - asm_text: "and64 r0, 0x2782067c" - - - input: - bytes: [ 0x58, 0x6d, 0xf1, 0x05, 0xd3, 0x50, 0x4b, 0xc0 ] - arch: "CS_ARCH_BPF" - options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] - expected: - insns: - - - asm_text: "lddw [r6+0xc04b50d3]" - input: bytes: [ 0x5c, 0x02, 0x95, 0xb2, 0xbd, 0x3f, 0x38, 0x37 ] @@ -394,7 +367,7 @@ test_cases: expected: insns: - - asm_text: "ldxw r8, [r2+0xedb2]" + asm_text: "ldxw r8, [r2-0x124e]" - input: bytes: [ 0x62, 0xa5, 0xdf, 0xe0, 0x14, 0x7d, 0x95, 0x78 ] @@ -403,7 +376,7 @@ test_cases: expected: insns: - - asm_text: "stw [r5+0xe0df], 0x78957d14" + asm_text: "stw [r5-0x1f21], 0x78957d14" - input: bytes: [ 0x63, 0x77, 0x2f, 0xcf, 0x76, 0xb7, 0xd3, 0xfa ] @@ -412,7 +385,7 @@ test_cases: expected: insns: - - asm_text: "stxw [r7+0xcf2f], r7" + asm_text: "stxw [r7-0x30d1], r7" - input: bytes: [ 0x64, 0x68, 0xc1, 0xf4, 0x88, 0x92, 0xd2, 0xeb ] @@ -430,7 +403,7 @@ test_cases: expected: insns: - - asm_text: "jsgt r8, 0xf88fbe87, +0xe197" + asm_text: "jsgt r8, 0xf88fbe87, -0x1e69" - input: bytes: [ 0x67, 0x00, 0xd7, 0xc0, 0x05, 0xb0, 0xf6, 0x74 ] @@ -448,7 +421,7 @@ test_cases: expected: insns: - - asm_text: "ldxh r4, [r1+0x8ec7]" + asm_text: "ldxh r4, [r1-0x7139]" - input: bytes: [ 0x6a, 0xb5, 0xbc, 0x8c, 0x4f, 0x5c, 0x94, 0x01 ] @@ -457,7 +430,7 @@ test_cases: expected: insns: - - asm_text: "sth [r5+0x8cbc], 0x1945c4f" + asm_text: "sth [r5-0x7344], 0x1945c4f" - input: bytes: [ 0x6b, 0x34, 0x58, 0xf5, 0xc8, 0x27, 0x9e, 0x14 ] @@ -466,7 +439,7 @@ test_cases: expected: insns: - - asm_text: "stxh [r4+0xf558], r3" + asm_text: "stxh [r4-0xaa8], r3" - input: bytes: [ 0x6c, 0x21, 0x10, 0x48, 0x01, 0x3e, 0x6e, 0xf8 ] @@ -484,7 +457,7 @@ test_cases: expected: insns: - - asm_text: "jsgt r8, r3, +0xe369" + asm_text: "jsgt r8, r3, -0x1c97" - input: bytes: [ 0x6f, 0x64, 0x49, 0xd6, 0x07, 0xa9, 0x93, 0x13 ] @@ -502,7 +475,7 @@ test_cases: expected: insns: - - asm_text: "ldxb r0, [r10+0xfbeb]" + asm_text: "ldxb r0, [r10-0x415]" - input: bytes: [ 0x72, 0xe2, 0xc1, 0x1b, 0x25, 0x2f, 0x4a, 0xdc ] @@ -620,6 +593,15 @@ test_cases: insns: - asm_text: "call 0x5dbd3d83" + - + input: + bytes: [ 0x85, 0x00, 0x00, 0xe2, 0x83, 0x3d, 0xbd, 0xfd ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "call -0x242c27d" - input: bytes: [ 0x87, 0xf5, 0x2b, 0xbe, 0xa9, 0xc7, 0x31, 0xa3 ] @@ -631,7 +613,7 @@ test_cases: asm_text: "neg64 r5" - input: - bytes: [ 0x94, 0x39, 0x0d, 0xdc, 0x0b, 0xd2, 0xd1, 0xc9 ] + bytes: [ 0x94, 0x39, 0x00, 0x00, 0x0b, 0xd2, 0xd1, 0xc9 ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -649,7 +631,7 @@ test_cases: asm_text: "exit" - input: - bytes: [ 0x97, 0xc8, 0xa6, 0x75, 0xd2, 0x09, 0x98, 0x09 ] + bytes: [ 0x97, 0xc8, 0x00, 0x00, 0xd2, 0x09, 0x98, 0x09 ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -658,7 +640,7 @@ test_cases: asm_text: "mod64 r8, 0x99809d2" - input: - bytes: [ 0x9c, 0x96, 0xe7, 0x16, 0x0f, 0x69, 0x13, 0x90 ] + bytes: [ 0x9c, 0x96, 0x00, 0x00, 0x0f, 0x69, 0x13, 0x90 ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -667,7 +649,7 @@ test_cases: asm_text: "mod r6, r9" - input: - bytes: [ 0x9f, 0x35, 0x5a, 0x59, 0xd6, 0x70, 0xd9, 0x5e ] + bytes: [ 0x9f, 0x35, 0x00, 0x00, 0xd6, 0x70, 0xd9, 0x5e ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -721,7 +703,7 @@ test_cases: asm_text: "xor64 r1, r4" - input: - bytes: [ 0xb4, 0xa1, 0x9c, 0x78, 0xf9, 0x3f, 0x77, 0x1f ] + bytes: [ 0xb4, 0xa1, 0x00, 0x00, 0xf9, 0x3f, 0x77, 0x1f ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -739,7 +721,7 @@ test_cases: asm_text: "jle r2, 0x33fc3349, +0x5a5d" - input: - bytes: [ 0xb7, 0x70, 0x59, 0x4d, 0x5b, 0x52, 0x2a, 0x99 ] + bytes: [ 0xb7, 0x70, 0x00, 0x00, 0x5b, 0x52, 0x2a, 0x99 ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -748,7 +730,7 @@ test_cases: asm_text: "mov64 r0, 0x992a525b" - input: - bytes: [ 0xbc, 0x72, 0x3e, 0x6c, 0xc9, 0x8a, 0x56, 0xd6 ] + bytes: [ 0xbc, 0x72, 0x00, 0x00, 0xc9, 0x8a, 0x56, 0xd6 ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -763,10 +745,10 @@ test_cases: expected: insns: - - asm_text: "jle r9, r1, +0xe880" + asm_text: "jle r9, r1, -0x1780" - input: - bytes: [ 0xbf, 0x86, 0x55, 0x58, 0xb2, 0x6d, 0x14, 0x03 ] + bytes: [ 0xbf, 0x86, 0x00, 0x00, 0xb2, 0x6d, 0x14, 0x03 ] arch: "CS_ARCH_BPF" options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] expected: @@ -790,7 +772,7 @@ test_cases: expected: insns: - - asm_text: "jslt r2, 0x4fcec0ba, +0xe4eb" + asm_text: "jslt r2, 0x4fcec0ba, -0x1b15" - input: bytes: [ 0xc7, 0xe8, 0xba, 0xff, 0x1f, 0xef, 0xc0, 0x88 ] @@ -817,7 +799,7 @@ test_cases: expected: insns: - - asm_text: "jslt r0, r9, +0x8867" + asm_text: "jslt r0, r9, -0x7799" - input: bytes: [ 0xcf, 0x82, 0xe1, 0xcd, 0xbe, 0xc3, 0x2d, 0x7c ] @@ -844,7 +826,7 @@ test_cases: expected: insns: - - asm_text: "jsle r9, 0xe5b0fd50, +0xb2f6" + asm_text: "jsle r9, 0xe5b0fd50, -0x4d0a" - input: bytes: [ 0xdc, 0xb2, 0xa3, 0x50, 0x20, 0x00, 0x00, 0x00 ] @@ -862,7 +844,16 @@ test_cases: expected: insns: - - asm_text: "jsle r5, r9, +0xb1bf" + asm_text: "jsle r5, r9, -0x4e41" + - + input: + bytes: [ 0x2d, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jgt r8, r1, +0x7" - input: bytes: [ 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 ] @@ -872,3 +863,355 @@ test_cases: insns: - asm_text: "callx r2" + + - input: + bytes: [ 0x06, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jal -0xa46e0bd" + + - input: + bytes: [ 0x16, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jeq32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0x26, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jgt32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0x36, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jge32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0x46, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jset32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0x56, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jne32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0x66, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jsgt32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0x76, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jsge32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0xa6, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jlt32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0xb6, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jle32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0xc6, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jslt32 r7, -0xa46e0bd, -0x33f1" + + - input: + bytes: [ 0xd6, 0xc7, 0x0f, 0xcc, 0x43, 0x1f, 0xb9, 0xf5 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "jsle32 r7, -0xa46e0bd, -0x33f1" + - + input: + bytes: [ 0x37, 0x84, 0x01, 0x00, 0x3b, 0x84, 0x55, 0x1f ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "sdiv64 r4, 0x1f55843b" + - + input: + bytes: [ 0x34, 0x46, 0x01, 0x00, 0xe1, 0x72, 0xd4, 0xcb ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "sdiv r6, 0xcbd472e1" + - + input: + bytes: [ 0x94, 0x39, 0x01, 0x00, 0x0b, 0xd2, 0xd1, 0xc9 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "smod r9, 0xc9d1d20b" + + - + input: + bytes: [ 0x9f, 0x35, 0x01, 0x00, 0xd6, 0x70, 0xd9, 0x5e ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "smod64 r5, r3" + + - input: + bytes: [ 0xbf, 0x31, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "movsb64 r1, r3" + + - input: + bytes: [ 0xbf, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "movsh64 r1, r3" + + - input: + bytes: [ 0xbf, 0x31, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "movsw64 r1, r3" + + - input: + bytes: [ 0xbc, 0x31, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "movsb r1, r3" + + - input: + bytes: [ 0xbc, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "movsh r1, r3" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "aadd64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0x01, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afadd64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0x40, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "aor64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0x41, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afor64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0x50, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "aand64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0x51, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afand64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0xa0, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "axor64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0xa1, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afxor64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0xe1, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "axchg64 [r10-0x4], r1" + + - input: + bytes: [ 0xdb, 0x1a, 0xfc, 0xff, 0xf1, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "acmpxchg64 [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "aadd [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0x01, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afadd [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0x40, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "aor [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0x41, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afor [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0x50, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "aand [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0x51, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afand [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0xa0, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "axor [r10-0x4], r1" + + - input: + bytes: [ 0xc3, 0x1a, 0xfc, 0xff, 0xa1, 0x00, 0x00, 0x00 ] + arch: "CS_ARCH_BPF" + options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_LITTLE_ENDIAN" ] + expected: + insns: + - + asm_text: "afxor [r10-0x4], r1" diff --git a/tests/MC/BPF/extended-be.yaml b/tests/MC/BPF/extended-be.yaml index 423ff70ad3..e72628559b 100644 --- a/tests/MC/BPF/extended-be.yaml +++ b/tests/MC/BPF/extended-be.yaml @@ -7,7 +7,7 @@ test_cases: expected: insns: - - asm_text: "ldb [0x0]" + asm_text: "ldabsb [skb+0x0]" - input: bytes: [ 0x28, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xff ] @@ -16,7 +16,7 @@ test_cases: expected: insns: - - asm_text: "ldh [0xfa0000ff]" + asm_text: "ldabsh [skb-0x5ffff01]" - input: bytes: [ 0x40, 0x10, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00 ] @@ -25,7 +25,7 @@ test_cases: expected: insns: - - asm_text: "ldw [r1+0xcc000000]" + asm_text: "ldindw [skb+r1]" - input: bytes: [ 0x18, 0x00, 0x00, 0x00, 0x0c, 0xb0, 0xce, 0xfa, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde ] @@ -88,7 +88,7 @@ test_cases: expected: insns: - - asm_text: "jmp +0x800" + asm_text: "ja +0x800" - input: bytes: [ 0xdd, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 ] @@ -107,24 +107,6 @@ test_cases: insns: - asm_text: "jlt r5, 0x10000000, +0x3000" - - - input: - bytes: [ 0xc3, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00 ] - arch: "CS_ARCH_BPF" - options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_BIG_ENDIAN" ] - expected: - insns: - - - asm_text: "xaddw [r2+0x10], r1" - - - input: - bytes: [ 0xdb, 0xa9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 ] - arch: "CS_ARCH_BPF" - options: [ "CS_MODE_BPF_EXTENDED", "CS_MODE_BIG_ENDIAN" ] - expected: - insns: - - - asm_text: "xadddw [r9+0x1], r10" - input: bytes: [ 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 ] diff --git a/tests/details/bpf.yaml b/tests/details/bpf.yaml index 8988b6ff4f..88060f996e 100644 --- a/tests/details/bpf.yaml +++ b/tests/details/bpf.yaml @@ -84,7 +84,7 @@ test_cases: regs_read: [ r2 ] regs_write: [ r2 ] - - asm_text: "ldb [0x0]" + asm_text: "ldabsb [skb+0x0]" details: groups: [ BPF_GRP_LOAD ] bpf: @@ -94,7 +94,7 @@ test_cases: mem_disp: 0x0 regs_write: [ r0 ] - - asm_text: "xadddw [r10+0x100], r3" + asm_text: "aadd64 [r10+0x100], r3" details: groups: [ BPF_GRP_STORE ] bpf: @@ -134,4 +134,47 @@ test_cases: type: BPF_OP_OFF off: 0x217 regs_read: [ r3 ] + - + input: + bytes: [ 0x28, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfa, 0x07, 0x76, 0x01, 0x28, 0xff, 0xff, 0xff, 0xff, 0x85, 0xd3, 0xa5, 0xe2, 0xff, 0xff, 0xff, 0xff ] + arch: "bpf" + options: [ CS_OPT_DETAIL, CS_MODE_BPF_EXTENDED ] + address: 0x0 + expected: + insns: + - + asm_text: "ldabsh [skb-0x5ffff01]" + details: + groups: [ BPF_GRP_LOAD ] + bpf: + operands: + - + type: BPF_OP_MEM + mem_disp: 0xfa0000ff + is_pkt: 1 + is_signed: 1 + - + asm_text: "add64 r6, 0xffffffff" + details: + groups: [ BPF_GRP_ALU ] + bpf: + operands: + - + type: BPF_OP_REG + reg: r6 + - + type: BPF_OP_IMM + imm: 0xffffffff + is_signed: -1 + - + asm_text: "call -0x1" + details: + groups: [ BPF_GRP_CALL ] + bpf: + operands: + - + type: BPF_OP_IMM + imm: 0xffffffff + is_signed: 1 + diff --git a/tests/details/cs_common_details.yaml b/tests/details/cs_common_details.yaml index b3c8aeefc2..b3cdc2339e 100644 --- a/tests/details/cs_common_details.yaml +++ b/tests/details/cs_common_details.yaml @@ -1039,15 +1039,15 @@ test_cases: details: groups: [ alu ] - - asm_text: "ldb [0x0]" - mnemonic: ldb - op_str: "[0x0]" + asm_text: "ldabsb [skb+0x0]" + mnemonic: ldabsb + op_str: "[skb+0x0]" details: regs_write: [ r0 ] groups: [ load ] - - asm_text: "xadddw [r10+0x100], r3" - mnemonic: xadddw + asm_text: "aadd64 [r10+0x100], r3" + mnemonic: aadd64 op_str: "[r10+0x100], r3" details: groups: [ store ]