/* Capstone Disassembly Engine */ /* BPF Backend by david942j , 2019 */ /* SPDX-FileCopyrightText: 2024 Roee Toledano */ /* SPDX-License-Identifier: BSD-3 */ #ifdef CAPSTONE_HAS_BPF #include #include // offsetof macro #include "BPFConstants.h" #include "BPFDisassembler.h" #include "BPFMapping.h" #include "../../Mapping.h" #include "../../cs_priv.h" #include "../../utils.h" ///< Malloc bpf_internal, also checks if code_len is large enough. static bpf_internal *alloc_bpf_internal(const size_t code_len) { bpf_internal *bpf; if (code_len < 8) return NULL; bpf = cs_mem_malloc(sizeof(bpf_internal)); if (bpf == NULL) return NULL; /* default value */ bpf->insn_size = 8; return bpf; } ///< Fetch a cBPF structure from code static bpf_internal *fetch_cbpf(MCInst *instr, const uint8_t *code, const size_t code_len) { bpf_internal *bpf; bpf = alloc_bpf_internal(code_len); if (bpf == NULL) return NULL; bpf->op = readBytes16(instr, code); bpf->jt = code[2]; bpf->jf = code[3]; bpf->k = readBytes32(instr, code + 4); return bpf; } ///< Fetch an eBPF structure from code static bpf_internal *fetch_ebpf(MCInst *instr, const uint8_t *code, const size_t code_len) { bpf_internal *bpf; bpf = alloc_bpf_internal(code_len); if (bpf == NULL) return NULL; bpf->op = (uint16_t)code[0]; bpf->dst = code[1] & 0xf; bpf->src = (code[1] & 0xf0) >> 4; // eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM, // in this case imm is combined with the next block's imm. if (bpf->op == (BPF_CLASS_LD | BPF_SIZE_DW | BPF_MODE_IMM)) { if (code_len < 16) { cs_mem_free(bpf); return NULL; } bpf->k = readBytes32(instr, code + 4) | (((uint64_t)readBytes32(instr, code + 12)) << 32); bpf->insn_size = 16; } 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)) \ return false; \ } while (0) #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 { \ 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_WRITEABLE_REG(ud, r + BPF_REG_R0); \ MCOperand_CreateReg0(MI, r + BPF_REG_R0); \ } while (0) static bool decodeLoad(MCInst *MI, bpf_internal *bpf) { if (!EBPF_MODE(MI->csh->mode)) { /* * +-----+-----------+--------------------+ * | ldb | [k] | [x+k] | * | ldh | [k] | [x+k] | * +-----+-----------+--------------------+ */ 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) { /* no ldx */ if (BPF_CLASS(bpf->op) != BPF_CLASS_LD) return false; /* can only be BPF_ABS and BPF_IND */ if (BPF_MODE(bpf->op) == BPF_MODE_ABS) { MCOperand_CreateImm0(MI, bpf->k); return true; } else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { MCOperand_CreateReg0(MI, BPF_REG_X); MCOperand_CreateImm0(MI, bpf->k); return true; } return false; } /* * +-----+----+------+------+-----+-------+ * | ld | #k | #len | M[k] | [k] | [x+k] | * +-----+----+------+------+-----+-------+ * | ldx | #k | #len | M[k] | 4*([k]&0xf) | * +-----+----+------+------+-------------+ */ switch (BPF_MODE(bpf->op)) { default: break; case BPF_MODE_IMM: MCOperand_CreateImm0(MI, bpf->k); return true; case BPF_MODE_LEN: return true; case BPF_MODE_MEM: MCOperand_CreateImm0(MI, bpf->k); return true; } if (BPF_CLASS(bpf->op) == BPF_CLASS_LD) { if (BPF_MODE(bpf->op) == BPF_MODE_ABS) { MCOperand_CreateImm0(MI, bpf->k); return true; } else if (BPF_MODE(bpf->op) == BPF_MODE_IND) { MCOperand_CreateReg0(MI, BPF_REG_X); MCOperand_CreateImm0(MI, bpf->k); return true; } } else { /* LDX */ if (BPF_MODE(bpf->op) == BPF_MODE_MSH) { MCOperand_CreateImm0(MI, bpf->k); return true; } } return false; } /* eBPF mode */ /* * - IMM: lddw dst, imm64 * - ABS: ld{w,h,b} [k] * - IND: ld{w,h,b} [src] * - MEM: ldx{w,h,b,dw} dst, [src+off] */ if (BPF_CLASS(bpf->op) == BPF_CLASS_LD) { switch (BPF_MODE(bpf->op)) { case BPF_MODE_IMM: if (bpf->op != (BPF_CLASS_LD | BPF_SIZE_DW | BPF_MODE_IMM)) return false; CHECK_WRITABLE_AND_PUSH(ud, MI, bpf->dst); MCOperand_CreateImm0(MI, bpf->k); return true; case BPF_MODE_ABS: MCOperand_CreateImm0(MI, bpf->k); return true; case BPF_MODE_IND: CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); return true; } return false; } /* LDX */ if (BPF_MODE(bpf->op) == BPF_MODE_MEM) { CHECK_WRITABLE_AND_PUSH(ud, MI, bpf->dst); CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); MCOperand_CreateImm0(MI, bpf->offset); return true; } return false; } static bool decodeStore(MCInst *MI, bpf_internal *bpf) { /* in cBPF, only BPF_ST* | BPF_MEM | BPF_W is valid * while in eBPF: * - BPF_STX | BPF_XADD | BPF_{W,DW} * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW} * are valid */ 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; MCOperand_CreateImm0(MI, bpf->k); return true; } /* eBPF */ 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) return false; /* xadd [dst + off], src */ CHECK_READABLE_AND_PUSH(ud, MI, bpf->dst); MCOperand_CreateImm0(MI, bpf->offset); CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); return true; } if (BPF_MODE(bpf->op) != BPF_MODE_MEM) return false; /* st [dst + off], src */ CHECK_READABLE_AND_PUSH(ud, MI, bpf->dst); MCOperand_CreateImm0(MI, bpf->offset); if (BPF_CLASS(bpf->op) == BPF_CLASS_ST) MCOperand_CreateImm0(MI, bpf->k); else CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); return true; } static bool decodeALU(MCInst *MI, bpf_internal *bpf) { /* Set MI->Operands */ /* cBPF */ if (!EBPF_MODE(MI->csh->mode)) { if (BPF_OP(bpf->op) > BPF_ALU_XOR) return false; /* cBPF's NEG has no operands */ if (BPF_OP(bpf->op) == BPF_ALU_NEG) return true; if (BPF_SRC(bpf->op) == BPF_SRC_K) MCOperand_CreateImm0(MI, bpf->k); else /* BPF_SRC_X */ MCOperand_CreateReg0(MI, BPF_REG_X); return true; } /* eBPF */ if (BPF_OP(bpf->op) > BPF_ALU_END) return false; /* ENDian's imm must be one of 16, 32, 64 */ 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) return false; } /* - op dst, imm * - op dst, src * - neg dst * - le dst */ /* every ALU instructions have dst op */ CHECK_WRITABLE_AND_PUSH(ud, MI, bpf->dst); /* special cases */ if (BPF_OP(bpf->op) == BPF_ALU_NEG) return true; if (BPF_OP(bpf->op) == BPF_ALU_END) { /* bpf->k must be one of 16, 32, 64 */ bpf->op |= ((uint32_t)bpf->k << 4); return true; } /* normal cases */ if (BPF_SRC(bpf->op) == BPF_SRC_K) { MCOperand_CreateImm0(MI, bpf->k); } else { /* BPF_SRC_X */ CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); } return true; } static bool decodeJump(MCInst *MI, bpf_internal *bpf) { /* cBPF and eBPF are very different in class jump */ if (!EBPF_MODE(MI->csh->mode)) { if (BPF_OP(bpf->op) > BPF_JUMP_JSET) return false; /* ja is a special case of jumps */ if (BPF_OP(bpf->op) == BPF_JUMP_JA) { MCOperand_CreateImm0(MI, bpf->k); return true; } if (BPF_SRC(bpf->op) == BPF_SRC_K) MCOperand_CreateImm0(MI, bpf->k); else /* BPF_SRC_X */ MCOperand_CreateReg0(MI, BPF_REG_X); MCOperand_CreateImm0(MI, bpf->jt); MCOperand_CreateImm0(MI, bpf->jf); } else { if (BPF_OP(bpf->op) > BPF_JUMP_JSLE) return false; /* JMP32 has no CALL/EXIT instruction */ /* No operands for exit */ if (BPF_OP(bpf->op) == BPF_JUMP_EXIT) return bpf->op == (BPF_CLASS_JMP | BPF_JUMP_EXIT); if (BPF_OP(bpf->op) == BPF_JUMP_CALL) { if (bpf->op == (BPF_CLASS_JMP | BPF_JUMP_CALL)) { MCOperand_CreateImm0(MI, bpf->k); return true; } if (bpf->op == (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) { CHECK_READABLE_AND_PUSH(ud, MI, bpf->k); return true; } return false; } /* ja is a special case of jumps */ if (BPF_OP(bpf->op) == BPF_JUMP_JA) { if (BPF_SRC(bpf->op) != BPF_SRC_K) return false; if (BPF_CLASS(bpf->op) == BPF_CLASS_JMP) MCOperand_CreateImm0(MI, bpf->offset); else MCOperand_CreateImm0(MI, bpf->k); return true; } /* dst, src, +off */ CHECK_READABLE_AND_PUSH(ud, MI, bpf->dst); if (BPF_SRC(bpf->op) == BPF_SRC_K) MCOperand_CreateImm0(MI, bpf->k); else CHECK_READABLE_AND_PUSH(ud, MI, bpf->src); MCOperand_CreateImm0(MI, bpf->offset); } return true; } static bool decodeReturn(MCInst *MI, bpf_internal *bpf) { /* Here only handles the BPF_RET class in cBPF */ switch (BPF_RVAL(bpf->op)) { case BPF_SRC_K: MCOperand_CreateImm0(MI, bpf->k); return true; case BPF_SRC_X: MCOperand_CreateReg0(MI, BPF_REG_X); return true; case BPF_SRC_A: MCOperand_CreateReg0(MI, BPF_REG_A); return true; } return false; } 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; } ///< 1. Check if the instruction is valid ///< 2. Set MI->opcode ///< 3. Set MI->Operands static bool getInstruction(MCInst *MI, bpf_internal *bpf) { cs_detail *detail; detail = MI->flat_insn->detail; // initialize detail if (detail) { memset(detail, 0, offsetof(cs_detail, bpf) + sizeof(cs_bpf)); } MCInst_clear(MI); switch (BPF_CLASS(bpf->op)) { default: /* should never happen */ return false; case BPF_CLASS_LD: case BPF_CLASS_LDX: return decodeLoad(MI, bpf); case BPF_CLASS_ST: case BPF_CLASS_STX: return decodeStore(MI, bpf); case BPF_CLASS_ALU: return decodeALU(MI, bpf); case BPF_CLASS_JMP: return decodeJump(MI, bpf); case BPF_CLASS_RET: /* case BPF_CLASS_JMP32: */ if (EBPF_MODE(MI->csh->mode)) return decodeJump(MI, bpf); else return decodeReturn(MI, bpf); case BPF_CLASS_MISC: /* case BPF_CLASS_ALU64: */ if (EBPF_MODE(MI->csh->mode)) return decodeALU(MI, bpf); else return decodeMISC(MI, bpf); } } // Check for regular load instructions #define REG_LOAD_CASE(c) \ case BPF_SIZE_##c: \ if (BPF_CLASS(opcode) == BPF_CLASS_LD) \ return BPF_INS_LD##c; \ else \ return BPF_INS_LDX##c; static bpf_insn op2insn_ld_cbpf(unsigned opcode) { switch (BPF_SIZE(opcode)) { REG_LOAD_CASE(W); REG_LOAD_CASE(H); REG_LOAD_CASE(B); REG_LOAD_CASE(DW); } return BPF_INS_INVALID; } #undef REG_LOAD_CASE // Check for packet load instructions #define PACKET_LOAD_CASE(c) \ case BPF_SIZE_##c: \ if (BPF_MODE(opcode) == BPF_MODE_ABS) \ return BPF_INS_LDABS##c; \ else if (BPF_MODE(opcode) == BPF_MODE_IND) \ return BPF_INS_LDIND##c; \ else \ return BPF_INS_INVALID; static bpf_insn op2insn_ld_ebpf(unsigned opcode) { if (BPF_CLASS(opcode) == BPF_CLASS_LD) { switch (BPF_SIZE(opcode)) { PACKET_LOAD_CASE(W); PACKET_LOAD_CASE(H); PACKET_LOAD_CASE(B); } } // If it's not a packet load instruction, it must be a regular load instruction return op2insn_ld_cbpf(opcode); } #undef PACKET_LOAD_CASE /* During parsing we already checked to make sure the size is D/DW and * mode is STX and not ST, so we don't need to check again*/ #define ALU_CASE_REG(c) \ case BPF_ALU_##c: \ if (BPF_SIZE(opcode) == BPF_SIZE_W) \ return BPF_INS_A##c; \ else \ return BPF_INS_A##c##64; #define ALU_CASE_FETCH(c) \ case BPF_ALU_##c | BPF_MODE_FETCH: \ if (BPF_SIZE(opcode) == BPF_SIZE_W) \ return BPF_INS_AF##c; \ else \ return BPF_INS_AF##c##64; #define COMPLEX_CASE(c) \ case BPF_ATOMIC_##c | BPF_MODE_FETCH: \ if (BPF_SIZE(opcode) == BPF_SIZE_DW) \ return BPF_INS_A##c##64; #define CASE(c) \ case BPF_SIZE_##c: \ if (BPF_CLASS(opcode) == BPF_CLASS_ST) \ return BPF_INS_ST##c; \ else \ return BPF_INS_STX##c; static bpf_insn op2insn_st(unsigned opcode, const uint32_t imm) { /* * - BPF_STX | ALU atomic operations | BPF_{W,DW} * - BPF_STX | Complex atomic operations | BPF_{DW} * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW} */ if (BPF_MODE(opcode) == BPF_MODE_ATOMIC) { switch (imm) { ALU_CASE_REG(ADD); ALU_CASE_REG(OR); ALU_CASE_REG(AND); ALU_CASE_REG(XOR); ALU_CASE_FETCH(ADD); ALU_CASE_FETCH(OR); ALU_CASE_FETCH(AND); ALU_CASE_FETCH(XOR); COMPLEX_CASE(XCHG); COMPLEX_CASE(CMPXCHG); default: // Could only be reached if complex atomic operation is used without fetch modifier, or not as DW return BPF_INS_INVALID; } } /* should be BPF_MEM */ switch (BPF_SIZE(opcode)) { CASE(W); CASE(H); CASE(B); CASE(DW); } CS_ASSERT_RET_VAL( false && "Malformed atomic BPF instruction", BPF_INS_INVALID); } #undef CASE #define CASE(c) \ case BPF_ALU_##c: \ CASE_IF(c) #define CASE_IF(c) \ do { \ if (BPF_CLASS(opcode) == BPF_CLASS_ALU) \ return BPF_INS_##c; \ else \ return BPF_INS_##c##64; \ } while (0) static bpf_insn op2insn_alu(unsigned opcode, const uint16_t off, const bool is_ebpf) { /* Endian is a special case */ if (BPF_OP(opcode) == BPF_ALU_END) { if (BPF_CLASS(opcode) == BPF_CLASS_ALU64) { switch (opcode ^ BPF_CLASS_ALU64 ^ BPF_ALU_END ^ BPF_SRC_LITTLE) { case (16 << 4): return BPF_INS_BSWAP16; case (32 << 4): return BPF_INS_BSWAP32; case (64 << 4): return BPF_INS_BSWAP64; default: return BPF_INS_INVALID; } } switch (opcode ^ BPF_CLASS_ALU ^ BPF_ALU_END) { case BPF_SRC_LITTLE | (16 << 4): return BPF_INS_LE16; case BPF_SRC_LITTLE | (32 << 4): return BPF_INS_LE32; case BPF_SRC_LITTLE | (64 << 4): return BPF_INS_LE64; case BPF_SRC_BIG | (16 << 4): return BPF_INS_BE16; case BPF_SRC_BIG | (32 << 4): return BPF_INS_BE32; case BPF_SRC_BIG | (64 << 4): return BPF_INS_BE64; } return BPF_INS_INVALID; } switch (BPF_OP(opcode)) { CASE(ADD); CASE(SUB); CASE(MUL); CASE(OR); CASE(AND); CASE(LSH); CASE(RSH); CASE(NEG); CASE(XOR); CASE(ARSH); case BPF_ALU_DIV: if (!is_ebpf || off == 0) CASE_IF(DIV); else if (off == 1) CASE_IF(SDIV); else return BPF_INS_INVALID; case BPF_ALU_MOD: if (!is_ebpf || off == 0) CASE_IF(MOD); else if (off == 1) CASE_IF(SMOD); else return BPF_INS_INVALID; case BPF_ALU_MOV: /* BPF_CLASS_ALU can have: mov, mov8s, mov16s * BPF_CLASS_ALU64 can have: mov, mov8s, mov16s, mov32s * */ if (off == 0) CASE_IF(MOV); else if (off == 8) CASE_IF(MOVSB); else if (off == 16) CASE_IF(MOVSH); else if (off == 32 && BPF_CLASS(opcode) == BPF_CLASS_ALU64) return BPF_INS_MOVSW64; else return BPF_INS_INVALID; } return BPF_INS_INVALID; } #undef CASE_IF #undef CASE #define BPF_CALLX (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X) #define CASE(c) \ case BPF_JUMP_##c: \ if (BPF_CLASS(opcode) == BPF_CLASS_JMP) \ return BPF_INS_##c; \ else \ return BPF_INS_##c##32; #define SPEC_CASE(c) \ case BPF_JUMP_##c: \ if (BPF_CLASS(opcode) == BPF_CLASS_JMP) \ return BPF_INS_##c; \ else \ return BPF_INS_INVALID; static bpf_insn op2insn_jmp(unsigned opcode) { if (opcode == BPF_CALLX) { return BPF_INS_CALLX; } switch (BPF_OP(opcode)) { case BPF_JUMP_JA: if (BPF_CLASS(opcode) == BPF_CLASS_JMP) return BPF_INS_JA; else return BPF_INS_JAL; CASE(JEQ); CASE(JGT); CASE(JGE); CASE(JSET); CASE(JNE); CASE(JSGT); CASE(JSGE); SPEC_CASE(CALL); SPEC_CASE(EXIT); CASE(JLT); CASE(JLE); CASE(JSLT); CASE(JSLE); } return BPF_INS_INVALID; } #undef SPEC_CASE #undef CASE #undef BPF_CALLX #ifndef CAPSTONE_DIET static void update_regs_access(MCInst *MI, cs_detail *detail, bpf_insn insn_id, unsigned int opcode) { if (insn_id == BPF_INS_INVALID) return; /* * In eBPF mode, only these instructions have implicit registers access: * - legacy ld{w,h,b,dw} * // w: r0 * - exit // r: r0 */ if (EBPF_MODE(MI->csh->mode)) { switch (insn_id) { default: break; case BPF_INS_LDABSW: case BPF_INS_LDABSH: case BPF_INS_LDABSB: case BPF_INS_LDINDW: case BPF_INS_LDINDH: case BPF_INS_LDINDB: case BPF_INS_LDDW: if (BPF_MODE(opcode) == BPF_MODE_ABS || BPF_MODE(opcode) == BPF_MODE_IND) map_add_implicit_write(MI, BPF_REG_R0); break; case BPF_INS_EXIT: map_add_implicit_read(MI, BPF_REG_R0); break; } return; } /* cBPF mode */ switch (BPF_CLASS(opcode)) { default: break; case BPF_CLASS_LD: map_add_implicit_write(MI, BPF_REG_A); break; case BPF_CLASS_LDX: map_add_implicit_write(MI, BPF_REG_X); break; case BPF_CLASS_ST: map_add_implicit_read(MI, BPF_REG_A); break; case BPF_CLASS_STX: map_add_implicit_read(MI, BPF_REG_X); break; case BPF_CLASS_ALU: map_add_implicit_read(MI, BPF_REG_A); map_add_implicit_write(MI, BPF_REG_A); break; case BPF_CLASS_JMP: if (insn_id != BPF_INS_JA) // except the unconditional jump map_add_implicit_read(MI, BPF_REG_A); break; /* case BPF_CLASS_RET: */ case BPF_CLASS_MISC: if (insn_id == BPF_INS_TAX) { map_add_implicit_read(MI, BPF_REG_A); map_add_implicit_write(MI, BPF_REG_X); } else { map_add_implicit_read(MI, BPF_REG_X); map_add_implicit_write(MI, BPF_REG_A); } break; } } #endif static bool setFinalOpcode(MCInst *MI, const bpf_internal *bpf) { bpf_insn id = BPF_INS_INVALID; #ifndef CAPSTONE_DIET cs_detail *detail; detail = get_detail(MI); #endif const uint16_t opcode = bpf->op; switch (BPF_CLASS(opcode)) { default: // will never happen break; case BPF_CLASS_LD: case BPF_CLASS_LDX: if (EBPF_MODE(MI->csh->mode)) id = op2insn_ld_ebpf(opcode); else id = op2insn_ld_cbpf(opcode); add_group(MI, BPF_GRP_LOAD); break; case BPF_CLASS_ST: case BPF_CLASS_STX: id = op2insn_st(opcode, bpf->k); add_group(MI, BPF_GRP_STORE); break; case BPF_CLASS_ALU: id = op2insn_alu(opcode, bpf->offset, EBPF_MODE(MI->csh->mode)); add_group(MI, BPF_GRP_ALU); break; case BPF_CLASS_JMP: id = op2insn_jmp(opcode); #ifndef CAPSTONE_DIET if (id == BPF_INS_CALL || id == BPF_INS_CALLX) add_group(MI, BPF_GRP_CALL); else if (id == BPF_INS_EXIT) add_group(MI, BPF_GRP_RETURN); else add_group(MI, BPF_GRP_JUMP); #endif break; case BPF_CLASS_RET: /* case BPF_CLASS_JMP32: */ if (EBPF_MODE(MI->csh->mode)) { id = op2insn_jmp(opcode); add_group(MI, BPF_GRP_JUMP); } else { id = BPF_INS_RET; add_group(MI, BPF_GRP_RETURN); } break; // BPF_CLASS_MISC and BPF_CLASS_ALU64 have exactly same value case BPF_CLASS_MISC: /* case BPF_CLASS_ALU64: */ if (EBPF_MODE(MI->csh->mode)) { // ALU64 in eBPF id = op2insn_alu(opcode, bpf->offset, true); add_group(MI, BPF_GRP_ALU); } else { if (BPF_MISCOP(opcode) == BPF_MISCOP_TXA) id = BPF_INS_TXA; else id = BPF_INS_TAX; add_group(MI, BPF_GRP_MISC); } break; } if (id == BPF_INS_INVALID) return false; MCInst_setOpcodePub(MI, id); #undef PUSH_GROUP #ifndef CAPSTONE_DIET if (detail) { update_regs_access(MI, detail, id, opcode); } #endif return true; } bool BPF_getInstruction(csh ud, const uint8_t *code, size_t code_len, MCInst *instr, uint16_t *size, uint64_t address, void *info) { bpf_internal *bpf; if (EBPF_MODE(instr->csh->mode)) bpf = fetch_ebpf(instr, code, code_len); else bpf = fetch_cbpf(instr, code, code_len); if (bpf == NULL) return false; if (!getInstruction(instr, bpf) || !setFinalOpcode(instr, bpf)) { cs_mem_free(bpf); return false; } MCInst_setOpcode(instr, bpf->op); *size = bpf->insn_size; cs_mem_free(bpf); return true; } #endif