0001
0002
0003
0004
0005
0006 #include <linux/bpf.h>
0007
0008 #include "disasm.h"
0009
0010 #define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
0011 static const char * const func_id_str[] = {
0012 __BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN)
0013 };
0014 #undef __BPF_FUNC_STR_FN
0015
0016 static const char *__func_get_name(const struct bpf_insn_cbs *cbs,
0017 const struct bpf_insn *insn,
0018 char *buff, size_t len)
0019 {
0020 BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID);
0021
0022 if (!insn->src_reg &&
0023 insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID &&
0024 func_id_str[insn->imm])
0025 return func_id_str[insn->imm];
0026
0027 if (cbs && cbs->cb_call) {
0028 const char *res;
0029
0030 res = cbs->cb_call(cbs->private_data, insn);
0031 if (res)
0032 return res;
0033 }
0034
0035 if (insn->src_reg == BPF_PSEUDO_CALL)
0036 snprintf(buff, len, "%+d", insn->imm);
0037 else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL)
0038 snprintf(buff, len, "kernel-function");
0039
0040 return buff;
0041 }
0042
0043 static const char *__func_imm_name(const struct bpf_insn_cbs *cbs,
0044 const struct bpf_insn *insn,
0045 u64 full_imm, char *buff, size_t len)
0046 {
0047 if (cbs && cbs->cb_imm)
0048 return cbs->cb_imm(cbs->private_data, insn, full_imm);
0049
0050 snprintf(buff, len, "0x%llx", (unsigned long long)full_imm);
0051 return buff;
0052 }
0053
0054 const char *func_id_name(int id)
0055 {
0056 if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id])
0057 return func_id_str[id];
0058 else
0059 return "unknown";
0060 }
0061
0062 const char *const bpf_class_string[8] = {
0063 [BPF_LD] = "ld",
0064 [BPF_LDX] = "ldx",
0065 [BPF_ST] = "st",
0066 [BPF_STX] = "stx",
0067 [BPF_ALU] = "alu",
0068 [BPF_JMP] = "jmp",
0069 [BPF_JMP32] = "jmp32",
0070 [BPF_ALU64] = "alu64",
0071 };
0072
0073 const char *const bpf_alu_string[16] = {
0074 [BPF_ADD >> 4] = "+=",
0075 [BPF_SUB >> 4] = "-=",
0076 [BPF_MUL >> 4] = "*=",
0077 [BPF_DIV >> 4] = "/=",
0078 [BPF_OR >> 4] = "|=",
0079 [BPF_AND >> 4] = "&=",
0080 [BPF_LSH >> 4] = "<<=",
0081 [BPF_RSH >> 4] = ">>=",
0082 [BPF_NEG >> 4] = "neg",
0083 [BPF_MOD >> 4] = "%=",
0084 [BPF_XOR >> 4] = "^=",
0085 [BPF_MOV >> 4] = "=",
0086 [BPF_ARSH >> 4] = "s>>=",
0087 [BPF_END >> 4] = "endian",
0088 };
0089
0090 static const char *const bpf_atomic_alu_string[16] = {
0091 [BPF_ADD >> 4] = "add",
0092 [BPF_AND >> 4] = "and",
0093 [BPF_OR >> 4] = "or",
0094 [BPF_XOR >> 4] = "xor",
0095 };
0096
0097 static const char *const bpf_ldst_string[] = {
0098 [BPF_W >> 3] = "u32",
0099 [BPF_H >> 3] = "u16",
0100 [BPF_B >> 3] = "u8",
0101 [BPF_DW >> 3] = "u64",
0102 };
0103
0104 static const char *const bpf_jmp_string[16] = {
0105 [BPF_JA >> 4] = "jmp",
0106 [BPF_JEQ >> 4] = "==",
0107 [BPF_JGT >> 4] = ">",
0108 [BPF_JLT >> 4] = "<",
0109 [BPF_JGE >> 4] = ">=",
0110 [BPF_JLE >> 4] = "<=",
0111 [BPF_JSET >> 4] = "&",
0112 [BPF_JNE >> 4] = "!=",
0113 [BPF_JSGT >> 4] = "s>",
0114 [BPF_JSLT >> 4] = "s<",
0115 [BPF_JSGE >> 4] = "s>=",
0116 [BPF_JSLE >> 4] = "s<=",
0117 [BPF_CALL >> 4] = "call",
0118 [BPF_EXIT >> 4] = "exit",
0119 };
0120
0121 static void print_bpf_end_insn(bpf_insn_print_t verbose,
0122 void *private_data,
0123 const struct bpf_insn *insn)
0124 {
0125 verbose(private_data, "(%02x) r%d = %s%d r%d\n",
0126 insn->code, insn->dst_reg,
0127 BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
0128 insn->imm, insn->dst_reg);
0129 }
0130
0131 void print_bpf_insn(const struct bpf_insn_cbs *cbs,
0132 const struct bpf_insn *insn,
0133 bool allow_ptr_leaks)
0134 {
0135 const bpf_insn_print_t verbose = cbs->cb_print;
0136 u8 class = BPF_CLASS(insn->code);
0137
0138 if (class == BPF_ALU || class == BPF_ALU64) {
0139 if (BPF_OP(insn->code) == BPF_END) {
0140 if (class == BPF_ALU64)
0141 verbose(cbs->private_data, "BUG_alu64_%02x\n", insn->code);
0142 else
0143 print_bpf_end_insn(verbose, cbs->private_data, insn);
0144 } else if (BPF_OP(insn->code) == BPF_NEG) {
0145 verbose(cbs->private_data, "(%02x) %c%d = -%c%d\n",
0146 insn->code, class == BPF_ALU ? 'w' : 'r',
0147 insn->dst_reg, class == BPF_ALU ? 'w' : 'r',
0148 insn->dst_reg);
0149 } else if (BPF_SRC(insn->code) == BPF_X) {
0150 verbose(cbs->private_data, "(%02x) %c%d %s %c%d\n",
0151 insn->code, class == BPF_ALU ? 'w' : 'r',
0152 insn->dst_reg,
0153 bpf_alu_string[BPF_OP(insn->code) >> 4],
0154 class == BPF_ALU ? 'w' : 'r',
0155 insn->src_reg);
0156 } else {
0157 verbose(cbs->private_data, "(%02x) %c%d %s %d\n",
0158 insn->code, class == BPF_ALU ? 'w' : 'r',
0159 insn->dst_reg,
0160 bpf_alu_string[BPF_OP(insn->code) >> 4],
0161 insn->imm);
0162 }
0163 } else if (class == BPF_STX) {
0164 if (BPF_MODE(insn->code) == BPF_MEM)
0165 verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d\n",
0166 insn->code,
0167 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0168 insn->dst_reg,
0169 insn->off, insn->src_reg);
0170 else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
0171 (insn->imm == BPF_ADD || insn->imm == BPF_AND ||
0172 insn->imm == BPF_OR || insn->imm == BPF_XOR)) {
0173 verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) %s r%d\n",
0174 insn->code,
0175 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0176 insn->dst_reg, insn->off,
0177 bpf_alu_string[BPF_OP(insn->imm) >> 4],
0178 insn->src_reg);
0179 } else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
0180 (insn->imm == (BPF_ADD | BPF_FETCH) ||
0181 insn->imm == (BPF_AND | BPF_FETCH) ||
0182 insn->imm == (BPF_OR | BPF_FETCH) ||
0183 insn->imm == (BPF_XOR | BPF_FETCH))) {
0184 verbose(cbs->private_data, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)\n",
0185 insn->code, insn->src_reg,
0186 BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
0187 bpf_atomic_alu_string[BPF_OP(insn->imm) >> 4],
0188 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0189 insn->dst_reg, insn->off, insn->src_reg);
0190 } else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
0191 insn->imm == BPF_CMPXCHG) {
0192 verbose(cbs->private_data, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)\n",
0193 insn->code,
0194 BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
0195 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0196 insn->dst_reg, insn->off,
0197 insn->src_reg);
0198 } else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
0199 insn->imm == BPF_XCHG) {
0200 verbose(cbs->private_data, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)\n",
0201 insn->code, insn->src_reg,
0202 BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
0203 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0204 insn->dst_reg, insn->off, insn->src_reg);
0205 } else {
0206 verbose(cbs->private_data, "BUG_%02x\n", insn->code);
0207 }
0208 } else if (class == BPF_ST) {
0209 if (BPF_MODE(insn->code) == BPF_MEM) {
0210 verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d\n",
0211 insn->code,
0212 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0213 insn->dst_reg,
0214 insn->off, insn->imm);
0215 } else if (BPF_MODE(insn->code) == 0xc0 ) {
0216 verbose(cbs->private_data, "(%02x) nospec\n", insn->code);
0217 } else {
0218 verbose(cbs->private_data, "BUG_st_%02x\n", insn->code);
0219 }
0220 } else if (class == BPF_LDX) {
0221 if (BPF_MODE(insn->code) != BPF_MEM) {
0222 verbose(cbs->private_data, "BUG_ldx_%02x\n", insn->code);
0223 return;
0224 }
0225 verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)\n",
0226 insn->code, insn->dst_reg,
0227 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0228 insn->src_reg, insn->off);
0229 } else if (class == BPF_LD) {
0230 if (BPF_MODE(insn->code) == BPF_ABS) {
0231 verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]\n",
0232 insn->code,
0233 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0234 insn->imm);
0235 } else if (BPF_MODE(insn->code) == BPF_IND) {
0236 verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
0237 insn->code,
0238 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
0239 insn->src_reg, insn->imm);
0240 } else if (BPF_MODE(insn->code) == BPF_IMM &&
0241 BPF_SIZE(insn->code) == BPF_DW) {
0242
0243
0244
0245 u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
0246 bool is_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD ||
0247 insn->src_reg == BPF_PSEUDO_MAP_VALUE;
0248 char tmp[64];
0249
0250 if (is_ptr && !allow_ptr_leaks)
0251 imm = 0;
0252
0253 verbose(cbs->private_data, "(%02x) r%d = %s\n",
0254 insn->code, insn->dst_reg,
0255 __func_imm_name(cbs, insn, imm,
0256 tmp, sizeof(tmp)));
0257 } else {
0258 verbose(cbs->private_data, "BUG_ld_%02x\n", insn->code);
0259 return;
0260 }
0261 } else if (class == BPF_JMP32 || class == BPF_JMP) {
0262 u8 opcode = BPF_OP(insn->code);
0263
0264 if (opcode == BPF_CALL) {
0265 char tmp[64];
0266
0267 if (insn->src_reg == BPF_PSEUDO_CALL) {
0268 verbose(cbs->private_data, "(%02x) call pc%s\n",
0269 insn->code,
0270 __func_get_name(cbs, insn,
0271 tmp, sizeof(tmp)));
0272 } else {
0273 strcpy(tmp, "unknown");
0274 verbose(cbs->private_data, "(%02x) call %s#%d\n", insn->code,
0275 __func_get_name(cbs, insn,
0276 tmp, sizeof(tmp)),
0277 insn->imm);
0278 }
0279 } else if (insn->code == (BPF_JMP | BPF_JA)) {
0280 verbose(cbs->private_data, "(%02x) goto pc%+d\n",
0281 insn->code, insn->off);
0282 } else if (insn->code == (BPF_JMP | BPF_EXIT)) {
0283 verbose(cbs->private_data, "(%02x) exit\n", insn->code);
0284 } else if (BPF_SRC(insn->code) == BPF_X) {
0285 verbose(cbs->private_data,
0286 "(%02x) if %c%d %s %c%d goto pc%+d\n",
0287 insn->code, class == BPF_JMP32 ? 'w' : 'r',
0288 insn->dst_reg,
0289 bpf_jmp_string[BPF_OP(insn->code) >> 4],
0290 class == BPF_JMP32 ? 'w' : 'r',
0291 insn->src_reg, insn->off);
0292 } else {
0293 verbose(cbs->private_data,
0294 "(%02x) if %c%d %s 0x%x goto pc%+d\n",
0295 insn->code, class == BPF_JMP32 ? 'w' : 'r',
0296 insn->dst_reg,
0297 bpf_jmp_string[BPF_OP(insn->code) >> 4],
0298 insn->imm, insn->off);
0299 }
0300 } else {
0301 verbose(cbs->private_data, "(%02x) %s\n",
0302 insn->code, bpf_class_string[class]);
0303 }
0304 }