Commit 67174bb2 authored by David S. Miller's avatar David S. Miller

Merge branch 'bpf-get-rid-of-global-verifier-state-and-reuse-instruction-printer'

Jakub Kicinski says:

====================
bpf: get rid of global verifier state and reuse instruction printer

This set started off as simple extraction of eBPF verifier's instruction
printer into a separate file but evolved into removal of global state.
The purpose of moving instruction printing code is to be able to reuse it
from the bpftool.

As far as the global verifier lock goes, this set removes the global
variables relating to the log buffer, makes the one-time init done
by bpf_get_skb_set_tunnel_proto() not depend on any external locking,
and performs verifier log writeback as data is produced removing the need
for allocating a potentially large temporary buffer.

The final step of actually removing the verifier lock is left to someone
more competent and self-confident :)

Note that struct bpf_verifier_env is just 40B under two pages now,
we should probably switch to vzalloc() when it's expanded again...

v2:
 - add a selftest;
 - use env buffer and flush on every print (Alexei);
 - handle kernel log allocation failures (Daniel);
 - put the env log members into a struct (Daniel).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 442d713b a2a7d570
...@@ -115,6 +115,21 @@ struct bpf_insn_aux_data { ...@@ -115,6 +115,21 @@ struct bpf_insn_aux_data {
#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
#define BPF_VERIFIER_TMP_LOG_SIZE 1024
struct bpf_verifer_log {
u32 level;
char kbuf[BPF_VERIFIER_TMP_LOG_SIZE];
char __user *ubuf;
u32 len_used;
u32 len_total;
};
static inline bool bpf_verifier_log_full(const struct bpf_verifer_log *log)
{
return log->len_used >= log->len_total - 1;
}
struct bpf_verifier_env; struct bpf_verifier_env;
struct bpf_ext_analyzer_ops { struct bpf_ext_analyzer_ops {
int (*insn_hook)(struct bpf_verifier_env *env, int (*insn_hook)(struct bpf_verifier_env *env,
...@@ -139,6 +154,8 @@ struct bpf_verifier_env { ...@@ -139,6 +154,8 @@ struct bpf_verifier_env {
bool allow_ptr_leaks; bool allow_ptr_leaks;
bool seen_direct_write; bool seen_direct_write;
struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */ struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
struct bpf_verifer_log log;
}; };
int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops,
......
...@@ -87,6 +87,7 @@ static inline int skb_metadata_dst_cmp(const struct sk_buff *skb_a, ...@@ -87,6 +87,7 @@ static inline int skb_metadata_dst_cmp(const struct sk_buff *skb_a,
void metadata_dst_free(struct metadata_dst *); void metadata_dst_free(struct metadata_dst *);
struct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type, struct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type,
gfp_t flags); gfp_t flags);
void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst);
struct metadata_dst __percpu * struct metadata_dst __percpu *
metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags); metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags);
......
...@@ -2,6 +2,7 @@ obj-y := core.o ...@@ -2,6 +2,7 @@ obj-y := core.o
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
obj-$(CONFIG_BPF_SYSCALL) += disasm.o
ifeq ($(CONFIG_NET),y) ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_BPF_SYSCALL) += devmap.o obj-$(CONFIG_BPF_SYSCALL) += devmap.o
ifeq ($(CONFIG_STREAM_PARSER),y) ifeq ($(CONFIG_STREAM_PARSER),y)
......
/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
* Copyright (c) 2016 Facebook
*
* 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.
*/
#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
const char *func_id_name(int id)
{
BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_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_RET] = "BUG",
[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_cb verbose,
struct bpf_verifier_env *env,
const struct bpf_insn *insn)
{
verbose(env, "(%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(bpf_insn_print_cb verbose, struct bpf_verifier_env *env,
const struct bpf_insn *insn, bool allow_ptr_leaks)
{
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(env, "BUG_alu64_%02x\n", insn->code);
else
print_bpf_end_insn(verbose, env, insn);
} else if (BPF_OP(insn->code) == BPF_NEG) {
verbose(env, "(%02x) r%d = %s-r%d\n",
insn->code, insn->dst_reg,
class == BPF_ALU ? "(u32) " : "",
insn->dst_reg);
} else if (BPF_SRC(insn->code) == BPF_X) {
verbose(env, "(%02x) %sr%d %s %sr%d\n",
insn->code, class == BPF_ALU ? "(u32) " : "",
insn->dst_reg,
bpf_alu_string[BPF_OP(insn->code) >> 4],
class == BPF_ALU ? "(u32) " : "",
insn->src_reg);
} else {
verbose(env, "(%02x) %sr%d %s %s%d\n",
insn->code, class == BPF_ALU ? "(u32) " : "",
insn->dst_reg,
bpf_alu_string[BPF_OP(insn->code) >> 4],
class == BPF_ALU ? "(u32) " : "",
insn->imm);
}
} else if (class == BPF_STX) {
if (BPF_MODE(insn->code) == BPF_MEM)
verbose(env, "(%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(env, "(%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(env, "BUG_%02x\n", insn->code);
} else if (class == BPF_ST) {
if (BPF_MODE(insn->code) != BPF_MEM) {
verbose(env, "BUG_st_%02x\n", insn->code);
return;
}
verbose(env, "(%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(env, "BUG_ldx_%02x\n", insn->code);
return;
}
verbose(env, "(%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(env, "(%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(env, "(%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 map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD;
if (map_ptr && !allow_ptr_leaks)
imm = 0;
verbose(env, "(%02x) r%d = 0x%llx\n", insn->code,
insn->dst_reg, (unsigned long long)imm);
} else {
verbose(env, "BUG_ld_%02x\n", insn->code);
return;
}
} else if (class == BPF_JMP) {
u8 opcode = BPF_OP(insn->code);
if (opcode == BPF_CALL) {
verbose(env, "(%02x) call %s#%d\n", insn->code,
func_id_name(insn->imm), insn->imm);
} else if (insn->code == (BPF_JMP | BPF_JA)) {
verbose(env, "(%02x) goto pc%+d\n",
insn->code, insn->off);
} else if (insn->code == (BPF_JMP | BPF_EXIT)) {
verbose(env, "(%02x) exit\n", insn->code);
} else if (BPF_SRC(insn->code) == BPF_X) {
verbose(env, "(%02x) if r%d %s r%d goto pc%+d\n",
insn->code, insn->dst_reg,
bpf_jmp_string[BPF_OP(insn->code) >> 4],
insn->src_reg, insn->off);
} else {
verbose(env, "(%02x) if r%d %s 0x%x goto pc%+d\n",
insn->code, insn->dst_reg,
bpf_jmp_string[BPF_OP(insn->code) >> 4],
insn->imm, insn->off);
}
} else {
verbose(env, "(%02x) %s\n",
insn->code, bpf_class_string[class]);
}
}
/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
* Copyright (c) 2016 Facebook
*
* 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.
*/
#ifndef __BPF_DISASM_H__
#define __BPF_DISASM_H__
#include <linux/bpf.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
extern const char *const bpf_alu_string[16];
extern const char *const bpf_class_string[8];
const char *func_id_name(int id);
struct bpf_verifier_env;
typedef void (*bpf_insn_print_cb)(struct bpf_verifier_env *env,
const char *, ...);
void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env,
const struct bpf_insn *insn, bool allow_ptr_leaks);
#endif
This diff is collapsed.
...@@ -322,3 +322,19 @@ metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags) ...@@ -322,3 +322,19 @@ metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags)
return md_dst; return md_dst;
} }
EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu); EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu);
void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst)
{
int cpu;
#ifdef CONFIG_DST_CACHE
for_each_possible_cpu(cpu) {
struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu);
if (one_md_dst->type == METADATA_IP_TUNNEL)
dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache);
}
#endif
free_percpu(md_dst);
}
EXPORT_SYMBOL_GPL(metadata_dst_free_percpu);
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <asm/cmpxchg.h>
#include <linux/filter.h> #include <linux/filter.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/seccomp.h> #include <linux/seccomp.h>
...@@ -2987,14 +2988,15 @@ static const struct bpf_func_proto * ...@@ -2987,14 +2988,15 @@ static const struct bpf_func_proto *
bpf_get_skb_set_tunnel_proto(enum bpf_func_id which) bpf_get_skb_set_tunnel_proto(enum bpf_func_id which)
{ {
if (!md_dst) { if (!md_dst) {
/* Race is not possible, since it's called from verifier struct metadata_dst __percpu *tmp;
* that is holding verifier mutex.
*/ tmp = metadata_dst_alloc_percpu(IP_TUNNEL_OPTS_MAX,
md_dst = metadata_dst_alloc_percpu(IP_TUNNEL_OPTS_MAX, METADATA_IP_TUNNEL,
METADATA_IP_TUNNEL, GFP_KERNEL);
GFP_KERNEL); if (!tmp)
if (!md_dst)
return NULL; return NULL;
if (cmpxchg(&md_dst, NULL, tmp))
metadata_dst_free_percpu(tmp);
} }
switch (which) { switch (which) {
......
...@@ -11,7 +11,7 @@ SYNOPSIS ...@@ -11,7 +11,7 @@ SYNOPSIS
======== ========
| **bpftool** prog show [*PROG*] | **bpftool** prog show [*PROG*]
| **bpftool** prog dump xlated *PROG* file *FILE* | **bpftool** prog dump xlated *PROG* [file *FILE*] [opcodes]
| **bpftool** prog dump jited *PROG* [file *FILE*] [opcodes] | **bpftool** prog dump jited *PROG* [file *FILE*] [opcodes]
| **bpftool** prog pin *PROG* *FILE* | **bpftool** prog pin *PROG* *FILE*
| **bpftool** prog help | **bpftool** prog help
...@@ -28,9 +28,12 @@ DESCRIPTION ...@@ -28,9 +28,12 @@ DESCRIPTION
Output will start with program ID followed by program type and Output will start with program ID followed by program type and
zero or more named attributes (depending on kernel version). zero or more named attributes (depending on kernel version).
**bpftool prog dump xlated** *PROG* **file** *FILE* **bpftool prog dump xlated** *PROG* [**file** *FILE*] [**opcodes**]
Dump eBPF instructions of the program from the kernel to a Dump eBPF instructions of the program from the kernel.
file. If *FILE* is specified image will be written to a file,
otherwise it will be disassembled and printed to stdout.
**opcodes** controls if raw opcodes will be printed.
**bpftool prog dump jited** *PROG* [**file** *FILE*] [**opcodes**] **bpftool prog dump jited** *PROG* [**file** *FILE*] [**opcodes**]
Dump jited image (host machine code) of the program. Dump jited image (host machine code) of the program.
......
...@@ -51,7 +51,7 @@ CC = gcc ...@@ -51,7 +51,7 @@ CC = gcc
CFLAGS += -O2 CFLAGS += -O2
CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow
CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/
LIBS = -lelf -lbfd -lopcodes $(LIBBPF) LIBS = -lelf -lbfd -lopcodes $(LIBBPF)
include $(wildcard *.d) include $(wildcard *.d)
...@@ -59,7 +59,10 @@ include $(wildcard *.d) ...@@ -59,7 +59,10 @@ include $(wildcard *.d)
all: $(OUTPUT)bpftool all: $(OUTPUT)bpftool
SRCS=$(wildcard *.c) SRCS=$(wildcard *.c)
OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
$(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF) $(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS) $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
......
...@@ -36,11 +36,12 @@ ...@@ -36,11 +36,12 @@
#ifndef __BPF_TOOL_H #ifndef __BPF_TOOL_H
#define __BPF_TOOL_H #define __BPF_TOOL_H
/* BFD and kernel.h both define GCC_VERSION, differently */
#undef GCC_VERSION
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/kernel.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define err(msg...) fprintf(stderr, "Error: " msg) #define err(msg...) fprintf(stderr, "Error: " msg)
#define warn(msg...) fprintf(stderr, "Warning: " msg) #define warn(msg...) fprintf(stderr, "Warning: " msg)
...@@ -48,11 +49,6 @@ ...@@ -48,11 +49,6 @@
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
#define min(a, b) \
({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _b : _a; })
#define max(a, b) \
({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _b : _a; })
#define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); }) #define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); })
#define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
#define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; }) #define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; })
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
...@@ -46,6 +47,7 @@ ...@@ -46,6 +47,7 @@
#include <bpf.h> #include <bpf.h>
#include "main.h" #include "main.h"
#include "disasm.h"
static const char * const prog_type_name[] = { static const char * const prog_type_name[] = {
[BPF_PROG_TYPE_UNSPEC] = "unspec", [BPF_PROG_TYPE_UNSPEC] = "unspec",
...@@ -297,11 +299,39 @@ static int do_show(int argc, char **argv) ...@@ -297,11 +299,39 @@ static int do_show(int argc, char **argv)
return 0; return 0;
} }
static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
static void dump_xlated(void *buf, unsigned int len, bool opcodes)
{
struct bpf_insn *insn = buf;
unsigned int i;
for (i = 0; i < len / sizeof(*insn); i++) {
printf("% 4d: ", i);
print_bpf_insn(print_insn, NULL, insn + i, true);
if (opcodes) {
printf(" ");
print_hex(insn + i, 8, " ");
printf("\n");
}
if (insn[i].code == (BPF_LD | BPF_IMM | BPF_DW))
i++;
}
}
static int do_dump(int argc, char **argv) static int do_dump(int argc, char **argv)
{ {
struct bpf_prog_info info = {}; struct bpf_prog_info info = {};
__u32 len = sizeof(info); __u32 len = sizeof(info);
bool can_disasm = false;
unsigned int buf_size; unsigned int buf_size;
char *filepath = NULL; char *filepath = NULL;
bool opcodes = false; bool opcodes = false;
...@@ -315,7 +345,6 @@ static int do_dump(int argc, char **argv) ...@@ -315,7 +345,6 @@ static int do_dump(int argc, char **argv)
if (is_prefix(*argv, "jited")) { if (is_prefix(*argv, "jited")) {
member_len = &info.jited_prog_len; member_len = &info.jited_prog_len;
member_ptr = &info.jited_prog_insns; member_ptr = &info.jited_prog_insns;
can_disasm = true;
} else if (is_prefix(*argv, "xlated")) { } else if (is_prefix(*argv, "xlated")) {
member_len = &info.xlated_prog_len; member_len = &info.xlated_prog_len;
member_ptr = &info.xlated_prog_insns; member_ptr = &info.xlated_prog_insns;
...@@ -346,10 +375,6 @@ static int do_dump(int argc, char **argv) ...@@ -346,10 +375,6 @@ static int do_dump(int argc, char **argv)
NEXT_ARG(); NEXT_ARG();
} }
if (!filepath && !can_disasm) {
err("expected 'file' got %s\n", *argv);
return -1;
}
if (argc) { if (argc) {
usage(); usage();
return -1; return -1;
...@@ -409,7 +434,10 @@ static int do_dump(int argc, char **argv) ...@@ -409,7 +434,10 @@ static int do_dump(int argc, char **argv)
goto err_free; goto err_free;
} }
} else { } else {
disasm_print_insn(buf, *member_len, opcodes); if (member_len == &info.jited_prog_len)
disasm_print_insn(buf, *member_len, opcodes);
else
dump_xlated(buf, *member_len, opcodes);
} }
free(buf); free(buf);
...@@ -430,7 +458,7 @@ static int do_help(int argc, char **argv) ...@@ -430,7 +458,7 @@ static int do_help(int argc, char **argv)
{ {
fprintf(stderr, fprintf(stderr,
"Usage: %s %s show [PROG]\n" "Usage: %s %s show [PROG]\n"
" %s %s dump xlated PROG file FILE\n" " %s %s dump xlated PROG [file FILE] [opcodes]\n"
" %s %s dump jited PROG [file FILE] [opcodes]\n" " %s %s dump jited PROG [file FILE] [opcodes]\n"
" %s %s pin PROG FILE\n" " %s %s pin PROG FILE\n"
" %s %s help\n" " %s %s help\n"
......
...@@ -12,7 +12,7 @@ CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../i ...@@ -12,7 +12,7 @@ CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../i
LDLIBS += -lcap -lelf LDLIBS += -lcap -lelf
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
test_align test_align test_verifier_log
TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \
......
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/unistd.h>
#include <bpf/bpf.h>
#define LOG_SIZE (1 << 20)
#define err(str...) printf("ERROR: " str)
static const struct bpf_insn code_sample[] = {
/* We need a few instructions to pass the min log length */
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_map_lookup_elem),
BPF_EXIT_INSN(),
};
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
}
static int load(char *log, size_t log_len, int log_level)
{
union bpf_attr attr;
bzero(&attr, sizeof(attr));
attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
attr.insn_cnt = (__u32)(sizeof(code_sample) / sizeof(struct bpf_insn));
attr.insns = ptr_to_u64(code_sample);
attr.license = ptr_to_u64("GPL");
attr.log_buf = ptr_to_u64(log);
attr.log_size = log_len;
attr.log_level = log_level;
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}
static void check_ret(int ret, int exp_errno)
{
if (ret > 0) {
close(ret);
err("broken sample loaded successfully!?\n");
exit(1);
}
if (!ret || errno != exp_errno) {
err("Program load returned: ret:%d/errno:%d, expected ret:%d/errno:%d\n",
ret, errno, -1, exp_errno);
exit(1);
}
}
static void check_ones(const char *buf, size_t len, const char *msg)
{
while (len--)
if (buf[len] != 1) {
err("%s", msg);
exit(1);
}
}
static void test_log_good(char *log, size_t buf_len, size_t log_len,
size_t exp_len, int exp_errno, const char *full_log)
{
size_t len;
int ret;
memset(log, 1, buf_len);
ret = load(log, log_len, 1);
check_ret(ret, exp_errno);
len = strnlen(log, buf_len);
if (len == buf_len) {
err("verifier did not NULL terminate the log\n");
exit(1);
}
if (exp_len && len != exp_len) {
err("incorrect log length expected:%zd have:%zd\n",
exp_len, len);
exit(1);
}
if (strchr(log, 1)) {
err("verifier leaked a byte through\n");
exit(1);
}
check_ones(log + len + 1, buf_len - len - 1,
"verifier wrote bytes past NULL termination\n");
if (memcmp(full_log, log, LOG_SIZE)) {
err("log did not match expected output\n");
exit(1);
}
}
static void test_log_bad(char *log, size_t log_len, int log_level)
{
int ret;
ret = load(log, log_len, log_level);
check_ret(ret, EINVAL);
if (log)
check_ones(log, LOG_SIZE,
"verifier touched log with bad parameters\n");
}
int main(int argc, char **argv)
{
char full_log[LOG_SIZE];
char log[LOG_SIZE];
size_t want_len;
int i;
memset(log, 1, LOG_SIZE);
/* Test incorrect attr */
printf("Test log_level 0...\n");
test_log_bad(log, LOG_SIZE, 0);
printf("Test log_size < 128...\n");
test_log_bad(log, 15, 1);
printf("Test log_buff = NULL...\n");
test_log_bad(NULL, LOG_SIZE, 1);
/* Test with log big enough */
printf("Test oversized buffer...\n");
test_log_good(full_log, LOG_SIZE, LOG_SIZE, 0, EACCES, full_log);
want_len = strlen(full_log);
printf("Test exact buffer...\n");
test_log_good(log, LOG_SIZE, want_len + 2, want_len, EACCES, full_log);
printf("Test undersized buffers...\n");
for (i = 0; i < 64; i++) {
full_log[want_len - i + 1] = 1;
full_log[want_len - i] = 0;
test_log_good(log, LOG_SIZE, want_len + 1 - i, want_len - i,
ENOSPC, full_log);
}
printf("test_verifier_log: OK\n");
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment