Coverage Report

Created: 2026-07-16 06:55

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