From 41f7fd9e5a2317d2019af0e542cc8f282d8ee34a Mon Sep 17 00:00:00 2001 From: Roee Toledano Date: Sat, 30 Nov 2024 02:39:02 +0200 Subject: [PATCH 1/5] Update and refactor BPF One of the main improvements recent eBPF verifiers have is the ability to jump backwards in code. This means that the OFF field of the opcode can be negative, and thus should be interpreted as signed. eBPF used to have load instructions, specific to loading data from socket buffer. Today, they are deprecated, but it is still good practice to support them, as it's very likely older programs might use them. Some of the newer instructions added by the specification require additional fields to dictate what is the exact operation to be performed (eg. CALL requires 'src' to decide whether 'imm' should interpreted as BTF function ID, or as offset from IP. MOV requires 'off' field in order to decide whether a regular MOV or rather a MOVSX should be performed) * Move BPF opcode setting to the disassembler * Change BPF JMP instruction naming to conventional JA * Fix the sign interpretation of 'off', 'imm', etc * Implement LDIND* and LDABS* legacy packet instructions * Implement JUMP32 instruction class * Move BPF opcode setting to disassembler * Correct and remove branch + load tests, and add more exhaustive ones * Correct and remove malformed LD tests, and add legacy packet tests --- arch/BPF/BPFConstants.h | 5 +- arch/BPF/BPFDisassembler.c | 377 +++++++++++++++++++++++++- arch/BPF/BPFInstPrinter.c | 126 +++++---- arch/BPF/BPFMapping.c | 318 ++-------------------- bindings/python/capstone/bpf_const.py | 2 +- include/capstone/bpf.h | 25 +- tests/MC/BPF/classic-all.yaml | 2 +- tests/MC/BPF/extended-all.yaml | 194 +++++++++---- tests/MC/BPF/extended-be.yaml | 8 +- tests/details/bpf.yaml | 2 +- tests/details/cs_common_details.yaml | 6 +- 11 files changed, 646 insertions(+), 419 deletions(-) diff --git a/arch/BPF/BPFConstants.h b/arch/BPF/BPFConstants.h index d12590460c..73e87c1b20 100644 --- a/arch/BPF/BPFConstants.h +++ b/arch/BPF/BPFConstants.h @@ -16,6 +16,7 @@ #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 @@ -73,8 +74,8 @@ #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_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 diff --git a/arch/BPF/BPFDisassembler.c b/arch/BPF/BPFDisassembler.c index 6b3e00bdac..9acd033647 100644 --- a/arch/BPF/BPFDisassembler.c +++ b/arch/BPF/BPFDisassembler.c @@ -9,6 +9,7 @@ #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) @@ -179,8 +180,8 @@ 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) { @@ -196,7 +197,6 @@ 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; @@ -298,7 +298,7 @@ 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; } @@ -336,6 +336,7 @@ static bool decodeJump(cs_struct *ud, MCInst *MI, bpf_internal *bpf) 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); @@ -355,7 +356,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; } @@ -407,7 +412,6 @@ 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 */ @@ -423,10 +427,11 @@ static bool getInstruction(cs_struct *ud, MCInst *MI, bpf_internal *bpf) case BPF_CLASS_JMP: return decodeJump(ud, MI, bpf); case BPF_CLASS_RET: - /* eBPF doesn't have this class */ + /* case BPF_CLASS_JMP32: */ if (EBPF_MODE(ud)) - return false; - return decodeReturn(ud, MI, bpf); + return decodeJump(ud, MI, bpf); + else + return decodeReturn(ud, MI, bpf); case BPF_CLASS_MISC: /* case BPF_CLASS_ALU64: */ if (EBPF_MODE(ud)) @@ -434,6 +439,356 @@ static bool getInstruction(cs_struct *ud, MCInst *MI, bpf_internal *bpf) else return decodeMISC(ud, MI, bpf); } + +} + +static bpf_insn op2insn_ld_cbpf(unsigned opcode) +{ +// 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; + + switch (BPF_SIZE(opcode)) { + REG_LOAD_CASE(W); + REG_LOAD_CASE(H); + REG_LOAD_CASE(B); + REG_LOAD_CASE(DW); + } +#undef REG_LOAD_CASE + + return BPF_INS_INVALID; +} + +static bpf_insn op2insn_ld_ebpf(unsigned opcode) +{ +// 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; + + if (BPF_CLASS(opcode) == BPF_CLASS_LD) { + switch (BPF_SIZE(opcode)) { + PACKET_LOAD_CASE(W); + PACKET_LOAD_CASE(H); + PACKET_LOAD_CASE(B); + } + } +#undef PACKET_LOAD_CASE + + // If it's not a packet load instruction, it must be a regular load instruction + return op2insn_ld_cbpf(opcode); +} + +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: \ + 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; + + 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); + } +#undef SPEC_CASE +#undef CASE + + return BPF_INS_INVALID; +} + +#ifndef CAPSTONE_DIET +static void update_regs_access(const 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_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) { + 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_JA) // 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 + +static bool setFinalOpcode(const cs_struct* ud, MCInst *insnr, const bpf_internal* bpf) { + bpf_insn id = BPF_INS_INVALID; +#ifndef CAPSTONE_DIET + cs_detail *detail; + bpf_insn_group grp; + + detail = get_detail(insnr); + #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 + + 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(ud)) + id = op2insn_ld_ebpf(opcode); + else + id = op2insn_ld_cbpf(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: + /* case BPF_CLASS_JMP32: */ + if (EBPF_MODE(ud)) { + id = op2insn_jmp(opcode); +#ifndef CAPSTONE_DIET + PUSH_GROUP(BPF_GRP_JUMP); +#endif + } else { + 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; + } + + if (id == BPF_INS_INVALID) + return false; + + MCInst_setOpcodePub(insnr, id); +#undef PUSH_GROUP + +#ifndef CAPSTONE_DIET + if (detail) { + update_regs_access(ud, detail, id, opcode); + } +#endif + return true; } bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len, @@ -449,10 +804,12 @@ bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len, bpf = fetch_cbpf(cs, code, code_len); if (bpf == NULL) return false; - if (!getInstruction(cs, instr, bpf)) { + if (!getInstruction(cs, instr, bpf) || + !setFinalOpcode(cs, 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/BPFInstPrinter.c b/arch/BPF/BPFInstPrinter.c index 9c65a4adda..36b380d01e 100644 --- a/arch/BPF/BPFInstPrinter.c +++ b/arch/BPF/BPFInstPrinter.c @@ -22,29 +22,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) @@ -85,19 +90,23 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) 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))); + 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), EBPF_MODE(MI->csh)); 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)) + 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, false); + } break; case BPF_MODE_MEM: if (EBPF_MODE(MI->csh)) { @@ -105,7 +114,7 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) 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)); + 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))); @@ -135,37 +144,42 @@ 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) && (BPF_CLASS(opcode) == BPF_CLASS_JMP32); + if (BPF_CLASS(opcode) == BPF_CLASS_JMP || is_jmp32) { + for (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) && i >= 1) || + (EBPF_MODE(MI->csh) && i == 2)) + push_op_off(bpf, MCOperand_getImm(op), EBPF_MODE(MI->csh)); + 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)) { @@ -173,7 +187,7 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) for (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); } @@ -201,12 +215,20 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) 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); } } +static void print_signed_offset(struct SStream *O, const bool is_signed, const uint32_t off) +{ + if (is_signed && ((int16_t)off) < 0) + SStream_concat(O, "-0x%x", -((int16_t)off)); + else + SStream_concat(O, "+0x%x", off); +} + static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op) { switch (op->type) { @@ -217,22 +239,35 @@ 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 && ((int32_t)op->imm) < 0) + SStream_concat(O, "-0x%" PRIx64, -(int32_t)op->imm); + else + SStream_concat(O, "0x%" PRIx64, op->imm); break; case BPF_OP_OFF: - SStream_concat(O, "+0x%x", op->off); + print_signed_offset(O, op->is_signed, 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) { + 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 + print_signed_offset(O, op->is_signed, 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) + print_signed_offset(O, op->is_signed, op->mem.disp); + else + SStream_concat(O, "0x%x", op->mem.disp); + } + if (op->mem.base == BPF_REG_INVALID && op->mem.disp == 0) // special case + 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: @@ -259,15 +294,10 @@ 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; - 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++) { if (i == 0) diff --git a/arch/BPF/BPFMapping.c b/arch/BPF/BPFMapping.c index 17d3c55710..86fddf9a08 100644 --- a/arch/BPF/BPFMapping.c +++ b/arch/BPF/BPFMapping.c @@ -81,6 +81,12 @@ static const name_map insn_name_maps[BPF_INS_ENDING] = { { 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" }, @@ -93,7 +99,7 @@ static const name_map insn_name_maps[BPF_INS_ENDING] = { { BPF_INS_XADDW, "xaddw" }, { BPF_INS_XADDDW, "xadddw" }, - { BPF_INS_JMP, "jmp" }, + { BPF_INS_JA, "ja" }, { BPF_INS_JEQ, "jeq" }, { BPF_INS_JGT, "jgt" }, { BPF_INS_JGE, "jge" }, @@ -109,6 +115,19 @@ static const name_map insn_name_maps[BPF_INS_ENDING] = { { 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_RET, "ret" }, { BPF_INS_TAX, "tax" }, @@ -162,302 +181,9 @@ const char *BPF_reg_name(csh handle, unsigned int reg) #endif } -static bpf_insn op2insn_ld(unsigned opcode) -{ -#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) +void BPF_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) { - 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) 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/include/capstone/bpf.h b/include/capstone/bpf.h index 12c8d1d0ea..1b79c138c5 100644 --- a/include/capstone/bpf.h +++ b/include/capstone/bpf.h @@ -78,6 +78,8 @@ typedef struct cs_bpf_op { uint32_t ext; ///< cBPF's extension (not eBPF) }; + bool is_signed; ///< is this operand signed? (used mem, imm and off operands) + bool is_pkt; ///< is this operand referring to packet data? (used in MEM operand) /// 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. @@ -144,6 +146,13 @@ typedef enum bpf_insn { 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 @@ -158,7 +167,7 @@ typedef enum bpf_insn { BPF_INS_XADDDW, ///< eBPF only ///< Jump - BPF_INS_JMP, + BPF_INS_JA, BPF_INS_JEQ, BPF_INS_JGT, BPF_INS_JGE, @@ -174,6 +183,20 @@ typedef enum bpf_insn { 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, 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..35a3841e7a 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+0xca1a41da]" - 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,7 +187,7 @@ test_cases: expected: insns: - - asm_text: "ldb [0x4b7c6685]" + asm_text: "ldabsb [skb+0x4b7c6685]" - input: bytes: [ 0x34, 0x46, 0x49, 0x33, 0xe1, 0x72, 0xd4, 0xcb ] @@ -205,7 +205,7 @@ 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 ] @@ -215,24 +215,6 @@ test_cases: insns: - 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 ] @@ -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 ] @@ -763,7 +736,7 @@ 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 ] @@ -790,7 +763,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 +790,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 +817,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 +835,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 +854,111 @@ 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" diff --git a/tests/MC/BPF/extended-be.yaml b/tests/MC/BPF/extended-be.yaml index 423ff70ad3..d8f8c290f0 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+0xfa0000ff]" - 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 ] diff --git a/tests/details/bpf.yaml b/tests/details/bpf.yaml index 8988b6ff4f..5b9c94bf3d 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: diff --git a/tests/details/cs_common_details.yaml b/tests/details/cs_common_details.yaml index b3c8aeefc2..1dbeea3fc5 100644 --- a/tests/details/cs_common_details.yaml +++ b/tests/details/cs_common_details.yaml @@ -1039,9 +1039,9 @@ 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 ] From 4adc79ffc5a6673c13eafd06dc88d3e140a28425 Mon Sep 17 00:00:00 2001 From: Roee Toledano Date: Mon, 2 Dec 2024 18:37:12 +0200 Subject: [PATCH 2/5] Add missing new eBPF instructions * Implement `movs`, `sdiv`, `div` instructions * Implement atomic ALU and complex operations (atomic `add`, `or`, `and`, `xor`, and `cmp`, `cmpxchg` respectively) * Remove outdated `xadd` tests, and add new ones for the new instructions --- arch/BPF/BPFConstants.h | 10 +- arch/BPF/BPFDisassembler.c | 94 +++++++++-- arch/BPF/BPFMapping.c | 29 ++++ include/capstone/bpf.h | 31 ++++ tests/MC/BPF/extended-all.yaml | 238 +++++++++++++++++++++++++-- tests/MC/BPF/extended-be.yaml | 18 -- tests/details/bpf.yaml | 2 +- tests/details/cs_common_details.yaml | 4 +- 8 files changed, 376 insertions(+), 50 deletions(-) diff --git a/arch/BPF/BPFConstants.h b/arch/BPF/BPFConstants.h index 73e87c1b20..104ce77d3e 100644 --- a/arch/BPF/BPFConstants.h +++ b/arch/BPF/BPFConstants.h @@ -54,6 +54,10 @@ #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 @@ -79,7 +83,11 @@ #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_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 diff --git a/arch/BPF/BPFDisassembler.c b/arch/BPF/BPFDisassembler.c index 9acd033647..854f861208 100644 --- a/arch/BPF/BPFDisassembler.c +++ b/arch/BPF/BPFDisassembler.c @@ -229,8 +229,7 @@ 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) @@ -486,17 +485,48 @@ static bpf_insn op2insn_ld_ebpf(unsigned opcode) return op2insn_ld_cbpf(opcode); } -static bpf_insn op2insn_st(unsigned opcode) +static bpf_insn op2insn_st(unsigned opcode, const uint32_t imm) { /* - * - BPF_STX | BPF_XADD | BPF_{W,DW} + * - 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 (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; +/* 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; + + 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 */ #define CASE(c) case BPF_SIZE_##c: \ @@ -514,7 +544,7 @@ static bpf_insn op2insn_st(unsigned opcode) return BPF_INS_INVALID; } -static bpf_insn op2insn_alu(unsigned opcode) +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) { @@ -548,27 +578,57 @@ static bpf_insn op2insn_alu(unsigned opcode) return BPF_INS_INVALID; } -#define CASE(c) case BPF_ALU_##c: \ +#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; + return BPF_INS_##c##64; \ + } while (0) + 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); + 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; } +#undef CASE_IF #undef CASE return BPF_INS_INVALID; @@ -729,11 +789,11 @@ static bool setFinalOpcode(const cs_struct* ud, MCInst *insnr, const bpf_interna break; case BPF_CLASS_ST: case BPF_CLASS_STX: - id = op2insn_st(opcode); + id = op2insn_st(opcode, bpf->k); PUSH_GROUP(BPF_GRP_STORE); break; case BPF_CLASS_ALU: - id = op2insn_alu(opcode); + id = op2insn_alu(opcode, bpf->offset, EBPF_MODE(ud)); PUSH_GROUP(BPF_GRP_ALU); break; case BPF_CLASS_JMP: @@ -764,7 +824,7 @@ static bool setFinalOpcode(const cs_struct* ud, MCInst *insnr, const bpf_interna /* case BPF_CLASS_ALU64: */ if (EBPF_MODE(ud)) { // ALU64 in eBPF - id = op2insn_alu(opcode); + id = op2insn_alu(opcode, bpf->offset, true); PUSH_GROUP(BPF_GRP_ALU); } else { diff --git a/arch/BPF/BPFMapping.c b/arch/BPF/BPFMapping.c index 86fddf9a08..f7e5b3c02c 100644 --- a/arch/BPF/BPFMapping.c +++ b/arch/BPF/BPFMapping.c @@ -39,28 +39,37 @@ static const name_map insn_name_maps[BPF_INS_ENDING] = { { 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_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" }, @@ -130,6 +139,26 @@ static const name_map insn_name_maps[BPF_INS_ENDING] = { { BPF_INS_RET, "ret" }, + { 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_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" }, }; diff --git a/include/capstone/bpf.h b/include/capstone/bpf.h index 1b79c138c5..cd225b62fa 100644 --- a/include/capstone/bpf.h +++ b/include/capstone/bpf.h @@ -101,14 +101,18 @@ 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_MOVSB, ///< eBPF only + BPF_INS_MOVSH, ///< eBPF only BPF_INS_ARSH, ///< eBPF only ///< ALU64, eBPF only @@ -116,14 +120,19 @@ typedef enum bpf_insn { 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 @@ -200,6 +209,28 @@ typedef enum bpf_insn { ///< 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, diff --git a/tests/MC/BPF/extended-all.yaml b/tests/MC/BPF/extended-all.yaml index 35a3841e7a..1a5d3f4b65 100644 --- a/tests/MC/BPF/extended-all.yaml +++ b/tests/MC/BPF/extended-all.yaml @@ -190,7 +190,7 @@ test_cases: 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: @@ -208,7 +208,7 @@ test_cases: 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,7 +217,7 @@ test_cases: asm_text: "div64 r4, 0x1f55843b" - 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: @@ -593,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 ] @@ -604,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: @@ -622,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: @@ -631,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: @@ -640,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: @@ -694,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: @@ -712,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: @@ -721,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: @@ -739,7 +748,7 @@ test_cases: 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: @@ -962,3 +971,210 @@ test_cases: insns: - asm_text: "jsle32 r7, -0xa46e0bd, -0x33f1" + + - 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 d8f8c290f0..c0dc62f4b5 100644 --- a/tests/MC/BPF/extended-be.yaml +++ b/tests/MC/BPF/extended-be.yaml @@ -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 5b9c94bf3d..900e11798c 100644 --- a/tests/details/bpf.yaml +++ b/tests/details/bpf.yaml @@ -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: diff --git a/tests/details/cs_common_details.yaml b/tests/details/cs_common_details.yaml index 1dbeea3fc5..b3cdc2339e 100644 --- a/tests/details/cs_common_details.yaml +++ b/tests/details/cs_common_details.yaml @@ -1046,8 +1046,8 @@ test_cases: 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 ] From 2c87f8a59c14748a167814768bd6a6ed85633c8c Mon Sep 17 00:00:00 2001 From: Roee Toledano Date: Fri, 6 Dec 2024 12:05:08 +0200 Subject: [PATCH 3/5] Document BPF changes --- docs/cs_v6_release_guide.md | 8 ++++++++ 1 file changed, 8 insertions(+) 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). From d2e0e9203578ffe7f96954def7e40a77e2dccfc9 Mon Sep 17 00:00:00 2001 From: Roee Toledano Date: Tue, 10 Dec 2024 19:53:00 +0200 Subject: [PATCH 4/5] Fix comments --- Mapping.c | 1 + Mapping.h | 5 + SStream.c | 46 ++ SStream.h | 4 + arch/BPF/BPFConstants.h | 128 ++--- arch/BPF/BPFDisassembler.c | 481 +++++++++--------- arch/BPF/BPFDisassembler.h | 3 +- arch/BPF/BPFInstPrinter.c | 180 ++++--- arch/BPF/BPFMapping.c | 243 ++++----- arch/BPF/BPFMapping.h | 10 +- bindings/python/capstone/bpf.py | 3 +- .../python/cstest_py/src/cstest_py/details.py | 4 + include/capstone/bpf.h | 101 ++-- suite/cstest/include/test_compare.h | 3 + suite/cstest/include/test_detail_bpf.h | 16 +- suite/cstest/src/test_detail_bpf.c | 4 + tests/MC/BPF/extended-all.yaml | 39 +- tests/MC/BPF/extended-be.yaml | 2 +- tests/details/bpf.yaml | 43 ++ 19 files changed, 736 insertions(+), 580 deletions(-) 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 104ce77d3e..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,89 +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_JMP32 0x06 ///< eBPF 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_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 ///< 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_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_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 854f861208..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 @@ -11,25 +13,10 @@ #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; @@ -44,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; @@ -53,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; @@ -81,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] | @@ -122,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; @@ -130,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; @@ -161,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; @@ -187,7 +176,8 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) 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); @@ -200,7 +190,6 @@ static bool decodeLoad(cs_struct *ud, MCInst *MI, bpf_internal *bpf) 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; @@ -232,7 +221,8 @@ static bool decodeStore(cs_struct *ud, MCInst *MI, bpf_internal *bpf) 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); @@ -254,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 */ @@ -280,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; } @@ -304,17 +295,16 @@ static bool decodeALU(cs_struct *ud, MCInst *MI, bpf_internal *bpf) /* 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; @@ -330,8 +320,7 @@ 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; @@ -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; } @@ -374,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)) { @@ -391,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; @@ -400,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; @@ -417,54 +407,53 @@ static bool getInstruction(cs_struct *ud, MCInst *MI, bpf_internal *bpf) 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: - /* case BPF_CLASS_JMP32: */ - if (EBPF_MODE(ud)) - return decodeJump(ud, MI, bpf); + /* case BPF_CLASS_JMP32: */ + if (EBPF_MODE(MI->csh->mode)) + return decodeJump(MI, bpf); else - return decodeReturn(ud, MI, bpf); + 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); } - } -static bpf_insn op2insn_ld_cbpf(unsigned opcode) -{ // Check for regular load instructions -#define REG_LOAD_CASE(c) case BPF_SIZE_##c: \ +#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); + REG_LOAD_CASE(W); + REG_LOAD_CASE(H); + REG_LOAD_CASE(B); + REG_LOAD_CASE(DW); } -#undef REG_LOAD_CASE return BPF_INS_INVALID; } +#undef REG_LOAD_CASE -static bpf_insn op2insn_ld_ebpf(unsigned opcode) -{ // Check for packet load instructions -#define PACKET_LOAD_CASE(c) case BPF_SIZE_##c: \ +#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) \ @@ -472,84 +461,108 @@ static bpf_insn op2insn_ld_ebpf(unsigned opcode) 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); + PACKET_LOAD_CASE(W); + PACKET_LOAD_CASE(H); + PACKET_LOAD_CASE(B); } } -#undef PACKET_LOAD_CASE // If it's not a packet load instruction, it must be a regular load instruction return op2insn_ld_cbpf(opcode); } - -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} - */ +#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: \ +#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: \ +#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: \ +#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 + 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 */ -#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); + CASE(W); + CASE(H); + CASE(B); + CASE(DW); } -#undef CASE - return BPF_INS_INVALID; + CS_ASSERT_RET_VAL( + false && "Malformed atomic BPF instruction", + BPF_INS_INVALID); } -static bpf_insn op2insn_alu(unsigned opcode, const uint16_t off, const bool is_ebpf) +#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) { + switch (opcode ^ BPF_CLASS_ALU64 ^ BPF_ALU_END ^ + BPF_SRC_LITTLE) { case (16 << 4): return BPF_INS_BSWAP16; case (32 << 4): @@ -578,27 +591,17 @@ static bpf_insn op2insn_alu(unsigned opcode, const uint16_t off, const bool is_e return BPF_INS_INVALID; } -#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) - - 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(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); @@ -628,76 +631,74 @@ static bpf_insn op2insn_alu(unsigned opcode, const uint16_t off, const bool is_e else return BPF_INS_INVALID; } -#undef CASE_IF -#undef CASE return BPF_INS_INVALID; } +#undef CASE_IF +#undef CASE -static bpf_insn op2insn_jmp(unsigned opcode) -{ - if (opcode == (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) { - return BPF_INS_CALLX; - } +#define BPF_CALLX (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X) -#define CASE(c) case BPF_JUMP_##c: \ +#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: \ +#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); + 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); } -#undef SPEC_CASE -#undef CASE return BPF_INS_INVALID; } +#undef SPEC_CASE +#undef CASE +#undef BPF_CALLX #ifndef CAPSTONE_DIET -static void update_regs_access(const cs_struct *ud, cs_detail *detail, - bpf_insn insn_id, unsigned int opcode) + +static void update_regs_access(MCInst *MI, 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)) { + if (EBPF_MODE(MI->csh->mode)) { switch (insn_id) { default: break; @@ -708,12 +709,12 @@ static void update_regs_access(const cs_struct *ud, cs_detail *detail, 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) { - PUSH_WRITE(BPF_REG_R0); - } + 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: - PUSH_READ(BPF_REG_R0); + map_add_implicit_read(MI, BPF_REG_R0); break; } return; @@ -724,115 +725,103 @@ static void update_regs_access(const cs_struct *ud, cs_detail *detail, default: break; case BPF_CLASS_LD: - PUSH_WRITE(BPF_REG_A); + map_add_implicit_write(MI, BPF_REG_A); break; case BPF_CLASS_LDX: - PUSH_WRITE(BPF_REG_X); + map_add_implicit_write(MI, BPF_REG_X); break; case BPF_CLASS_ST: - PUSH_READ(BPF_REG_A); + map_add_implicit_read(MI, BPF_REG_A); break; case BPF_CLASS_STX: - PUSH_READ(BPF_REG_X); + map_add_implicit_read(MI, BPF_REG_X); break; case BPF_CLASS_ALU: - PUSH_READ(BPF_REG_A); - PUSH_WRITE(BPF_REG_A); + 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 - PUSH_READ(BPF_REG_A); + map_add_implicit_read(MI, 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); + 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(const cs_struct* ud, MCInst *insnr, const bpf_internal* bpf) { +static bool setFinalOpcode(MCInst *MI, const bpf_internal *bpf) +{ bpf_insn id = BPF_INS_INVALID; #ifndef CAPSTONE_DIET cs_detail *detail; - bpf_insn_group grp; - - detail = get_detail(insnr); - #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) + + detail = get_detail(MI); #endif - + const uint16_t opcode = bpf->op; switch (BPF_CLASS(opcode)) { - default: // will never happen + default: // will never happen break; case BPF_CLASS_LD: case BPF_CLASS_LDX: - if (EBPF_MODE(ud)) + if (EBPF_MODE(MI->csh->mode)) id = op2insn_ld_ebpf(opcode); else id = op2insn_ld_cbpf(opcode); - PUSH_GROUP(BPF_GRP_LOAD); + add_group(MI, BPF_GRP_LOAD); break; case BPF_CLASS_ST: case BPF_CLASS_STX: id = op2insn_st(opcode, bpf->k); - PUSH_GROUP(BPF_GRP_STORE); + add_group(MI, BPF_GRP_STORE); break; case BPF_CLASS_ALU: - id = op2insn_alu(opcode, bpf->offset, EBPF_MODE(ud)); - PUSH_GROUP(BPF_GRP_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 - grp = BPF_GRP_JUMP; if (id == BPF_INS_CALL || id == BPF_INS_CALLX) - grp = BPF_GRP_CALL; + add_group(MI, BPF_GRP_CALL); else if (id == BPF_INS_EXIT) - grp = BPF_GRP_RETURN; - PUSH_GROUP(grp); + 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(ud)) { + /* case BPF_CLASS_JMP32: */ + if (EBPF_MODE(MI->csh->mode)) { id = op2insn_jmp(opcode); -#ifndef CAPSTONE_DIET - PUSH_GROUP(BPF_GRP_JUMP); -#endif + add_group(MI, BPF_GRP_JUMP); } else { id = BPF_INS_RET; - PUSH_GROUP(BPF_GRP_RETURN); + 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(ud)) { + /* case BPF_CLASS_ALU64: */ + if (EBPF_MODE(MI->csh->mode)) { // ALU64 in eBPF id = op2insn_alu(opcode, bpf->offset, true); - PUSH_GROUP(BPF_GRP_ALU); - } - else { + add_group(MI, 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); + add_group(MI, BPF_GRP_MISC); } break; } @@ -840,36 +829,34 @@ static bool setFinalOpcode(const cs_struct* ud, MCInst *insnr, const bpf_interna if (id == BPF_INS_INVALID) return false; - MCInst_setOpcodePub(insnr, id); + MCInst_setOpcodePub(MI, id); #undef PUSH_GROUP #ifndef CAPSTONE_DIET if (detail) { - update_regs_access(ud, detail, id, opcode); + 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) || - !setFinalOpcode(cs, instr, bpf)) { + if (!getInstruction(instr, bpf) || !setFinalOpcode(instr, bpf)) { cs_mem_free(bpf); return false; } - MCInst_setOpcode(instr, bpf->op); + 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 36b380d01e..d12dc2bd45 100644 --- a/arch/BPF/BPFInstPrinter.c +++ b/arch/BPF/BPFInstPrinter.c @@ -1,5 +1,7 @@ /* Capstone Disassembly Engine */ /* BPF Backend by david942j , 2019 */ +/* SPDX-FileCopyrightText: 2024 Roee Toledano */ +/* SPDX-License-Identifier: BSD-3 */ #include @@ -40,8 +42,8 @@ static void push_op_off(cs_bpf *bpf, uint32_t val, const bool is_signed) op->is_signed = is_signed; } -static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val, - const bool is_signed, const bool is_pkt) +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); @@ -82,42 +84,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)), false); + 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)), false); + 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), EBPF_MODE(MI->csh), EBPF_MODE(MI->csh)); + 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); - if (EBPF_MODE(MI->csh)) - push_op_mem(bpf, MCOperand_getReg(op), 0x0, true, true); + 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, false); + 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), true, false); - } - 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: @@ -127,14 +148,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: @@ -144,7 +167,9 @@ 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), true, false); + 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), false); @@ -154,9 +179,10 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) } { - const bool is_jmp32 = EBPF_MODE(MI->csh) && (BPF_CLASS(opcode) == BPF_CLASS_JMP32); + 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 (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)) { /* Decide if we're using IMM or OFF here (and if OFF, then signed or unsigned): @@ -167,29 +193,40 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) * 4. any jump {x,k}, +jt, +jf // cBPF * */ - if ((BPF_OP(opcode) == BPF_JUMP_JA && !is_jmp32) || - (!EBPF_MODE(MI->csh) && i >= 1) || - (EBPF_MODE(MI->csh) && i == 2)) - push_op_off(bpf, MCOperand_getImm(op), EBPF_MODE(MI->csh)); + 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); + push_op_imm( + bpf, + MCOperand_getImm(op), + true); + } else if (MCOperand_isReg(op)) { + push_op_reg(bpf, MCOperand_getReg(op), + CS_AC_READ); } } 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), 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; } @@ -207,11 +244,12 @@ 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)) @@ -221,14 +259,6 @@ static void convert_operands(MCInst *MI, cs_bpf *bpf) } } -static void print_signed_offset(struct SStream *O, const bool is_signed, const uint32_t off) -{ - if (is_signed && ((int16_t)off) < 0) - SStream_concat(O, "-0x%x", -((int16_t)off)); - else - SStream_concat(O, "+0x%x", off); -} - static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op) { switch (op->type) { @@ -239,32 +269,57 @@ 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: - if (op->is_signed && ((int32_t)op->imm) < 0) - SStream_concat(O, "-0x%" PRIx64, -(int32_t)op->imm); + if (op->is_signed) + printInt32Hex(O, op->imm); else SStream_concat(O, "0x%" PRIx64, op->imm); break; case BPF_OP_OFF: - print_signed_offset(O, op->is_signed, 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->is_pkt) { + + 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 - print_signed_offset(O, op->is_signed, op->mem.disp); + 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, BPF_reg_name((csh)MI->csh, op->mem.base)); + SStream_concat(O, BPF_reg_name((csh)MI->csh, + op->mem.base)); if (op->mem.disp != 0) { - if (op->mem.base != BPF_REG_INVALID) - print_signed_offset(O, op->is_signed, op->mem.disp); - else - SStream_concat(O, "0x%x", op->mem.disp); + 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) // special case + + if (op->mem.base == BPF_REG_INVALID && + op->mem.disp == 0) SStream_concat(O, "0x0"); } @@ -293,13 +348,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_bpf bpf; /* set pubOpcode as instruction 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 diff --git a/arch/BPF/BPFMapping.c b/arch/BPF/BPFMapping.c index f7e5b3c02c..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,147 +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_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_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_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_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_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_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_RET, "ret" }, - { 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_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_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_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); @@ -187,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]; } @@ -219,24 +169,21 @@ 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; @@ -245,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; @@ -257,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..c50c6232d9 100644 --- a/bindings/python/capstone/bpf.py +++ b/bindings/python/capstone/bpf.py @@ -27,6 +27,8 @@ class BPFOp(ctypes.Structure): ('type', ctypes.c_uint), ('value', BPFOpValue), ('access', ctypes.c_uint8), + ('is_signed', ctypes.c_bool), + ('is_pkt', ctypes.c_bool), ) @property @@ -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/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/include/capstone/bpf.h b/include/capstone/bpf.h index cd225b62fa..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,15 +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? (used mem, imm and off operands) - bool is_pkt; ///< is this operand referring to packet data? (used in MEM operand) + 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. @@ -110,10 +113,10 @@ typedef enum bpf_insn { BPF_INS_MOD, BPF_INS_SMOD, BPF_INS_XOR, - BPF_INS_MOV, ///< eBPF only - BPF_INS_MOVSB, ///< eBPF only - BPF_INS_MOVSH, ///< 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, @@ -147,21 +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 + 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 @@ -173,7 +176,7 @@ 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_JA, @@ -181,28 +184,28 @@ typedef enum bpf_insn { 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_JEQ32, + BPF_INS_JGT32, + BPF_INS_JGE32, BPF_INS_JSET32, - BPF_INS_JNE32, + BPF_INS_JNE32, BPF_INS_JSGT32, BPF_INS_JSGE32, - BPF_INS_JLT32, - BPF_INS_JLE32, + BPF_INS_JLT32, + BPF_INS_JLE32, BPF_INS_JSLT32, BPF_INS_JSLE32, @@ -238,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..8802a3868a 100644 --- a/suite/cstest/src/test_detail_bpf.c +++ b/suite/cstest/src/test_detail_bpf.c @@ -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/extended-all.yaml b/tests/MC/BPF/extended-all.yaml index 1a5d3f4b65..823aa5bdef 100644 --- a/tests/MC/BPF/extended-all.yaml +++ b/tests/MC/BPF/extended-all.yaml @@ -115,7 +115,7 @@ test_cases: expected: insns: - - asm_text: "ldabsw [skb+0xca1a41da]" + asm_text: "ldabsw [skb-0x35e5be26]" - input: bytes: [ 0x24, 0xb6, 0x69, 0x66, 0xe3, 0xef, 0xec, 0x25 ] @@ -971,6 +971,43 @@ test_cases: 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 ] diff --git a/tests/MC/BPF/extended-be.yaml b/tests/MC/BPF/extended-be.yaml index c0dc62f4b5..e72628559b 100644 --- a/tests/MC/BPF/extended-be.yaml +++ b/tests/MC/BPF/extended-be.yaml @@ -16,7 +16,7 @@ test_cases: expected: insns: - - asm_text: "ldabsh [skb+0xfa0000ff]" + asm_text: "ldabsh [skb-0x5ffff01]" - input: bytes: [ 0x40, 0x10, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00 ] diff --git a/tests/details/bpf.yaml b/tests/details/bpf.yaml index 900e11798c..88060f996e 100644 --- a/tests/details/bpf.yaml +++ b/tests/details/bpf.yaml @@ -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 + From 7dfbe55efbaab3d137d3849851795d63ed082a67 Mon Sep 17 00:00:00 2001 From: Roee Toledano Date: Wed, 11 Dec 2024 21:53:13 +0200 Subject: [PATCH 5/5] Correct BPF bindings error --- arch/BPF/BPFInstPrinter.c | 5 +++-- bindings/python/capstone/bpf.py | 6 +++--- suite/cstest/src/test_detail_bpf.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/BPF/BPFInstPrinter.c b/arch/BPF/BPFInstPrinter.c index d12dc2bd45..de993628ed 100644 --- a/arch/BPF/BPFInstPrinter.c +++ b/arch/BPF/BPFInstPrinter.c @@ -8,6 +8,7 @@ #include "BPFConstants.h" #include "BPFInstPrinter.h" #include "BPFMapping.h" +#include "../../Mapping.h" static cs_bpf_op *expand_bpf_operands(cs_bpf *bpf) { @@ -348,7 +349,7 @@ static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op) * */ void BPF_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) { - cs_bpf bpf; + cs_bpf bpf = { 0 }; /* set pubOpcode as instruction id */ SStream_concat(O, BPF_insn_name((csh)MI->csh, MCInst_getOpcodePub(MI))); @@ -362,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/bindings/python/capstone/bpf.py b/bindings/python/capstone/bpf.py index c50c6232d9..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,9 +26,9 @@ class BPFOp(ctypes.Structure): _fields_ = ( ('type', ctypes.c_uint), ('value', BPFOpValue), - ('access', ctypes.c_uint8), ('is_signed', ctypes.c_bool), ('is_pkt', ctypes.c_bool), + ('access', ctypes.c_uint8), ) @property diff --git a/suite/cstest/src/test_detail_bpf.c b/suite/cstest/src/test_detail_bpf.c index 8802a3868a..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