Coverage Report

Created: 2024-09-08 06:22

/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
31.4k
{
27
31.4k
#ifndef CAPSTONE_DIET
28
31.4k
  return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
29
#else
30
  return NULL;
31
#endif
32
31.4k
}
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
  { BPF_INS_BSWAP16, "bswap16" },
73
  { BPF_INS_BSWAP32, "bswap32" },
74
  { BPF_INS_BSWAP64, "bswap64" },
75
76
  { BPF_INS_LDW, "ldw" },
77
  { BPF_INS_LDH, "ldh" },
78
  { BPF_INS_LDB, "ldb" },
79
  { BPF_INS_LDDW, "lddw" },
80
  { BPF_INS_LDXW, "ldxw" },
81
  { BPF_INS_LDXH, "ldxh" },
82
  { BPF_INS_LDXB, "ldxb" },
83
  { BPF_INS_LDXDW, "ldxdw" },
84
85
  { BPF_INS_STW, "stw" },
86
  { BPF_INS_STH, "sth" },
87
  { BPF_INS_STB, "stb" },
88
  { BPF_INS_STDW, "stdw" },
89
  { BPF_INS_STXW, "stxw" },
90
  { BPF_INS_STXH, "stxh" },
91
  { BPF_INS_STXB, "stxb" },
92
  { BPF_INS_STXDW, "stxdw" },
93
  { BPF_INS_XADDW, "xaddw" },
94
  { BPF_INS_XADDDW, "xadddw" },
95
96
  { BPF_INS_JMP, "jmp" },
97
  { BPF_INS_JEQ, "jeq" },
98
  { BPF_INS_JGT, "jgt" },
99
  { BPF_INS_JGE, "jge" },
100
  { BPF_INS_JSET, "jset" },
101
  { BPF_INS_JNE, "jne" },
102
  { BPF_INS_JSGT, "jsgt" },
103
  { BPF_INS_JSGE, "jsge" },
104
  { BPF_INS_CALL, "call" },
105
  { BPF_INS_CALLX, "callx" },
106
  { BPF_INS_EXIT, "exit" },
107
  { BPF_INS_JLT, "jlt" },
108
  { BPF_INS_JLE, "jle" },
109
  { BPF_INS_JSLT, "jslt" },
110
  { BPF_INS_JSLE, "jsle" },
111
112
  { BPF_INS_RET, "ret" },
113
114
  { BPF_INS_TAX, "tax" },
115
  { BPF_INS_TXA, "txa" },
116
};
117
#endif
118
119
const char *BPF_insn_name(csh handle, unsigned int id)
120
62.8k
{
121
62.8k
#ifndef CAPSTONE_DIET
122
  /* We have some special cases because 'ld' in cBPF is equivalent to 'ldw'
123
   * in eBPF, and we don't want to see 'ldw' appears in cBPF mode.
124
   */
125
62.8k
  if (!EBPF_MODE(handle)) {
126
26.3k
    switch (id) {
127
5.43k
    case BPF_INS_LD: return "ld";
128
1.90k
    case BPF_INS_LDX: return "ldx";
129
194
    case BPF_INS_ST: return "st";
130
66
    case BPF_INS_STX: return "stx";
131
26.3k
    }
132
26.3k
  }
133
55.2k
  return id2name(insn_name_maps, ARR_SIZE(insn_name_maps), id);
134
#else
135
  return NULL;
136
#endif
137
62.8k
}
138
139
const char *BPF_reg_name(csh handle, unsigned int reg)
140
40.6k
{
141
40.6k
#ifndef CAPSTONE_DIET
142
40.6k
  if (EBPF_MODE(handle)) {
143
21.7k
    if (reg < BPF_REG_R0 || reg > BPF_REG_R10)
144
0
      return NULL;
145
21.7k
    static const char reg_names[11][4] = {
146
21.7k
      "r0", "r1", "r2", "r3", "r4",
147
21.7k
      "r5", "r6", "r7", "r8", "r9",
148
21.7k
      "r10"
149
21.7k
    };
150
21.7k
    return reg_names[reg - BPF_REG_R0];
151
21.7k
  }
152
153
  /* cBPF mode */
154
18.9k
  if (reg == BPF_REG_A)
155
14.1k
    return "a";
156
4.79k
  else if (reg == BPF_REG_X)
157
4.79k
    return "x";
158
0
  else
159
0
    return NULL;
160
#else
161
  return NULL;
162
#endif
163
18.9k
}
164
165
static bpf_insn op2insn_ld(unsigned opcode)
166
20.1k
{
167
20.1k
#define CASE(c) case BPF_SIZE_##c: \
168
20.1k
    if (BPF_CLASS(opcode) == BPF_CLASS_LD) \
169
20.1k
      return BPF_INS_LD##c; \
170
20.1k
    else \
171
20.1k
      return BPF_INS_LDX##c;
172
173
20.1k
  switch (BPF_SIZE(opcode)) {
174
9.98k
  CASE(W);
175
3.58k
  CASE(H);
176
3.87k
  CASE(B);
177
2.66k
  CASE(DW);
178
20.1k
  }
179
0
#undef CASE
180
181
0
  return BPF_INS_INVALID;
182
20.1k
}
183
184
static bpf_insn op2insn_st(unsigned opcode)
185
6.46k
{
186
  /*
187
   * - BPF_STX | BPF_XADD | BPF_{W,DW}
188
   * - BPF_ST* | BPF_MEM | BPF_{W,H,B,DW}
189
   */
190
191
6.46k
  if (opcode == (BPF_CLASS_STX | BPF_MODE_XADD | BPF_SIZE_W))
192
138
    return BPF_INS_XADDW;
193
6.32k
  if (opcode == (BPF_CLASS_STX | BPF_MODE_XADD | BPF_SIZE_DW))
194
44
    return BPF_INS_XADDDW;
195
196
  /* should be BPF_MEM */
197
6.28k
#define CASE(c) case BPF_SIZE_##c: \
198
6.28k
    if (BPF_CLASS(opcode) == BPF_CLASS_ST) \
199
6.28k
      return BPF_INS_ST##c; \
200
6.28k
    else \
201
6.28k
      return BPF_INS_STX##c;
202
6.28k
  switch (BPF_SIZE(opcode)) {
203
2.71k
  CASE(W);
204
858
  CASE(H);
205
2.11k
  CASE(B);
206
600
  CASE(DW);
207
6.28k
  }
208
0
#undef CASE
209
210
0
  return BPF_INS_INVALID;
211
6.28k
}
212
213
static bpf_insn op2insn_alu(unsigned opcode)
214
8.87k
{
215
  /* Endian is a special case */
216
8.87k
  if (BPF_OP(opcode) == BPF_ALU_END) {
217
94
    if (BPF_CLASS(opcode) == BPF_CLASS_ALU64) {
218
24
      switch (opcode ^ BPF_CLASS_ALU64 ^ BPF_ALU_END ^ BPF_SRC_LITTLE) {
219
2
      case (16 << 4):
220
2
        return BPF_INS_BSWAP16;
221
2
      case (32 << 4):
222
2
        return BPF_INS_BSWAP32;
223
20
      case (64 << 4):
224
20
        return BPF_INS_BSWAP64;
225
0
      default:
226
0
        return BPF_INS_INVALID;
227
24
      }
228
24
    }
229
230
70
    switch (opcode ^ BPF_CLASS_ALU ^ BPF_ALU_END) {
231
2
    case BPF_SRC_LITTLE | (16 << 4):
232
2
      return BPF_INS_LE16;
233
32
    case BPF_SRC_LITTLE | (32 << 4):
234
32
      return BPF_INS_LE32;
235
2
    case BPF_SRC_LITTLE | (64 << 4):
236
2
      return BPF_INS_LE64;
237
26
    case BPF_SRC_BIG | (16 << 4):
238
26
      return BPF_INS_BE16;
239
6
    case BPF_SRC_BIG | (32 << 4):
240
6
      return BPF_INS_BE32;
241
2
    case BPF_SRC_BIG | (64 << 4):
242
2
      return BPF_INS_BE64;
243
70
    }
244
0
    return BPF_INS_INVALID;
245
70
  }
246
247
8.77k
#define CASE(c) case BPF_ALU_##c: \
248
8.77k
    if (BPF_CLASS(opcode) == BPF_CLASS_ALU) \
249
8.77k
      return BPF_INS_##c; \
250
8.77k
    else \
251
8.77k
      return BPF_INS_##c##64;
252
253
8.77k
  switch (BPF_OP(opcode)) {
254
696
  CASE(ADD);
255
976
  CASE(SUB);
256
534
  CASE(MUL);
257
432
  CASE(DIV);
258
456
  CASE(OR);
259
1.16k
  CASE(AND);
260
760
  CASE(LSH);
261
410
  CASE(RSH);
262
1.13k
  CASE(NEG);
263
928
  CASE(MOD);
264
664
  CASE(XOR);
265
422
  CASE(MOV);
266
198
  CASE(ARSH);
267
8.77k
  }
268
0
#undef CASE
269
270
0
  return BPF_INS_INVALID;
271
8.77k
}
272
273
static bpf_insn op2insn_jmp(unsigned opcode)
274
14.1k
{
275
14.1k
  if (opcode == (BPF_CLASS_JMP | BPF_JUMP_CALL | BPF_SRC_X)) {
276
42
    return BPF_INS_CALLX;
277
42
  }
278
279
14.1k
#define CASE(c) case BPF_JUMP_##c: return BPF_INS_##c
280
14.1k
  switch (BPF_OP(opcode)) {
281
2.48k
  case BPF_JUMP_JA:
282
2.48k
    return BPF_INS_JMP;
283
994
  CASE(JEQ);
284
1.47k
  CASE(JGT);
285
610
  CASE(JGE);
286
1.33k
  CASE(JSET);
287
808
  CASE(JNE);
288
824
  CASE(JSGT);
289
678
  CASE(JSGE);
290
2.05k
  CASE(CALL);
291
1.18k
  CASE(EXIT);
292
520
  CASE(JLT);
293
524
  CASE(JLE);
294
360
  CASE(JSLT);
295
14.1k
  CASE(JSLE);
296
14.1k
  }
297
0
#undef CASE
298
299
0
  return BPF_INS_INVALID;
300
14.1k
}
301
302
#ifndef CAPSTONE_DIET
303
static void update_regs_access(cs_struct *ud, cs_detail *detail,
304
    bpf_insn insn_id, unsigned int opcode)
305
31.4k
{
306
31.4k
  if (insn_id == BPF_INS_INVALID)
307
0
    return;
308
31.4k
#define PUSH_READ(r) do { \
309
5.78k
    detail->regs_read[detail->regs_read_count] = r; \
310
5.78k
    detail->regs_read_count++; \
311
5.78k
  } while (0)
312
31.4k
#define PUSH_WRITE(r) do { \
313
11.8k
    detail->regs_write[detail->regs_write_count] = r; \
314
11.8k
    detail->regs_write_count++; \
315
11.8k
  } while (0)
316
  /*
317
   * In eBPF mode, only these instructions have implicit registers access:
318
   * - legacy ld{w,h,b,dw} * // w: r0
319
   * - exit // r: r0
320
   */
321
31.4k
  if (EBPF_MODE(ud)) {
322
18.2k
    switch (insn_id) {
323
14.3k
    default:
324
14.3k
      break;
325
14.3k
    case BPF_INS_LDW:
326
1.33k
    case BPF_INS_LDH:
327
2.12k
    case BPF_INS_LDB:
328
3.26k
    case BPF_INS_LDDW:
329
3.26k
      if (BPF_MODE(opcode) == BPF_MODE_ABS || BPF_MODE(opcode) == BPF_MODE_IND) {
330
2.69k
        PUSH_WRITE(BPF_REG_R0);
331
2.69k
      }
332
3.26k
      break;
333
590
    case BPF_INS_EXIT:
334
590
      PUSH_READ(BPF_REG_R0);
335
590
      break;
336
18.2k
    }
337
18.2k
    return;
338
18.2k
  }
339
340
  /* cBPF mode */
341
13.1k
  switch (BPF_CLASS(opcode)) {
342
1.93k
  default:
343
1.93k
    break;
344
4.45k
  case BPF_CLASS_LD:
345
4.45k
    PUSH_WRITE(BPF_REG_A);
346
4.45k
    break;
347
950
  case BPF_CLASS_LDX:
348
950
    PUSH_WRITE(BPF_REG_X);
349
950
    break;
350
97
  case BPF_CLASS_ST:
351
97
    PUSH_READ(BPF_REG_A);
352
97
    break;
353
33
  case BPF_CLASS_STX:
354
33
    PUSH_READ(BPF_REG_X);
355
33
    break;
356
3.74k
  case BPF_CLASS_ALU:
357
3.74k
    PUSH_READ(BPF_REG_A);
358
3.74k
    PUSH_WRITE(BPF_REG_A);
359
3.74k
    break;
360
1.93k
  case BPF_CLASS_JMP:
361
1.93k
    if (insn_id != BPF_INS_JMP) // except the unconditional jump
362
1.27k
      PUSH_READ(BPF_REG_A);
363
1.93k
    break;
364
  /* case BPF_CLASS_RET: */
365
43
  case BPF_CLASS_MISC:
366
43
    if (insn_id == BPF_INS_TAX) {
367
18
      PUSH_READ(BPF_REG_A);
368
18
      PUSH_WRITE(BPF_REG_X);
369
18
    }
370
25
    else {
371
25
      PUSH_READ(BPF_REG_X);
372
25
      PUSH_WRITE(BPF_REG_A);
373
25
    }
374
43
    break;
375
13.1k
  }
376
13.1k
}
377
#endif
378
379
/*
380
 * 1. Convert opcode(id) to BPF_INS_*
381
 * 2. Set regs_read/regs_write/groups
382
 */
383
void BPF_get_insn_id(cs_struct *ud, cs_insn *insn, unsigned int opcode)
384
62.8k
{
385
  // No need to care the mode (cBPF or eBPF) since all checks has be done in
386
  // BPF_getInstruction, we can simply map opcode to BPF_INS_*.
387
62.8k
  bpf_insn id = BPF_INS_INVALID;
388
62.8k
#ifndef CAPSTONE_DIET
389
62.8k
  cs_detail *detail;
390
62.8k
  bpf_insn_group grp;
391
392
62.8k
  detail = insn->detail;
393
62.8k
 #define PUSH_GROUP(grp) do { \
394
62.8k
    if (detail) { \
395
31.4k
      detail->groups[detail->groups_count] = grp; \
396
31.4k
      detail->groups_count++; \
397
31.4k
    } \
398
62.8k
  } while(0)
399
#else
400
 #define PUSH_GROUP(grp) do {} while(0)
401
#endif
402
403
62.8k
  switch (BPF_CLASS(opcode)) {
404
0
  default:  // will never happen
405
0
    break;
406
15.4k
  case BPF_CLASS_LD:
407
20.1k
  case BPF_CLASS_LDX:
408
20.1k
    id = op2insn_ld(opcode);
409
20.1k
    PUSH_GROUP(BPF_GRP_LOAD);
410
20.1k
    break;
411
2.96k
  case BPF_CLASS_ST:
412
6.46k
  case BPF_CLASS_STX:
413
6.46k
    id = op2insn_st(opcode);
414
6.46k
    PUSH_GROUP(BPF_GRP_STORE);
415
6.46k
    break;
416
11.8k
  case BPF_CLASS_ALU:
417
11.8k
    id = op2insn_alu(opcode);
418
11.8k
    PUSH_GROUP(BPF_GRP_ALU);
419
11.8k
    break;
420
14.1k
  case BPF_CLASS_JMP:
421
14.1k
    id = op2insn_jmp(opcode);
422
14.1k
#ifndef CAPSTONE_DIET
423
14.1k
    grp = BPF_GRP_JUMP;
424
14.1k
    if (id == BPF_INS_CALL || id == BPF_INS_CALLX)
425
2.09k
      grp = BPF_GRP_CALL;
426
12.0k
    else if (id == BPF_INS_EXIT)
427
1.18k
      grp = BPF_GRP_RETURN;
428
14.1k
    PUSH_GROUP(grp);
429
14.1k
#endif
430
14.1k
    break;
431
3.87k
  case BPF_CLASS_RET:
432
3.87k
    id = BPF_INS_RET;
433
3.87k
    PUSH_GROUP(BPF_GRP_RETURN);
434
3.87k
    break;
435
  // BPF_CLASS_MISC and BPF_CLASS_ALU64 have exactly same value
436
6.35k
  case BPF_CLASS_MISC:
437
  /* case BPF_CLASS_ALU64: */
438
6.35k
    if (EBPF_MODE(ud)) {
439
      // ALU64 in eBPF
440
6.26k
      id = op2insn_alu(opcode);
441
6.26k
      PUSH_GROUP(BPF_GRP_ALU);
442
6.26k
    }
443
86
    else {
444
86
      if (BPF_MISCOP(opcode) == BPF_MISCOP_TXA)
445
50
        id = BPF_INS_TXA;
446
36
      else
447
36
        id = BPF_INS_TAX;
448
86
      PUSH_GROUP(BPF_GRP_MISC);
449
86
    }
450
6.35k
    break;
451
62.8k
  }
452
453
62.8k
  insn->id = id;
454
62.8k
#undef PUSH_GROUP
455
456
62.8k
#ifndef CAPSTONE_DIET
457
62.8k
  if (detail) {
458
31.4k
    update_regs_access(ud, detail, id, opcode);
459
31.4k
  }
460
62.8k
#endif
461
62.8k
}
462
463
static void sort_and_uniq(cs_regs arr, uint8_t n, uint8_t *new_n)
464
0
{
465
  /* arr is always a tiny (usually n < 3) array,
466
   * a simple O(n^2) sort is efficient enough. */
467
0
  int i;
468
0
  int j;
469
0
  int iMin;
470
0
  int tmp;
471
472
  /* a modified selection sort for sorting and making unique */
473
0
  for (j = 0; j < n; j++) {
474
    /* arr[iMin] will be min(arr[j .. n-1]) */
475
0
    iMin = j;
476
0
    for (i = j + 1; i < n; i++) {
477
0
      if (arr[i] < arr[iMin])
478
0
        iMin = i;
479
0
    }
480
0
    if (j != 0 && arr[iMin] == arr[j - 1]) { // duplicate ele found
481
0
      arr[iMin] = arr[n - 1];
482
0
      --n;
483
0
    }
484
0
    else {
485
0
      tmp = arr[iMin];
486
0
      arr[iMin] = arr[j];
487
0
      arr[j] = tmp;
488
0
    }
489
0
  }
490
491
0
  *new_n = n;
492
0
}
493
void BPF_reg_access(const cs_insn *insn,
494
    cs_regs regs_read, uint8_t *regs_read_count,
495
    cs_regs regs_write, uint8_t *regs_write_count)
496
0
{
497
0
  unsigned i;
498
0
  uint8_t read_count, write_count;
499
0
  const cs_bpf *bpf = &(insn->detail->bpf);
500
501
0
  read_count = insn->detail->regs_read_count;
502
0
  write_count = insn->detail->regs_write_count;
503
504
  // implicit registers
505
0
  memcpy(regs_read, insn->detail->regs_read, read_count * sizeof(insn->detail->regs_read[0]));
506
0
  memcpy(regs_write, insn->detail->regs_write, write_count * sizeof(insn->detail->regs_write[0]));
507
508
0
  for (i = 0; i < bpf->op_count; i++) {
509
0
    const cs_bpf_op *op = &(bpf->operands[i]);
510
0
    switch (op->type) {
511
0
    default:
512
0
      break;
513
0
    case BPF_OP_REG:
514
0
      if (op->access & CS_AC_READ) {
515
0
        regs_read[read_count] = op->reg;
516
0
        read_count++;
517
0
      }
518
0
      if (op->access & CS_AC_WRITE) {
519
0
        regs_write[write_count] = op->reg;
520
0
        write_count++;
521
0
      }
522
0
      break;
523
0
    case BPF_OP_MEM:
524
0
      if (op->mem.base != BPF_REG_INVALID) {
525
0
        regs_read[read_count] = op->mem.base;
526
0
        read_count++;
527
0
      }
528
0
      break;
529
0
    }
530
0
  }
531
532
0
  sort_and_uniq(regs_read, read_count, regs_read_count);
533
0
  sort_and_uniq(regs_write, write_count, regs_write_count);
534
0
}