Coverage Report

Created: 2025-07-01 07:03

/src/capstonenext/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
/* SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com> */
4
/* SPDX-License-Identifier: BSD-3 */
5
6
#include <capstone/platform.h>
7
8
#include "BPFConstants.h"
9
#include "BPFInstPrinter.h"
10
#include "BPFMapping.h"
11
#include "../../Mapping.h"
12
13
static cs_bpf_op *expand_bpf_operands(cs_bpf *bpf)
14
28.2k
{
15
28.2k
  assert(bpf->op_count < 3);
16
28.2k
  return &bpf->operands[bpf->op_count++];
17
28.2k
}
18
19
static void push_op_reg(cs_bpf *bpf, bpf_op_type val, uint8_t ac_mode)
20
10.2k
{
21
10.2k
  cs_bpf_op *op = expand_bpf_operands(bpf);
22
23
10.2k
  op->type = BPF_OP_REG;
24
10.2k
  op->reg = val;
25
10.2k
  op->access = ac_mode;
26
10.2k
}
27
28
static void push_op_imm(cs_bpf *bpf, uint64_t val, const bool is_signed)
29
7.46k
{
30
7.46k
  cs_bpf_op *op = expand_bpf_operands(bpf);
31
32
7.46k
  op->type = BPF_OP_IMM;
33
7.46k
  op->imm = val;
34
7.46k
  op->is_signed = is_signed;
35
7.46k
}
36
37
static void push_op_off(cs_bpf *bpf, uint32_t val, const bool is_signed)
38
4.73k
{
39
4.73k
  cs_bpf_op *op = expand_bpf_operands(bpf);
40
41
4.73k
  op->type = BPF_OP_OFF;
42
4.73k
  op->off = val;
43
4.73k
  op->is_signed = is_signed;
44
4.73k
}
45
46
static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val,
47
      const bool is_signed, const bool is_pkt)
48
5.15k
{
49
5.15k
  cs_bpf_op *op = expand_bpf_operands(bpf);
50
51
5.15k
  op->type = BPF_OP_MEM;
52
5.15k
  op->mem.base = reg;
53
5.15k
  op->mem.disp = val;
54
5.15k
  op->is_signed = is_signed;
55
5.15k
  op->is_pkt = is_pkt;
56
5.15k
}
57
58
static void push_op_mmem(cs_bpf *bpf, uint32_t val)
59
306
{
60
306
  cs_bpf_op *op = expand_bpf_operands(bpf);
61
62
306
  op->type = BPF_OP_MMEM;
63
306
  op->mmem = val;
64
306
}
65
66
static void push_op_msh(cs_bpf *bpf, uint32_t val)
67
183
{
68
183
  cs_bpf_op *op = expand_bpf_operands(bpf);
69
70
183
  op->type = BPF_OP_MSH;
71
183
  op->msh = val;
72
183
}
73
74
static void push_op_ext(cs_bpf *bpf, bpf_ext_type val)
75
171
{
76
171
  cs_bpf_op *op = expand_bpf_operands(bpf);
77
78
171
  op->type = BPF_OP_EXT;
79
171
  op->ext = val;
80
171
}
81
82
static void convert_operands(MCInst *MI, cs_bpf *bpf)
83
15.4k
{
84
15.4k
  unsigned opcode = MCInst_getOpcode(MI);
85
15.4k
  unsigned mc_op_count = MCInst_getNumOperands(MI);
86
15.4k
  MCOperand *op;
87
15.4k
  MCOperand *op2;
88
89
15.4k
  bpf->op_count = 0;
90
15.4k
  if (BPF_CLASS(opcode) == BPF_CLASS_LD ||
91
15.4k
      BPF_CLASS(opcode) == BPF_CLASS_LDX) {
92
5.17k
    switch (BPF_MODE(opcode)) {
93
565
    case BPF_MODE_IMM:
94
565
      if (EBPF_MODE(MI->csh->mode)) {
95
265
        push_op_reg(bpf,
96
265
              MCOperand_getReg(
97
265
                MCInst_getOperand(MI, 0)),
98
265
              CS_AC_WRITE);
99
265
        push_op_imm(bpf,
100
265
              MCOperand_getImm(
101
265
                MCInst_getOperand(MI, 1)),
102
265
              false);
103
300
      } else {
104
300
        push_op_imm(bpf,
105
300
              MCOperand_getImm(
106
300
                MCInst_getOperand(MI, 0)),
107
300
              false);
108
300
      }
109
565
      break;
110
1.68k
    case BPF_MODE_ABS:
111
1.68k
      op = MCInst_getOperand(MI, 0);
112
1.68k
      push_op_mem(bpf, BPF_REG_INVALID,
113
1.68k
            (uint32_t)MCOperand_getImm(op), EBPF_MODE(MI->csh->mode), true);
114
1.68k
      break;
115
889
    case BPF_MODE_IND:
116
889
      op = MCInst_getOperand(MI, 0);
117
889
      if (EBPF_MODE(MI->csh->mode))
118
593
        push_op_mem(bpf, MCOperand_getReg(op), 0x0,
119
593
              true, true);
120
296
      else {
121
296
        op2 = MCInst_getOperand(MI, 1);
122
296
        push_op_mem(bpf, MCOperand_getReg(op),
123
296
              (uint32_t)MCOperand_getImm(op2),
124
296
              false, true);
125
296
      }
126
889
      break;
127
1.67k
    case BPF_MODE_MEM:
128
1.67k
      if (EBPF_MODE(MI->csh->mode)) {
129
        /* ldx{w,h,b,dw} dst, [src+off] */
130
1.39k
        push_op_reg(bpf,
131
1.39k
              MCOperand_getReg(
132
1.39k
                MCInst_getOperand(MI, 0)),
133
1.39k
              CS_AC_WRITE);
134
1.39k
        op = MCInst_getOperand(MI, 1);
135
1.39k
        op2 = MCInst_getOperand(MI, 2);
136
1.39k
        push_op_mem(bpf, MCOperand_getReg(op),
137
1.39k
              (uint32_t)MCOperand_getImm(op2),
138
1.39k
              true, false);
139
1.39k
      } else {
140
283
        push_op_mmem(bpf,
141
283
               (uint32_t)MCOperand_getImm(
142
283
                 MCInst_getOperand(MI, 0)));
143
283
      }
144
1.67k
      break;
145
171
    case BPF_MODE_LEN:
146
171
      push_op_ext(bpf, BPF_EXT_LEN);
147
171
      break;
148
183
    case BPF_MODE_MSH:
149
183
      op = MCInst_getOperand(MI, 0);
150
183
      push_op_msh(bpf, (uint32_t)MCOperand_getImm(op));
151
183
      break;
152
      /* case BPF_MODE_XADD: // not exists */
153
5.17k
    }
154
5.17k
    return;
155
5.17k
  }
156
10.3k
  if (BPF_CLASS(opcode) == BPF_CLASS_ST ||
157
10.3k
      BPF_CLASS(opcode) == BPF_CLASS_STX) {
158
1.21k
    if (!EBPF_MODE(MI->csh->mode)) {
159
      // cBPF has only one case - st* M[k]
160
23
      push_op_mmem(bpf, (uint32_t)MCOperand_getImm(
161
23
              MCInst_getOperand(MI, 0)));
162
23
      return;
163
23
    }
164
    /* eBPF has two cases:
165
     * - st [dst + off], src
166
     * - xadd [dst + off], src
167
     * they have same form of operands.
168
     */
169
1.18k
    op = MCInst_getOperand(MI, 0);
170
1.18k
    op2 = MCInst_getOperand(MI, 1);
171
1.18k
    push_op_mem(bpf, MCOperand_getReg(op),
172
1.18k
          (uint32_t)MCOperand_getImm(op2), true, false);
173
174
1.18k
    op = MCInst_getOperand(MI, 2);
175
1.18k
    if (MCOperand_isImm(op))
176
597
      push_op_imm(bpf, MCOperand_getImm(op), false);
177
591
    else if (MCOperand_isReg(op))
178
591
      push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ);
179
1.18k
    return;
180
1.21k
  }
181
182
9.10k
  {
183
9.10k
    const bool is_jmp32 = EBPF_MODE(MI->csh->mode) &&
184
9.10k
              (BPF_CLASS(opcode) == BPF_CLASS_JMP32);
185
9.10k
    if (BPF_CLASS(opcode) == BPF_CLASS_JMP || is_jmp32) {
186
16.4k
      for (size_t i = 0; i < mc_op_count; i++) {
187
12.1k
        op = MCInst_getOperand(MI, i);
188
12.1k
        if (MCOperand_isImm(op)) {
189
          /* Decide if we're using IMM or OFF here (and if OFF, then signed or unsigned):
190
           *
191
           * 1. any jump/jump32 + signed off (not including exit/call and ja on jump32) // eBPF 
192
           * 2. exit/call/ja + k // eBPF
193
           * 3. ja + unsigned off // cBPF (cBPF programs can only jump forwards) 
194
           * 4. any jump {x,k}, +jt, +jf // cBPF 
195
           * */
196
197
8.47k
          if ((BPF_OP(opcode) == BPF_JUMP_JA &&
198
8.47k
               !is_jmp32) ||
199
8.47k
              (!EBPF_MODE(MI->csh->mode) &&
200
8.35k
               i >= 1) ||
201
8.47k
              (EBPF_MODE(MI->csh->mode) &&
202
7.06k
               i == 2))
203
4.73k
            push_op_off(
204
4.73k
              bpf,
205
4.73k
              MCOperand_getImm(op),
206
4.73k
              EBPF_MODE(
207
4.73k
                MI->csh->mode));
208
3.74k
          else
209
3.74k
            push_op_imm(
210
3.74k
              bpf,
211
3.74k
              MCOperand_getImm(op),
212
3.74k
              true);
213
8.47k
        } else if (MCOperand_isReg(op)) {
214
3.71k
          push_op_reg(bpf, MCOperand_getReg(op),
215
3.71k
                CS_AC_READ);
216
3.71k
        }
217
12.1k
      }
218
4.30k
      return;
219
4.30k
    }
220
9.10k
  }
221
222
4.80k
  if (!EBPF_MODE(MI->csh->mode)) {
223
    /* In cBPF mode, all registers in operands are accessed as read */
224
3.98k
    for (size_t i = 0; i < mc_op_count; i++) {
225
1.93k
      op = MCInst_getOperand(MI, i);
226
1.93k
      if (MCOperand_isImm(op))
227
656
        push_op_imm(bpf, MCOperand_getImm(op), false);
228
1.27k
      else if (MCOperand_isReg(op))
229
1.27k
        push_op_reg(bpf, MCOperand_getReg(op),
230
1.27k
              CS_AC_READ);
231
1.93k
    }
232
2.05k
    return;
233
2.05k
  }
234
235
  /* remain cases are: eBPF mode && ALU */
236
  /* if (BPF_CLASS(opcode) == BPF_CLASS_ALU || BPF_CLASS(opcode) == BPF_CLASS_ALU64) */
237
238
  /* We have three types:
239
   * 1. {l,b}e dst               // dst = byteswap(dst)
240
   * 2. neg dst                  // dst = -dst
241
   * 3. <op> dst, {src_reg, imm} // dst = dst <op> src
242
   * so we can simply check the number of operands,
243
   * exactly one operand means we are in case 1. and 2.,
244
   * otherwise in case 3.
245
   */
246
2.75k
  if (mc_op_count == 1) {
247
632
    op = MCInst_getOperand(MI, 0);
248
632
    push_op_reg(bpf, MCOperand_getReg(op),
249
632
          CS_AC_READ | CS_AC_WRITE);
250
2.11k
  } else { // if (mc_op_count == 2)
251
2.11k
    op = MCInst_getOperand(MI, 0);
252
2.11k
    push_op_reg(bpf, MCOperand_getReg(op),
253
2.11k
          CS_AC_READ | CS_AC_WRITE);
254
255
2.11k
    op = MCInst_getOperand(MI, 1);
256
2.11k
    if (MCOperand_isImm(op))
257
1.91k
      push_op_imm(bpf, MCOperand_getImm(op), false);
258
207
    else if (MCOperand_isReg(op))
259
207
      push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ);
260
2.11k
  }
261
2.75k
}
262
263
static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op)
264
28.2k
{
265
28.2k
  switch (op->type) {
266
0
  case BPF_OP_INVALID:
267
0
    SStream_concat(O, "invalid");
268
0
    break;
269
10.2k
  case BPF_OP_REG:
270
10.2k
    SStream_concat(O, BPF_reg_name((csh)MI->csh, op->reg));
271
10.2k
    break;
272
7.46k
  case BPF_OP_IMM:
273
7.46k
    if (op->is_signed)
274
3.74k
      printInt32Hex(O, op->imm);
275
3.72k
    else
276
3.72k
      SStream_concat(O, "0x%" PRIx64, op->imm);
277
7.46k
    break;
278
4.73k
  case BPF_OP_OFF:
279
4.73k
    if (op->is_signed)
280
3.36k
      printInt16HexOffset(O, op->off);
281
1.36k
    else
282
1.36k
      SStream_concat(O, "+0x%" PRIx32, op->off);
283
4.73k
    break;
284
5.15k
  case BPF_OP_MEM:
285
5.15k
    SStream_concat(O, "[");
286
287
5.15k
    if (op->is_pkt && EBPF_MODE(MI->csh->mode)) {
288
1.87k
      SStream_concat(O, "skb");
289
290
1.87k
      if (op->mem.base != BPF_REG_INVALID)
291
593
        SStream_concat(O, "+%s",
292
593
                 BPF_reg_name((csh)MI->csh,
293
593
                  op->mem.base));
294
1.28k
      else {
295
1.28k
        if (op->is_signed)
296
1.28k
          printInt32HexOffset(O, op->mem.disp);
297
0
        else
298
0
          SStream_concat(O, "+0x%" PRIx32,
299
0
                   op->mem.disp);
300
1.28k
      }
301
3.27k
    } else {
302
3.27k
      if (op->mem.base != BPF_REG_INVALID)
303
2.87k
        SStream_concat(O, BPF_reg_name((csh)MI->csh,
304
2.87k
                     op->mem.base));
305
3.27k
      if (op->mem.disp != 0) {
306
3.21k
        if (op->mem.base != BPF_REG_INVALID) {
307
          // if operation is signed, then it always uses off, not k
308
2.83k
          if (op->is_signed)
309
2.53k
            printInt16HexOffset(
310
2.53k
              O, op->mem.disp);
311
294
          else if (op->is_pkt)
312
294
            SStream_concat(O, "+0x%" PRIx32,
313
294
                     op->mem.disp);
314
0
          else
315
0
            SStream_concat(O, "+0x%" PRIx16,
316
0
                     op->mem.disp);
317
2.83k
        } else
318
381
          SStream_concat(O, "0x%" PRIx32,
319
381
                   op->mem.disp);
320
3.21k
      }
321
322
3.27k
      if (op->mem.base == BPF_REG_INVALID &&
323
3.27k
          op->mem.disp == 0)
324
19
        SStream_concat(O, "0x0");
325
3.27k
    }
326
327
5.15k
    SStream_concat(O, "]");
328
5.15k
    break;
329
306
  case BPF_OP_MMEM:
330
306
    SStream_concat(O, "m[0x%x]", op->mmem);
331
306
    break;
332
183
  case BPF_OP_MSH:
333
183
    SStream_concat(O, "4*([0x%x]&0xf)", op->msh);
334
183
    break;
335
171
  case BPF_OP_EXT:
336
171
    switch (op->ext) {
337
171
    case BPF_EXT_LEN:
338
171
      SStream_concat(O, "#len");
339
171
      break;
340
171
    }
341
171
    break;
342
28.2k
  }
343
28.2k
}
344
345
/*
346
 * 1. human readable mnemonic
347
 * 2. set pubOpcode (BPF_INSN_*)
348
 * 3. set detail->bpf.operands
349
 * */
350
void BPF_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo)
351
15.4k
{
352
15.4k
  cs_bpf bpf = { 0 };
353
354
  /* set pubOpcode as instruction id */
355
15.4k
  SStream_concat(O, BPF_insn_name((csh)MI->csh, MCInst_getOpcodePub(MI)));
356
15.4k
  convert_operands(MI, &bpf);
357
43.7k
  for (size_t i = 0; i < bpf.op_count; i++) {
358
28.2k
    if (i == 0)
359
15.3k
      SStream_concat(O, "\t");
360
12.8k
    else
361
12.8k
      SStream_concat(O, ", ");
362
28.2k
    print_operand(MI, O, &bpf.operands[i]);
363
28.2k
  }
364
365
15.4k
#ifndef CAPSTONE_DIET
366
15.4k
  if (detail_is_set(MI)) {
367
15.4k
    MI->flat_insn->detail->bpf = bpf;
368
15.4k
  }
369
15.4k
#endif
370
15.4k
}