mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
![Thomas Gleixner](/assets/img/avatar_default.png)
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of version 2 of the gnu general public license as published by the free software foundation this program is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 64 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190529141901.894819585@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
258 lines
7.4 KiB
C
258 lines
7.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
|
|
* Copyright (c) 2016 Facebook
|
|
*/
|
|
|
|
#include <linux/bpf.h>
|
|
|
|
#include "disasm.h"
|
|
|
|
#define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
|
|
static const char * const func_id_str[] = {
|
|
__BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN)
|
|
};
|
|
#undef __BPF_FUNC_STR_FN
|
|
|
|
static const char *__func_get_name(const struct bpf_insn_cbs *cbs,
|
|
const struct bpf_insn *insn,
|
|
char *buff, size_t len)
|
|
{
|
|
BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID);
|
|
|
|
if (insn->src_reg != BPF_PSEUDO_CALL &&
|
|
insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID &&
|
|
func_id_str[insn->imm])
|
|
return func_id_str[insn->imm];
|
|
|
|
if (cbs && cbs->cb_call)
|
|
return cbs->cb_call(cbs->private_data, insn);
|
|
|
|
if (insn->src_reg == BPF_PSEUDO_CALL)
|
|
snprintf(buff, len, "%+d", insn->imm);
|
|
|
|
return buff;
|
|
}
|
|
|
|
static const char *__func_imm_name(const struct bpf_insn_cbs *cbs,
|
|
const struct bpf_insn *insn,
|
|
u64 full_imm, char *buff, size_t len)
|
|
{
|
|
if (cbs && cbs->cb_imm)
|
|
return cbs->cb_imm(cbs->private_data, insn, full_imm);
|
|
|
|
snprintf(buff, len, "0x%llx", (unsigned long long)full_imm);
|
|
return buff;
|
|
}
|
|
|
|
const char *func_id_name(int id)
|
|
{
|
|
if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id])
|
|
return func_id_str[id];
|
|
else
|
|
return "unknown";
|
|
}
|
|
|
|
const char *const bpf_class_string[8] = {
|
|
[BPF_LD] = "ld",
|
|
[BPF_LDX] = "ldx",
|
|
[BPF_ST] = "st",
|
|
[BPF_STX] = "stx",
|
|
[BPF_ALU] = "alu",
|
|
[BPF_JMP] = "jmp",
|
|
[BPF_JMP32] = "jmp32",
|
|
[BPF_ALU64] = "alu64",
|
|
};
|
|
|
|
const char *const bpf_alu_string[16] = {
|
|
[BPF_ADD >> 4] = "+=",
|
|
[BPF_SUB >> 4] = "-=",
|
|
[BPF_MUL >> 4] = "*=",
|
|
[BPF_DIV >> 4] = "/=",
|
|
[BPF_OR >> 4] = "|=",
|
|
[BPF_AND >> 4] = "&=",
|
|
[BPF_LSH >> 4] = "<<=",
|
|
[BPF_RSH >> 4] = ">>=",
|
|
[BPF_NEG >> 4] = "neg",
|
|
[BPF_MOD >> 4] = "%=",
|
|
[BPF_XOR >> 4] = "^=",
|
|
[BPF_MOV >> 4] = "=",
|
|
[BPF_ARSH >> 4] = "s>>=",
|
|
[BPF_END >> 4] = "endian",
|
|
};
|
|
|
|
static const char *const bpf_ldst_string[] = {
|
|
[BPF_W >> 3] = "u32",
|
|
[BPF_H >> 3] = "u16",
|
|
[BPF_B >> 3] = "u8",
|
|
[BPF_DW >> 3] = "u64",
|
|
};
|
|
|
|
static const char *const bpf_jmp_string[16] = {
|
|
[BPF_JA >> 4] = "jmp",
|
|
[BPF_JEQ >> 4] = "==",
|
|
[BPF_JGT >> 4] = ">",
|
|
[BPF_JLT >> 4] = "<",
|
|
[BPF_JGE >> 4] = ">=",
|
|
[BPF_JLE >> 4] = "<=",
|
|
[BPF_JSET >> 4] = "&",
|
|
[BPF_JNE >> 4] = "!=",
|
|
[BPF_JSGT >> 4] = "s>",
|
|
[BPF_JSLT >> 4] = "s<",
|
|
[BPF_JSGE >> 4] = "s>=",
|
|
[BPF_JSLE >> 4] = "s<=",
|
|
[BPF_CALL >> 4] = "call",
|
|
[BPF_EXIT >> 4] = "exit",
|
|
};
|
|
|
|
static void print_bpf_end_insn(bpf_insn_print_t verbose,
|
|
void *private_data,
|
|
const struct bpf_insn *insn)
|
|
{
|
|
verbose(private_data, "(%02x) r%d = %s%d r%d\n",
|
|
insn->code, insn->dst_reg,
|
|
BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
|
|
insn->imm, insn->dst_reg);
|
|
}
|
|
|
|
void print_bpf_insn(const struct bpf_insn_cbs *cbs,
|
|
const struct bpf_insn *insn,
|
|
bool allow_ptr_leaks)
|
|
{
|
|
const bpf_insn_print_t verbose = cbs->cb_print;
|
|
u8 class = BPF_CLASS(insn->code);
|
|
|
|
if (class == BPF_ALU || class == BPF_ALU64) {
|
|
if (BPF_OP(insn->code) == BPF_END) {
|
|
if (class == BPF_ALU64)
|
|
verbose(cbs->private_data, "BUG_alu64_%02x\n", insn->code);
|
|
else
|
|
print_bpf_end_insn(verbose, cbs->private_data, insn);
|
|
} else if (BPF_OP(insn->code) == BPF_NEG) {
|
|
verbose(cbs->private_data, "(%02x) %c%d = -%c%d\n",
|
|
insn->code, class == BPF_ALU ? 'w' : 'r',
|
|
insn->dst_reg, class == BPF_ALU ? 'w' : 'r',
|
|
insn->dst_reg);
|
|
} else if (BPF_SRC(insn->code) == BPF_X) {
|
|
verbose(cbs->private_data, "(%02x) %c%d %s %c%d\n",
|
|
insn->code, class == BPF_ALU ? 'w' : 'r',
|
|
insn->dst_reg,
|
|
bpf_alu_string[BPF_OP(insn->code) >> 4],
|
|
class == BPF_ALU ? 'w' : 'r',
|
|
insn->src_reg);
|
|
} else {
|
|
verbose(cbs->private_data, "(%02x) %c%d %s %d\n",
|
|
insn->code, class == BPF_ALU ? 'w' : 'r',
|
|
insn->dst_reg,
|
|
bpf_alu_string[BPF_OP(insn->code) >> 4],
|
|
insn->imm);
|
|
}
|
|
} else if (class == BPF_STX) {
|
|
if (BPF_MODE(insn->code) == BPF_MEM)
|
|
verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d\n",
|
|
insn->code,
|
|
bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
|
|
insn->dst_reg,
|
|
insn->off, insn->src_reg);
|
|
else if (BPF_MODE(insn->code) == BPF_XADD)
|
|
verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) += r%d\n",
|
|
insn->code,
|
|
bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
|
|
insn->dst_reg, insn->off,
|
|
insn->src_reg);
|
|
else
|
|
verbose(cbs->private_data, "BUG_%02x\n", insn->code);
|
|
} else if (class == BPF_ST) {
|
|
if (BPF_MODE(insn->code) != BPF_MEM) {
|
|
verbose(cbs->private_data, "BUG_st_%02x\n", insn->code);
|
|
return;
|
|
}
|
|
verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d\n",
|
|
insn->code,
|
|
bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
|
|
insn->dst_reg,
|
|
insn->off, insn->imm);
|
|
} else if (class == BPF_LDX) {
|
|
if (BPF_MODE(insn->code) != BPF_MEM) {
|
|
verbose(cbs->private_data, "BUG_ldx_%02x\n", insn->code);
|
|
return;
|
|
}
|
|
verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)\n",
|
|
insn->code, insn->dst_reg,
|
|
bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
|
|
insn->src_reg, insn->off);
|
|
} else if (class == BPF_LD) {
|
|
if (BPF_MODE(insn->code) == BPF_ABS) {
|
|
verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]\n",
|
|
insn->code,
|
|
bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
|
|
insn->imm);
|
|
} else if (BPF_MODE(insn->code) == BPF_IND) {
|
|
verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
|
|
insn->code,
|
|
bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
|
|
insn->src_reg, insn->imm);
|
|
} else if (BPF_MODE(insn->code) == BPF_IMM &&
|
|
BPF_SIZE(insn->code) == BPF_DW) {
|
|
/* At this point, we already made sure that the second
|
|
* part of the ldimm64 insn is accessible.
|
|
*/
|
|
u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
|
|
bool is_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD ||
|
|
insn->src_reg == BPF_PSEUDO_MAP_VALUE;
|
|
char tmp[64];
|
|
|
|
if (is_ptr && !allow_ptr_leaks)
|
|
imm = 0;
|
|
|
|
verbose(cbs->private_data, "(%02x) r%d = %s\n",
|
|
insn->code, insn->dst_reg,
|
|
__func_imm_name(cbs, insn, imm,
|
|
tmp, sizeof(tmp)));
|
|
} else {
|
|
verbose(cbs->private_data, "BUG_ld_%02x\n", insn->code);
|
|
return;
|
|
}
|
|
} else if (class == BPF_JMP32 || class == BPF_JMP) {
|
|
u8 opcode = BPF_OP(insn->code);
|
|
|
|
if (opcode == BPF_CALL) {
|
|
char tmp[64];
|
|
|
|
if (insn->src_reg == BPF_PSEUDO_CALL) {
|
|
verbose(cbs->private_data, "(%02x) call pc%s\n",
|
|
insn->code,
|
|
__func_get_name(cbs, insn,
|
|
tmp, sizeof(tmp)));
|
|
} else {
|
|
strcpy(tmp, "unknown");
|
|
verbose(cbs->private_data, "(%02x) call %s#%d\n", insn->code,
|
|
__func_get_name(cbs, insn,
|
|
tmp, sizeof(tmp)),
|
|
insn->imm);
|
|
}
|
|
} else if (insn->code == (BPF_JMP | BPF_JA)) {
|
|
verbose(cbs->private_data, "(%02x) goto pc%+d\n",
|
|
insn->code, insn->off);
|
|
} else if (insn->code == (BPF_JMP | BPF_EXIT)) {
|
|
verbose(cbs->private_data, "(%02x) exit\n", insn->code);
|
|
} else if (BPF_SRC(insn->code) == BPF_X) {
|
|
verbose(cbs->private_data,
|
|
"(%02x) if %c%d %s %c%d goto pc%+d\n",
|
|
insn->code, class == BPF_JMP32 ? 'w' : 'r',
|
|
insn->dst_reg,
|
|
bpf_jmp_string[BPF_OP(insn->code) >> 4],
|
|
class == BPF_JMP32 ? 'w' : 'r',
|
|
insn->src_reg, insn->off);
|
|
} else {
|
|
verbose(cbs->private_data,
|
|
"(%02x) if %c%d %s 0x%x goto pc%+d\n",
|
|
insn->code, class == BPF_JMP32 ? 'w' : 'r',
|
|
insn->dst_reg,
|
|
bpf_jmp_string[BPF_OP(insn->code) >> 4],
|
|
insn->imm, insn->off);
|
|
}
|
|
} else {
|
|
verbose(cbs->private_data, "(%02x) %s\n",
|
|
insn->code, bpf_class_string[class]);
|
|
}
|
|
}
|