linux/kernel/bpf/disasm.c
<<
>>
Prefs
   1/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
   2 * Copyright (c) 2016 Facebook
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of version 2 of the GNU General Public
   6 * License as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful, but
   9 * WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11 * General Public License for more details.
  12 */
  13
  14#include <linux/bpf.h>
  15
  16#include "disasm.h"
  17
  18#define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
  19static const char * const func_id_str[] = {
  20        __BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN)
  21};
  22#undef __BPF_FUNC_STR_FN
  23
  24static const char *__func_get_name(const struct bpf_insn_cbs *cbs,
  25                                   const struct bpf_insn *insn,
  26                                   char *buff, size_t len)
  27{
  28        BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID);
  29
  30        if (insn->src_reg != BPF_PSEUDO_CALL &&
  31            insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID &&
  32            func_id_str[insn->imm])
  33                return func_id_str[insn->imm];
  34
  35        if (cbs && cbs->cb_call)
  36                return cbs->cb_call(cbs->private_data, insn);
  37
  38        if (insn->src_reg == BPF_PSEUDO_CALL)
  39                snprintf(buff, len, "%+d", insn->imm);
  40
  41        return buff;
  42}
  43
  44static const char *__func_imm_name(const struct bpf_insn_cbs *cbs,
  45                                   const struct bpf_insn *insn,
  46                                   u64 full_imm, char *buff, size_t len)
  47{
  48        if (cbs && cbs->cb_imm)
  49                return cbs->cb_imm(cbs->private_data, insn, full_imm);
  50
  51        snprintf(buff, len, "0x%llx", (unsigned long long)full_imm);
  52        return buff;
  53}
  54
  55const char *func_id_name(int id)
  56{
  57        if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id])
  58                return func_id_str[id];
  59        else
  60                return "unknown";
  61}
  62
  63const char *const bpf_class_string[8] = {
  64        [BPF_LD]    = "ld",
  65        [BPF_LDX]   = "ldx",
  66        [BPF_ST]    = "st",
  67        [BPF_STX]   = "stx",
  68        [BPF_ALU]   = "alu",
  69        [BPF_JMP]   = "jmp",
  70        [BPF_JMP32] = "jmp32",
  71        [BPF_ALU64] = "alu64",
  72};
  73
  74const char *const bpf_alu_string[16] = {
  75        [BPF_ADD >> 4]  = "+=",
  76        [BPF_SUB >> 4]  = "-=",
  77        [BPF_MUL >> 4]  = "*=",
  78        [BPF_DIV >> 4]  = "/=",
  79        [BPF_OR  >> 4]  = "|=",
  80        [BPF_AND >> 4]  = "&=",
  81        [BPF_LSH >> 4]  = "<<=",
  82        [BPF_RSH >> 4]  = ">>=",
  83        [BPF_NEG >> 4]  = "neg",
  84        [BPF_MOD >> 4]  = "%=",
  85        [BPF_XOR >> 4]  = "^=",
  86        [BPF_MOV >> 4]  = "=",
  87        [BPF_ARSH >> 4] = "s>>=",
  88        [BPF_END >> 4]  = "endian",
  89};
  90
  91static const char *const bpf_ldst_string[] = {
  92        [BPF_W >> 3]  = "u32",
  93        [BPF_H >> 3]  = "u16",
  94        [BPF_B >> 3]  = "u8",
  95        [BPF_DW >> 3] = "u64",
  96};
  97
  98static const char *const bpf_jmp_string[16] = {
  99        [BPF_JA >> 4]   = "jmp",
 100        [BPF_JEQ >> 4]  = "==",
 101        [BPF_JGT >> 4]  = ">",
 102        [BPF_JLT >> 4]  = "<",
 103        [BPF_JGE >> 4]  = ">=",
 104        [BPF_JLE >> 4]  = "<=",
 105        [BPF_JSET >> 4] = "&",
 106        [BPF_JNE >> 4]  = "!=",
 107        [BPF_JSGT >> 4] = "s>",
 108        [BPF_JSLT >> 4] = "s<",
 109        [BPF_JSGE >> 4] = "s>=",
 110        [BPF_JSLE >> 4] = "s<=",
 111        [BPF_CALL >> 4] = "call",
 112        [BPF_EXIT >> 4] = "exit",
 113};
 114
 115static void print_bpf_end_insn(bpf_insn_print_t verbose,
 116                               void *private_data,
 117                               const struct bpf_insn *insn)
 118{
 119        verbose(private_data, "(%02x) r%d = %s%d r%d\n",
 120                insn->code, insn->dst_reg,
 121                BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
 122                insn->imm, insn->dst_reg);
 123}
 124
 125void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 126                    const struct bpf_insn *insn,
 127                    bool allow_ptr_leaks)
 128{
 129        const bpf_insn_print_t verbose = cbs->cb_print;
 130        u8 class = BPF_CLASS(insn->code);
 131
 132        if (class == BPF_ALU || class == BPF_ALU64) {
 133                if (BPF_OP(insn->code) == BPF_END) {
 134                        if (class == BPF_ALU64)
 135                                verbose(cbs->private_data, "BUG_alu64_%02x\n", insn->code);
 136                        else
 137                                print_bpf_end_insn(verbose, cbs->private_data, insn);
 138                } else if (BPF_OP(insn->code) == BPF_NEG) {
 139                        verbose(cbs->private_data, "(%02x) %c%d = -%c%d\n",
 140                                insn->code, class == BPF_ALU ? 'w' : 'r',
 141                                insn->dst_reg, class == BPF_ALU ? 'w' : 'r',
 142                                insn->dst_reg);
 143                } else if (BPF_SRC(insn->code) == BPF_X) {
 144                        verbose(cbs->private_data, "(%02x) %c%d %s %c%d\n",
 145                                insn->code, class == BPF_ALU ? 'w' : 'r',
 146                                insn->dst_reg,
 147                                bpf_alu_string[BPF_OP(insn->code) >> 4],
 148                                class == BPF_ALU ? 'w' : 'r',
 149                                insn->src_reg);
 150                } else {
 151                        verbose(cbs->private_data, "(%02x) %c%d %s %d\n",
 152                                insn->code, class == BPF_ALU ? 'w' : 'r',
 153                                insn->dst_reg,
 154                                bpf_alu_string[BPF_OP(insn->code) >> 4],
 155                                insn->imm);
 156                }
 157        } else if (class == BPF_STX) {
 158                if (BPF_MODE(insn->code) == BPF_MEM)
 159                        verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d\n",
 160                                insn->code,
 161                                bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 162                                insn->dst_reg,
 163                                insn->off, insn->src_reg);
 164                else if (BPF_MODE(insn->code) == BPF_XADD)
 165                        verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) += r%d\n",
 166                                insn->code,
 167                                bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 168                                insn->dst_reg, insn->off,
 169                                insn->src_reg);
 170                else
 171                        verbose(cbs->private_data, "BUG_%02x\n", insn->code);
 172        } else if (class == BPF_ST) {
 173                if (BPF_MODE(insn->code) != BPF_MEM) {
 174                        verbose(cbs->private_data, "BUG_st_%02x\n", insn->code);
 175                        return;
 176                }
 177                verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d\n",
 178                        insn->code,
 179                        bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 180                        insn->dst_reg,
 181                        insn->off, insn->imm);
 182        } else if (class == BPF_LDX) {
 183                if (BPF_MODE(insn->code) != BPF_MEM) {
 184                        verbose(cbs->private_data, "BUG_ldx_%02x\n", insn->code);
 185                        return;
 186                }
 187                verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)\n",
 188                        insn->code, insn->dst_reg,
 189                        bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 190                        insn->src_reg, insn->off);
 191        } else if (class == BPF_LD) {
 192                if (BPF_MODE(insn->code) == BPF_ABS) {
 193                        verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]\n",
 194                                insn->code,
 195                                bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 196                                insn->imm);
 197                } else if (BPF_MODE(insn->code) == BPF_IND) {
 198                        verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
 199                                insn->code,
 200                                bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 201                                insn->src_reg, insn->imm);
 202                } else if (BPF_MODE(insn->code) == BPF_IMM &&
 203                           BPF_SIZE(insn->code) == BPF_DW) {
 204                        /* At this point, we already made sure that the second
 205                         * part of the ldimm64 insn is accessible.
 206                         */
 207                        u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
 208                        bool map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD;
 209                        char tmp[64];
 210
 211                        if (map_ptr && !allow_ptr_leaks)
 212                                imm = 0;
 213
 214                        verbose(cbs->private_data, "(%02x) r%d = %s\n",
 215                                insn->code, insn->dst_reg,
 216                                __func_imm_name(cbs, insn, imm,
 217                                                tmp, sizeof(tmp)));
 218                } else {
 219                        verbose(cbs->private_data, "BUG_ld_%02x\n", insn->code);
 220                        return;
 221                }
 222        } else if (class == BPF_JMP32 || class == BPF_JMP) {
 223                u8 opcode = BPF_OP(insn->code);
 224
 225                if (opcode == BPF_CALL) {
 226                        char tmp[64];
 227
 228                        if (insn->src_reg == BPF_PSEUDO_CALL) {
 229                                verbose(cbs->private_data, "(%02x) call pc%s\n",
 230                                        insn->code,
 231                                        __func_get_name(cbs, insn,
 232                                                        tmp, sizeof(tmp)));
 233                        } else {
 234                                strcpy(tmp, "unknown");
 235                                verbose(cbs->private_data, "(%02x) call %s#%d\n", insn->code,
 236                                        __func_get_name(cbs, insn,
 237                                                        tmp, sizeof(tmp)),
 238                                        insn->imm);
 239                        }
 240                } else if (insn->code == (BPF_JMP | BPF_JA)) {
 241                        verbose(cbs->private_data, "(%02x) goto pc%+d\n",
 242                                insn->code, insn->off);
 243                } else if (insn->code == (BPF_JMP | BPF_EXIT)) {
 244                        verbose(cbs->private_data, "(%02x) exit\n", insn->code);
 245                } else if (BPF_SRC(insn->code) == BPF_X) {
 246                        verbose(cbs->private_data,
 247                                "(%02x) if %c%d %s %c%d goto pc%+d\n",
 248                                insn->code, class == BPF_JMP32 ? 'w' : 'r',
 249                                insn->dst_reg,
 250                                bpf_jmp_string[BPF_OP(insn->code) >> 4],
 251                                class == BPF_JMP32 ? 'w' : 'r',
 252                                insn->src_reg, insn->off);
 253                } else {
 254                        verbose(cbs->private_data,
 255                                "(%02x) if %c%d %s 0x%x goto pc%+d\n",
 256                                insn->code, class == BPF_JMP32 ? 'w' : 'r',
 257                                insn->dst_reg,
 258                                bpf_jmp_string[BPF_OP(insn->code) >> 4],
 259                                insn->imm, insn->off);
 260                }
 261        } else {
 262                verbose(cbs->private_data, "(%02x) %s\n",
 263                        insn->code, bpf_class_string[class]);
 264        }
 265}
 266