Coverage Report

Created: 2023-12-08 06:05

/src/capstonenext/arch/BPF/BPFMapping.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 <string.h>
5
6
#include "BPFConstants.h"
7
#include "BPFMapping.h"
8
#include "../../Mapping.h"
9
#include "../../utils.h"
10
11
#ifndef CAPSTONE_DIET
12
static const name_map group_name_maps[] = {
13
  { BPF_GRP_INVALID, NULL },
14
15
  { BPF_GRP_LOAD, "load" },
16
  { BPF_GRP_STORE, "store" },
17
  { BPF_GRP_ALU, "alu" },
18
  { BPF_GRP_JUMP, "jump" },
19
  { BPF_GRP_CALL, "call" },
20
  { BPF_GRP_RETURN, "return" },
21
  { BPF_GRP_MISC, "misc" },
22
};
23
#endif
24
25
const char *BPF_group_name(csh handle, unsigned int id)
26
27.8k
{
27
27.8k
#ifndef CAPSTONE_DIET
28
27.8k
  return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
29
#else
30
  return NULL;
31
#endif
32
27.8k
}
33
34
#ifndef CAPSTONE_DIET
35
static const name_map insn_name_maps[BPF_INS_ENDING] = {
36
  { BPF_INS_INVALID, NULL },
37
38
  { BPF_INS_ADD, "add" },
39
  { BPF_INS_SUB, "sub" },
40
  { BPF_INS_MUL, "mul" },
41
  { BPF_INS_DIV, "div" },
42
  { BPF_INS_OR, "or" },
43
  { BPF_INS_AND, "and" },
44
  { BPF_INS_LSH, "lsh" },
45
  { BPF_INS_RSH, "rsh" },
46
  { BPF_INS_NEG, "neg" },
47
  { BPF_INS_MOD, "mod" },
48
  { BPF_INS_XOR, "xor" },
49
  { BPF_INS_MOV, "mov" },
50
  { BPF_INS_ARSH, "arsh" },
51
52
  { BPF_INS_ADD64, "add64" },
53
  { BPF_INS_SUB64, "sub64" },
54
  { BPF_INS_MUL64, "mul64" },
55
  { BPF_INS_DIV64, "div64" },
56
  { BPF_INS_OR64, "or64" },
57
  { BPF_INS_AND64, "and64" },
58
  { BPF_INS_LSH64, "lsh64" },
59
  { BPF_INS_RSH64, "rsh64" },
60
  { BPF_INS_NEG64, "neg64" },
61
  { BPF_INS_MOD64, "mod64" },
62
  { BPF_INS_XOR64, "xor64" },
63
  { BPF_INS_MOV64, "mov64" },
64
  { BPF_INS_ARSH64, "arsh64" },
65
66
  { BPF_INS_LE16, "le16" },
67
  { BPF_INS_LE32, "le32" },
68
  { BPF_INS_LE64, "le64" },
69
  { BPF_INS_BE16, "be16" },
70
  { BPF_INS_BE32, "be32" },
71
  { BPF_INS_BE64, "be64" },
72
73
  { BPF_INS_LDW, "ldw" },
74
  { BPF_INS_LDH, "ldh" },
75
  { BPF_INS_LDB, "ldb" },
76
  { BPF_INS_LDDW, "lddw" },
77
  { BPF_INS_LDXW, "ldxw" },
78
  { BPF_INS_LDXH, "ldxh" },
79
  { BPF_INS_LDXB, "ldxb" },
80
  { BPF_INS_LDXDW, "ldxdw" },
81
82
  { BPF_INS_STW, "stw" },
83
  { BPF_INS_STH, "sth" },
84
  { BPF_INS_STB, "stb" },
85
  { BPF_INS_STDW, "stdw" },
86
  { BPF_INS_STXW, "stxw" },
87
  { BPF_INS_STXH, "stxh" },
88
  { BPF_INS_STXB, "stxb" },
89
  { BPF_INS_STXDW, "stxdw" },
90
  { BPF_INS_XADDW, "xaddw" },
91
  { BPF_INS_XADDDW, "xadddw" },
92
93
  { BPF_INS_JMP, "jmp" },
94
  { BPF_INS_JEQ, "jeq" },
95
  { BPF_INS_JGT, "jgt" },
96
  { BPF_INS_JGE, "jge" },
97
  { BPF_INS_JSET, "jset" },
98
  { BPF_INS_JNE, "jne" },
99
  { BPF_INS_JSGT, "jsgt" },
100
  { BPF_INS_JSGE, "jsge" },
101
  { BPF_INS_CALL, "call" },
102
  { BPF_INS_CALLX, "callx" },
103
  { BPF_INS_EXIT, "exit" },
104
  { BPF_INS_JLT, "jlt" },
105
  { BPF_INS_JLE, "jle" },
106
  { BPF_INS_JSLT, "jslt" },
107
  { BPF_INS_JSLE, "jsle" },
108
109
  { BPF_INS_RET, "ret" },
110
111
  { BPF_INS_TAX, "tax" },
112
  { BPF_INS_TXA, "txa" },
113
};
114
#endif
115
116
const char *BPF_insn_name(csh handle, unsigned int id)
117
55.7k
{
118
55.7k
#ifndef CAPSTONE_DIET
119
  /* We have some special cases because 'ld' in cBPF is equivalent to 'ldw'
120
   * in eBPF, and we don't want to see 'ldw' appears in cBPF mode.
121
   */
122
55.7k
  if (!EBPF_MODE(handle)) {
123
23.6k
    switch (id) {
124
4.30k
    case BPF_INS_LD: return "ld";
125
2.72k
    case BPF_INS_LDX: return "ldx";
126
174
    case BPF_INS_ST: return "st";
127
64
    case BPF_INS_STX: return "stx";
128
23.6k
    }
129
23.6k
  }
130
48.4k
  return id2name(insn_name_maps, ARR_SIZE(insn_name_maps), id);
131
#else
132
  return NULL;
133
#endif
134
55.7k
}
135
136
const char *BPF_reg_name(csh handle, unsigned int reg)
137
36.6k
{
138
36.6k
#ifndef CAPSTONE_DIET
139
36.6k
  if (EBPF_MODE(handle)) {
140
19.7k
    if (reg < BPF_REG_R0 || reg > BPF_REG_R10)
141
0
      return NULL;
142
19.7k
    static const char reg_names[11][4] = {
143
19.7k
      "r0", "r1", "r2", "r3", "r4",
144
19.7k
      "r5", "r6", "r7", "r8", "r9",
145
19.7k
      "r10"
146
19.7k
    };
147
19.7k
    return reg_names[reg - BPF_REG_R0];
148
19.7k
  }
149
150
  /* cBPF mode */
151
16.8k
  if (reg == BPF_REG_A)
152
11.6k
    return "a";
153
5.24k
  else if (reg == BPF_REG_X)
154
5.24k
    return "x";
155
0
  else
156
0
    return NULL;
157
#else
158
  return NULL;
159
#endif
160
16.8k
}
161
162
static bpf_insn op2insn_ld(unsigned opcode)
163
17.3k
{
164
17.3k
#define CASE(c) case BPF_SIZE_##c: \
165
17.3k
    if (BPF_CLASS(opcode) == BPF_CLASS_LD) \
166
17.3k
      return BPF_INS_LD##c; \
167
17.3k
    else \
168
17.3k
      return BPF_INS_LDX##c;
169
170
17.3k
  switch (BPF_SIZE(opcode)) {
171
8.91k
  CASE(W);
172
2.72k
  CASE(H);
173
2.90k
  CASE(B);
174
2.80k
  CASE(DW);
175
17.3k
  }
176
0
#undef CASE
177
178
0
  return BPF_INS_INVALID;
179
17.3k
}
180
181
static bpf_insn op2insn_st(unsigned opcode)
182
5.78k
{
183
  /*
184
   * - BPF_STX | BPF_XADD | BPF_{W,DW}
185
   * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW}
186
   */
187
188
5.78k
  if (opcode == (BPF_CLASS_STX | BPF_MODE_XADD | BPF_SIZE_W))
189
172
    return BPF_INS_XADDW;
190
5.61k
  if (opcode == (BPF_CLASS_STX | BPF_MODE_XADD | BPF_SIZE_DW))
191
94
    return BPF_INS_XADDDW;
192
193
  /* should be BPF_MEM */
194
5.52k
#define CASE(c) case BPF_SIZE_##c: \
195
5.52k
    if (BPF_CLASS(opcode) == BPF_CLASS_ST) \
196
5.52k
      return BPF_INS_ST##c; \
197
5.52k
    else \
198
5.52k
      return BPF_INS_STX##c;
199
5.52k
  switch (BPF_SIZE(opcode)) {
200
2.00k
  CASE(W);
201
790
  CASE(H);
202
1.70k
  CASE(B);
203
1.02k
  CASE(DW);
204
5.52k
  }
205
0
#undef CASE
206
207
0
  return BPF_INS_INVALID;
208
5.52k
}
209
210
static bpf_insn op2insn_alu(unsigned opcode)
211
17.1k
{
212
  /* Endian is a special case */
213
17.1k
  if (BPF_OP(opcode) == BPF_ALU_END) {
214
608
    switch (opcode ^ BPF_CLASS_ALU ^ BPF_ALU_END) {
215
52
    case BPF_SRC_LITTLE | (16 << 4):
216
52
      return BPF_INS_LE16;
217
114
    case BPF_SRC_LITTLE | (32 << 4):
218
114
      return BPF_INS_LE32;
219
24
    case BPF_SRC_LITTLE | (64 << 4):
220
24
      return BPF_INS_LE64;
221
26
    case BPF_SRC_BIG | (16 << 4):
222
26
      return BPF_INS_BE16;
223
310
    case BPF_SRC_BIG | (32 << 4):
224
310
      return BPF_INS_BE32;
225
82
    case BPF_SRC_BIG | (64 << 4):
226
82
      return BPF_INS_BE64;
227
608
    }
228
0
    return BPF_INS_INVALID;
229
608
  }
230
231
16.5k
#define CASE(c) case BPF_ALU_##c: \
232
16.5k
    if (BPF_CLASS(opcode) == BPF_CLASS_ALU) \
233
16.5k
      return BPF_INS_##c; \
234
16.5k
    else \
235
16.5k
      return BPF_INS_##c##64;
236
237
16.5k
  switch (BPF_OP(opcode)) {
238
1.62k
  CASE(ADD);
239
922
  CASE(SUB);
240
1.16k
  CASE(MUL);
241
694
  CASE(DIV);
242
838
  CASE(OR);
243
2.23k
  CASE(AND);
244
1.01k
  CASE(LSH);
245
852
  CASE(RSH);
246
2.29k
  CASE(NEG);
247
1.79k
  CASE(MOD);
248
1.09k
  CASE(XOR);
249
1.06k
  CASE(MOV);
250
948
  CASE(ARSH);
251
16.5k
  }
252
0
#undef CASE
253
254
0
  return BPF_INS_INVALID;
255
16.5k
}
256
257
static bpf_insn op2insn_jmp(unsigned opcode)
258
11.5k
{
259
11.5k
  if (opcode == (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) {
260
42
    return BPF_INS_CALLX;
261
42
  }
262
263
11.4k
#define CASE(c) case BPF_JUMP_##c: return BPF_INS_##c
264
11.4k
  switch (BPF_OP(opcode)) {
265
1.96k
  case BPF_JUMP_JA:
266
1.96k
    return BPF_INS_JMP;
267
1.44k
  CASE(JEQ);
268
1.01k
  CASE(JGT);
269
942
  CASE(JGE);
270
294
  CASE(JSET);
271
696
  CASE(JNE);
272
174
  CASE(JSGT);
273
1.32k
  CASE(JSGE);
274
1.22k
  CASE(CALL);
275
1.10k
  CASE(EXIT);
276
392
  CASE(JLT);
277
336
  CASE(JLE);
278
282
  CASE(JSLT);
279
11.4k
  CASE(JSLE);
280
11.4k
  }
281
0
#undef CASE
282
283
0
  return BPF_INS_INVALID;
284
11.4k
}
285
286
static void update_regs_access(cs_struct *ud, cs_detail *detail,
287
    bpf_insn insn_id, unsigned int opcode)
288
27.8k
{
289
27.8k
  if (insn_id == BPF_INS_INVALID)
290
0
    return;
291
27.8k
#define PUSH_READ(r) do { \
292
5.30k
    detail->regs_read[detail->regs_read_count] = r; \
293
5.30k
    detail->regs_read_count++; \
294
5.30k
  } while (0)
295
27.8k
#define PUSH_WRITE(r) do { \
296
10.0k
    detail->regs_write[detail->regs_write_count] = r; \
297
10.0k
    detail->regs_write_count++; \
298
10.0k
  } while (0)
299
  /*
300
   * In eBPF mode, only these instructions have implicit registers access:
301
   * - legacy ld{w,h,b,dw} * // w: r0
302
   * - exit // r: r0
303
   */
304
27.8k
  if (EBPF_MODE(ud)) {
305
16.0k
    switch (insn_id) {
306
12.4k
    default:
307
12.4k
      break;
308
12.4k
    case BPF_INS_LDW:
309
1.39k
    case BPF_INS_LDH:
310
1.90k
    case BPF_INS_LDB:
311
3.08k
    case BPF_INS_LDDW:
312
3.08k
      if (BPF_MODE(opcode) == BPF_MODE_ABS || BPF_MODE(opcode) == BPF_MODE_IND) {
313
2.11k
        PUSH_WRITE(BPF_REG_R0);
314
2.11k
      }
315
3.08k
      break;
316
551
    case BPF_INS_EXIT:
317
551
      PUSH_READ(BPF_REG_R0);
318
551
      break;
319
16.0k
    }
320
16.0k
    return;
321
16.0k
  }
322
323
  /* cBPF mode */
324
11.8k
  switch (BPF_CLASS(opcode)) {
325
1.78k
  default:
326
1.78k
    break;
327
3.23k
  case BPF_CLASS_LD:
328
3.23k
    PUSH_WRITE(BPF_REG_A);
329
3.23k
    break;
330
1.36k
  case BPF_CLASS_LDX:
331
1.36k
    PUSH_WRITE(BPF_REG_X);
332
1.36k
    break;
333
87
  case BPF_CLASS_ST:
334
87
    PUSH_READ(BPF_REG_A);
335
87
    break;
336
32
  case BPF_CLASS_STX:
337
32
    PUSH_READ(BPF_REG_X);
338
32
    break;
339
3.18k
  case BPF_CLASS_ALU:
340
3.18k
    PUSH_READ(BPF_REG_A);
341
3.18k
    PUSH_WRITE(BPF_REG_A);
342
3.18k
    break;
343
1.93k
  case BPF_CLASS_JMP:
344
1.93k
    if (insn_id != BPF_INS_JMP) // except the unconditional jump
345
1.25k
      PUSH_READ(BPF_REG_A);
346
1.93k
    break;
347
  /* case BPF_CLASS_RET: */
348
195
  case BPF_CLASS_MISC:
349
195
    if (insn_id == BPF_INS_TAX) {
350
38
      PUSH_READ(BPF_REG_A);
351
38
      PUSH_WRITE(BPF_REG_X);
352
38
    }
353
157
    else {
354
157
      PUSH_READ(BPF_REG_X);
355
157
      PUSH_WRITE(BPF_REG_A);
356
157
    }
357
195
    break;
358
11.8k
  }
359
11.8k
}
360
361
/*
362
 * 1. Convert opcode(id) to BPF_INS_*
363
 * 2. Set regs_read/regs_write/groups
364
 */
365
void BPF_get_insn_id(cs_struct *ud, cs_insn *insn, unsigned int opcode)
366
55.7k
{
367
  // No need to care the mode (cBPF or eBPF) since all checks has be done in
368
  // BPF_getInstruction, we can simply map opcode to BPF_INS_*.
369
55.7k
  cs_detail *detail;
370
55.7k
  bpf_insn id = BPF_INS_INVALID;
371
55.7k
  bpf_insn_group grp;
372
373
55.7k
  detail = insn->detail;
374
55.7k
#ifndef CAPSTONE_DIET
375
55.7k
 #define PUSH_GROUP(grp) do { \
376
55.7k
    if (detail) { \
377
27.8k
      detail->groups[detail->groups_count] = grp; \
378
27.8k
      detail->groups_count++; \
379
27.8k
    } \
380
55.7k
  } while(0)
381
#else
382
 #define PUSH_GROUP
383
#endif
384
385
55.7k
  switch (BPF_CLASS(opcode)) {
386
0
  default:  // will never happen
387
0
    break;
388
12.6k
  case BPF_CLASS_LD:
389
17.3k
  case BPF_CLASS_LDX:
390
17.3k
    id = op2insn_ld(opcode);
391
17.3k
    PUSH_GROUP(BPF_GRP_LOAD);
392
17.3k
    break;
393
2.29k
  case BPF_CLASS_ST:
394
5.78k
  case BPF_CLASS_STX:
395
5.78k
    id = op2insn_st(opcode);
396
5.78k
    PUSH_GROUP(BPF_GRP_STORE);
397
5.78k
    break;
398
9.93k
  case BPF_CLASS_ALU:
399
9.93k
    id = op2insn_alu(opcode);
400
9.93k
    PUSH_GROUP(BPF_GRP_ALU);
401
9.93k
    break;
402
11.5k
  case BPF_CLASS_JMP:
403
11.5k
    grp = BPF_GRP_JUMP;
404
11.5k
    id = op2insn_jmp(opcode);
405
11.5k
    if (id == BPF_INS_CALL || id == BPF_INS_CALLX)
406
1.26k
      grp = BPF_GRP_CALL;
407
10.2k
    else if (id == BPF_INS_EXIT)
408
1.10k
      grp = BPF_GRP_RETURN;
409
11.5k
    PUSH_GROUP(grp);
410
11.5k
    break;
411
3.56k
  case BPF_CLASS_RET:
412
3.56k
    id = BPF_INS_RET;
413
3.56k
    PUSH_GROUP(BPF_GRP_RETURN);
414
3.56k
    break;
415
  // BPF_CLASS_MISC and BPF_CLASS_ALU64 have exactly same value
416
7.60k
  case BPF_CLASS_MISC:
417
  /* case BPF_CLASS_ALU64: */
418
7.60k
    if (EBPF_MODE(ud)) {
419
      // ALU64 in eBPF
420
7.21k
      id = op2insn_alu(opcode);
421
7.21k
      PUSH_GROUP(BPF_GRP_ALU);
422
7.21k
    }
423
390
    else {
424
390
      if (BPF_MISCOP(opcode) == BPF_MISCOP_TXA)
425
314
        id = BPF_INS_TXA;
426
76
      else
427
76
        id = BPF_INS_TAX;
428
390
      PUSH_GROUP(BPF_GRP_MISC);
429
390
    }
430
7.60k
    break;
431
55.7k
  }
432
433
55.7k
  insn->id = id;
434
55.7k
#undef PUSH_GROUP
435
436
55.7k
#ifndef CAPSTONE_DIET
437
55.7k
  if (detail) {
438
27.8k
    update_regs_access(ud, detail, id, opcode);
439
27.8k
  }
440
55.7k
#endif
441
55.7k
}
442
443
static void sort_and_uniq(cs_regs arr, uint8_t n, uint8_t *new_n)
444
0
{
445
  /* arr is always a tiny (usually n < 3) array,
446
   * a simple O(n^2) sort is efficient enough. */
447
0
  int i;
448
0
  int j;
449
0
  int iMin;
450
0
  int tmp;
451
452
  /* a modified selection sort for sorting and making unique */
453
0
  for (j = 0; j < n; j++) {
454
    /* arr[iMin] will be min(arr[j .. n-1]) */
455
0
    iMin = j;
456
0
    for (i = j + 1; i < n; i++) {
457
0
      if (arr[i] < arr[iMin])
458
0
        iMin = i;
459
0
    }
460
0
    if (j != 0 && arr[iMin] == arr[j - 1]) { // duplicate ele found
461
0
      arr[iMin] = arr[n - 1];
462
0
      --n;
463
0
    }
464
0
    else {
465
0
      tmp = arr[iMin];
466
0
      arr[iMin] = arr[j];
467
0
      arr[j] = tmp;
468
0
    }
469
0
  }
470
471
0
  *new_n = n;
472
0
}
473
void BPF_reg_access(const cs_insn *insn,
474
    cs_regs regs_read, uint8_t *regs_read_count,
475
    cs_regs regs_write, uint8_t *regs_write_count)
476
0
{
477
0
  unsigned i;
478
0
  uint8_t read_count, write_count;
479
0
  const cs_bpf *bpf = &(insn->detail->bpf);
480
481
0
  read_count = insn->detail->regs_read_count;
482
0
  write_count = insn->detail->regs_write_count;
483
484
  // implicit registers
485
0
  memcpy(regs_read, insn->detail->regs_read, read_count * sizeof(insn->detail->regs_read[0]));
486
0
  memcpy(regs_write, insn->detail->regs_write, write_count * sizeof(insn->detail->regs_write[0]));
487
488
0
  for (i = 0; i < bpf->op_count; i++) {
489
0
    const cs_bpf_op *op = &(bpf->operands[i]);
490
0
    switch (op->type) {
491
0
    default:
492
0
      break;
493
0
    case BPF_OP_REG:
494
0
      if (op->access & CS_AC_READ) {
495
0
        regs_read[read_count] = op->reg;
496
0
        read_count++;
497
0
      }
498
0
      if (op->access & CS_AC_WRITE) {
499
0
        regs_write[write_count] = op->reg;
500
0
        write_count++;
501
0
      }
502
0
      break;
503
0
    case BPF_OP_MEM:
504
0
      if (op->mem.base != BPF_REG_INVALID) {
505
0
        regs_read[read_count] = op->mem.base;
506
0
        read_count++;
507
0
      }
508
0
      break;
509
0
    }
510
0
  }
511
512
0
  sort_and_uniq(regs_read, read_count, regs_read_count);
513
0
  sort_and_uniq(regs_write, write_count, regs_write_count);
514
0
}