Files
kaizen/arch/BPF/BPFDisassembler.c
Simone 3621a6c080 Squashed 'external/capstone/' changes from 5430745e..b102f1b8
b102f1b8 Update Actions (#2593)
86293136 Fix LoongArch aliases and CS_OPT_SYNTAX_NO_DOLLAR support (#2594)
27da950c Clarify between machine used vs. Capstone module affected. (#2586)
186f7aa0 Fix linking issue on Windows. (#2587)
e160cbc5 Fix complex atomic instructions handling (#2584)
9907b22d Update v6 to have Debian Packages (#2579)
efbbc3bb cstest: use DOWNLOAD_EXTRACT_TIMESTAMP conditionally (#2581)
be6be784 x86: update read/write registers for transfer instructions (#2578)
812e654c Update BPF arch (#2568)
2c4b05f6 Clean up the cstest documentation and build instructions. (#2580)
4dc14ba1 Fix 2572 (#2574)
b25aa841 PPC regressions (#2575)
0a29bf80 Small arm64 compat header fixes (#2563)
b42e0903 Make thumb, v8 and m-class positional cstool arguments. (#2557)
89aee400 Add arm64 and sysz compatibility layer to Python bindings (#2559)
a4281337 Python bindings: Enable more archs + bump cibuildwheel action to the v2.22.0 (#2558)
ef74d449 Arm regressions (#2556)
93a104c0 PPC LLVM 18 (#2540)
e46838ed Merge branch 'v6' into next
cf3600e7 Update Changelog Version to 6.0.0-Alpha2 (#2553)
b295cf57 Prepare for update (#2552)
fc59da4d fix xtensa DecodeMR23RegisterClass and add tests for MAC16 instru… (#2551)
7d01d7e7 Auto-Sync reproducability + ARM update (#2532)
6ad2608d Python package building rework (#2538)
e3bc578d Move debian package generation to a dispatch only workflow (#2543)
abbf32b4 fix coverity (#2546)
1ecfb5b0 xtensa: update to espressif/llvm-project (#2533)
379e2a41 Rename build arguments: (#2534)
d7be5f9f Change CI to create Debian Package to Release (#2521)
f6f96796 tricore: fixes #2474 (#2523)
09f35961 This time actually fix big endian issue. (#2530)
306d5716 Fix endianess issue during assignment. (#2528)
2cfca35e Add CC and VAS compatibility macros (#2525)
32519c01 Fix stringop-truncation warning some compilers raise. (#2522)
5026c2c4 Merge pull request #2507 from thestr4ng3r/no-varargs-aarch64
cecb5ede Fix #2509. (#2510)
f97e2705 xtensa: Fix Branch Target (#2516)
1d13a12f AArch64: Replace vararg add_cs_detail by multiple concrete functions
8b618528 Update libcyaml dependency in cstest to 1.4.2 (#2508)
ea081286 Tricore EA calculation (#2504)
7db9a080 Fix cstest build with Ninja (#2506)
76242699 Only trigger on released action. (#2497)
981d648b Add hard asserts to all SStream functions and memset MCInst. (#2501)
d667a627 Update labeler with Xtensa and v6 files. (#2500)
52b54ee3 Fixing UB santizer, `LITBASE` and assert errors. (#2499)
97db712c Remove irrelevant changes. (#2496)
5bd05e34 Remove irrelevant changes. (#2495)
616488c7 Update changelog for V6.0.0-Alpha1 (#2493) (#2494)
c5955b92 Update changelog for V6.0.0-Alpha1 (#2493)
a424e709 Be ready for V6-Alpha1 (#2492)
235ba8e0 SystemZ fixes (#2488)
5dffa75b Fix LDR not assigning immediate as memory offset. (#2487)
21f7bc85 Xtensa Support (#2380)
29d87734 Several small fixups (#2489)
a34901e9 Update sponsors and remove empty file. (#2485)
3120932d Fix Coverity CID 509730: overflow before widen (#2486)
1014864d Rename CS_OPT_NO_BRANCH_OFFSET and corresponding flag to better name. (#2482)
0c90fe13 Replace `assert` with `CS_ASSERT` in modules (#2478)
823bfd53 AArch64 issues (#2473)

git-subtree-dir: external/capstone
git-subtree-split: b102f1b89e0455c072a751d287ab64378c14205f
2025-01-07 15:08:55 +00:00

870 lines
20 KiB
C

/* Capstone Disassembly Engine */
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
/* SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com> */
/* SPDX-License-Identifier: BSD-3 */
#ifdef CAPSTONE_HAS_BPF
#include <string.h>
#include <stddef.h> // 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<imm> 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;
}
/* <j> 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; \
else \
return BPF_INS_INVALID;
#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: // Reached if complex atomic operation is used without fetch modifier
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