Coverage Report

Created: 2024-09-08 06:22

/src/capstonev5/arch/BPF/BPFInstPrinter.c
Line
Count
Source (jump to first uncovered line)
1
/* Capstone Disassembly Engine */
2
/* BPF Backend by david942j <david942j@gmail.com>, 2019 */
3
4
#include <capstone/platform.h>
5
6
#include "BPFConstants.h"
7
#include "BPFInstPrinter.h"
8
#include "BPFMapping.h"
9
10
static cs_bpf_op *expand_bpf_operands(cs_bpf *bpf)
11
47.9k
{
12
  /* assert(bpf->op_count < 3); */
13
47.9k
  return &bpf->operands[bpf->op_count++];
14
47.9k
}
15
16
static void push_op_reg(cs_bpf *bpf, bpf_op_type val, uint8_t ac_mode)
17
15.9k
{
18
15.9k
  cs_bpf_op *op = expand_bpf_operands(bpf);
19
20
15.9k
  op->type = BPF_OP_REG;
21
15.9k
  op->reg = val;
22
15.9k
  op->access = ac_mode;
23
15.9k
}
24
25
static void push_op_imm(cs_bpf *bpf, uint64_t val)
26
14.2k
{
27
14.2k
  cs_bpf_op *op = expand_bpf_operands(bpf);
28
29
14.2k
  op->type = BPF_OP_IMM;
30
14.2k
  op->imm = val;
31
14.2k
}
32
33
static void push_op_off(cs_bpf *bpf, uint32_t val)
34
6.71k
{
35
6.71k
  cs_bpf_op *op = expand_bpf_operands(bpf);
36
37
6.71k
  op->type = BPF_OP_OFF;
38
6.71k
  op->off = val;
39
6.71k
}
40
41
static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val)
42
9.79k
{
43
9.79k
  cs_bpf_op *op = expand_bpf_operands(bpf);
44
45
9.79k
  op->type = BPF_OP_MEM;
46
9.79k
  op->mem.base = reg;
47
9.79k
  op->mem.disp = val;
48
9.79k
}
49
50
static void push_op_mmem(cs_bpf *bpf, uint32_t val)
51
531
{
52
531
  cs_bpf_op *op = expand_bpf_operands(bpf);
53
54
531
  op->type = BPF_OP_MMEM;
55
531
  op->mmem = val;
56
531
}
57
58
static void push_op_msh(cs_bpf *bpf, uint32_t val)
59
329
{
60
329
  cs_bpf_op *op = expand_bpf_operands(bpf);
61
62
329
  op->type = BPF_OP_MSH;
63
329
  op->msh = val;
64
329
}
65
66
static void push_op_ext(cs_bpf *bpf, bpf_ext_type val)
67
401
{
68
401
  cs_bpf_op *op = expand_bpf_operands(bpf);
69
70
401
  op->type = BPF_OP_EXT;
71
401
  op->ext = val;
72
401
}
73
74
static void convert_operands(MCInst *MI, cs_bpf *bpf)
75
31.4k
{
76
31.4k
  unsigned opcode = MCInst_getOpcode(MI);
77
31.4k
  unsigned mc_op_count = MCInst_getNumOperands(MI);
78
31.4k
  MCOperand *op;
79
31.4k
  MCOperand *op2;
80
31.4k
  unsigned i;
81
82
31.4k
  bpf->op_count = 0;
83
31.4k
  if (BPF_CLASS(opcode) == BPF_CLASS_LD || BPF_CLASS(opcode) == BPF_CLASS_LDX) {
84
10.0k
    switch (BPF_MODE(opcode)) {
85
2.23k
    case BPF_MODE_IMM:
86
2.23k
      if (EBPF_MODE(MI->csh)) {
87
577
        push_op_reg(bpf, MCOperand_getReg(MCInst_getOperand(MI, 0)), CS_AC_WRITE);
88
577
        push_op_imm(bpf, MCOperand_getImm(MCInst_getOperand(MI, 1)));
89
1.65k
      } else {
90
1.65k
        push_op_imm(bpf, MCOperand_getImm(MCInst_getOperand(MI, 0)));
91
1.65k
      }
92
2.23k
      break;
93
2.67k
    case BPF_MODE_ABS:
94
2.67k
      op = MCInst_getOperand(MI, 0);
95
2.67k
      push_op_mem(bpf, BPF_REG_INVALID, (uint32_t)MCOperand_getImm(op));
96
2.67k
      break;
97
2.62k
    case BPF_MODE_IND:
98
2.62k
      op = MCInst_getOperand(MI, 0);
99
2.62k
      op2 = MCInst_getOperand(MI, 1);
100
2.62k
      push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2));
101
2.62k
      break;
102
1.79k
    case BPF_MODE_MEM:
103
1.79k
      if (EBPF_MODE(MI->csh)) {
104
        /* ldx{w,h,b,dw} dst, [src+off] */
105
1.38k
        push_op_reg(bpf, MCOperand_getReg(MCInst_getOperand(MI, 0)), CS_AC_WRITE);
106
1.38k
        op = MCInst_getOperand(MI, 1);
107
1.38k
        op2 = MCInst_getOperand(MI, 2);
108
1.38k
        push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2));
109
1.38k
      }
110
401
      else {
111
401
        push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0)));
112
401
      }
113
1.79k
      break;
114
401
    case BPF_MODE_LEN:
115
401
      push_op_ext(bpf, BPF_EXT_LEN);
116
401
      break;
117
329
    case BPF_MODE_MSH:
118
329
      op = MCInst_getOperand(MI, 0);
119
329
      push_op_msh(bpf, (uint32_t)MCOperand_getImm(op));
120
329
      break;
121
    /* case BPF_MODE_XADD: // not exists */
122
10.0k
    }
123
10.0k
    return;
124
10.0k
  }
125
21.3k
  if (BPF_CLASS(opcode) == BPF_CLASS_ST || BPF_CLASS(opcode) == BPF_CLASS_STX) {
126
3.23k
    if (!EBPF_MODE(MI->csh)) {
127
      // cBPF has only one case - st* M[k]
128
130
      push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0)));
129
130
      return;
130
130
    }
131
    /* eBPF has two cases:
132
     * - st [dst + off], src
133
     * - xadd [dst + off], src
134
     * they have same form of operands.
135
     */
136
3.10k
    op = MCInst_getOperand(MI, 0);
137
3.10k
    op2 = MCInst_getOperand(MI, 1);
138
3.10k
    push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2));
139
3.10k
    op = MCInst_getOperand(MI, 2);
140
3.10k
    if (MCOperand_isImm(op))
141
1.38k
      push_op_imm(bpf, MCOperand_getImm(op));
142
1.71k
    else if (MCOperand_isReg(op))
143
1.71k
      push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ);
144
3.10k
    return;
145
3.23k
  }
146
147
18.1k
  if (BPF_CLASS(opcode) == BPF_CLASS_JMP) {
148
21.9k
    for (i = 0; i < mc_op_count; i++) {
149
14.8k
      op = MCInst_getOperand(MI, i);
150
14.8k
      if (MCOperand_isImm(op)) {
151
        /* decide the imm is BPF_OP_IMM or BPF_OP_OFF type here */
152
        /*
153
         * 1. ja +off
154
         * 2. j {x,k}, +jt, +jf // cBPF
155
         * 3. j dst_reg, {src_reg, k}, +off // eBPF
156
         */
157
11.0k
        if (BPF_OP(opcode) == BPF_JUMP_JA ||
158
11.0k
            (!EBPF_MODE(MI->csh) && i >= 1) ||
159
11.0k
            (EBPF_MODE(MI->csh) && i == 2))
160
6.71k
          push_op_off(bpf, (uint32_t)MCOperand_getImm(op));
161
4.38k
        else
162
4.38k
          push_op_imm(bpf, MCOperand_getImm(op));
163
11.0k
      }
164
3.78k
      else if (MCOperand_isReg(op)) {
165
3.78k
        push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ);
166
3.78k
      }
167
14.8k
    }
168
7.07k
    return;
169
7.07k
  }
170
171
11.0k
  if (!EBPF_MODE(MI->csh)) {
172
    /* In cBPF mode, all registers in operands are accessed as read */
173
10.7k
    for (i = 0; i < mc_op_count; i++) {
174
5.05k
      op = MCInst_getOperand(MI, i);
175
5.05k
      if (MCOperand_isImm(op))
176
2.53k
        push_op_imm(bpf, MCOperand_getImm(op));
177
2.51k
      else if (MCOperand_isReg(op))
178
2.51k
        push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ);
179
5.05k
    }
180
5.73k
    return;
181
5.73k
  }
182
183
  /* remain cases are: eBPF mode && ALU */
184
  /* if (BPF_CLASS(opcode) == BPF_CLASS_ALU || BPF_CLASS(opcode) == BPF_CLASS_ALU64) */
185
186
  /* We have three types:
187
   * 1. {l,b}e dst               // dst = byteswap(dst)
188
   * 2. neg dst                  // dst = -dst
189
   * 3. <op> dst, {src_reg, imm} // dst = dst <op> src
190
   * so we can simply check the number of operands,
191
   * exactly one operand means we are in case 1. and 2.,
192
   * otherwise in case 3.
193
   */
194
5.31k
  if (mc_op_count == 1) {
195
1.01k
    op = MCInst_getOperand(MI, 0);
196
1.01k
    push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE);
197
1.01k
  }
198
4.30k
  else { // if (mc_op_count == 2)
199
4.30k
    op = MCInst_getOperand(MI, 0);
200
4.30k
    push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE);
201
202
4.30k
    op = MCInst_getOperand(MI, 1);
203
4.30k
    if (MCOperand_isImm(op))
204
3.68k
      push_op_imm(bpf, MCOperand_getImm(op));
205
617
    else if (MCOperand_isReg(op))
206
617
      push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ);
207
4.30k
  }
208
5.31k
}
209
210
static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op)
211
47.9k
{
212
47.9k
  switch (op->type) {
213
0
  case BPF_OP_INVALID:
214
0
    SStream_concat(O, "invalid");
215
0
    break;
216
15.9k
  case BPF_OP_REG:
217
15.9k
    SStream_concat(O, BPF_reg_name((csh)MI->csh, op->reg));
218
15.9k
    break;
219
14.2k
  case BPF_OP_IMM:
220
14.2k
    SStream_concat(O, "0x%" PRIx64, op->imm);
221
14.2k
    break;
222
6.71k
  case BPF_OP_OFF:
223
6.71k
    SStream_concat(O, "+0x%x", op->off);
224
6.71k
    break;
225
9.79k
  case BPF_OP_MEM:
226
9.79k
    SStream_concat(O, "[");
227
9.79k
    if (op->mem.base != BPF_REG_INVALID)
228
7.11k
      SStream_concat(O, BPF_reg_name((csh)MI->csh, op->mem.base));
229
9.79k
    if (op->mem.disp != 0) {
230
9.69k
      if (op->mem.base != BPF_REG_INVALID)
231
7.08k
        SStream_concat(O, "+");
232
9.69k
      SStream_concat(O, "0x%x", op->mem.disp);
233
9.69k
    }
234
9.79k
    if (op->mem.base == BPF_REG_INVALID && op->mem.disp == 0) // special case
235
67
      SStream_concat(O, "0x0");
236
9.79k
    SStream_concat(O, "]");
237
9.79k
    break;
238
531
  case BPF_OP_MMEM:
239
531
    SStream_concat(O, "m[0x%x]", op->mmem);
240
531
    break;
241
329
  case BPF_OP_MSH:
242
329
    SStream_concat(O, "4*([0x%x]&0xf)", op->msh);
243
329
    break;
244
401
  case BPF_OP_EXT:
245
401
    switch (op->ext) {
246
401
    case BPF_EXT_LEN:
247
401
      SStream_concat(O, "#len");
248
401
      break;
249
401
    }
250
401
    break;
251
47.9k
  }
252
47.9k
}
253
254
/*
255
 * 1. human readable mnemonic
256
 * 2. set pubOpcode (BPF_INSN_*)
257
 * 3. set detail->bpf.operands
258
 * */
259
void BPF_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo)
260
31.4k
{
261
31.4k
  int i;
262
31.4k
  cs_insn insn;
263
31.4k
  cs_bpf bpf;
264
265
31.4k
  insn.detail = NULL;
266
  /* set pubOpcode as instruction id */
267
31.4k
  BPF_get_insn_id((cs_struct*)MI->csh, &insn, MCInst_getOpcode(MI));
268
31.4k
  MCInst_setOpcodePub(MI, insn.id);
269
270
31.4k
  SStream_concat(O, BPF_insn_name((csh)MI->csh, insn.id));
271
31.4k
  convert_operands(MI, &bpf);
272
79.3k
  for (i = 0; i < bpf.op_count; i++) {
273
47.9k
    if (i == 0)
274
30.1k
      SStream_concat(O, "\t");
275
17.7k
    else
276
17.7k
      SStream_concat(O, ", ");
277
47.9k
    print_operand(MI, O, &bpf.operands[i]);
278
47.9k
  }
279
280
31.4k
#ifndef CAPSTONE_DIET
281
31.4k
  if (MI->flat_insn->detail) {
282
31.4k
    MI->flat_insn->detail->bpf = bpf;
283
31.4k
  }
284
31.4k
#endif
285
31.4k
}