Coverage Report

Created: 2024-08-21 06:24

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