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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-10-10 12:30:17 -07:00
Родитель 442d713baa a2a7d57010
Коммит 67174bb233
14 изменённых файлов: 776 добавлений и 459 удалений

Просмотреть файл

@ -115,6 +115,21 @@ struct bpf_insn_aux_data {
#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_ext_analyzer_ops {
int (*insn_hook)(struct bpf_verifier_env *env,
@ -139,6 +154,8 @@ struct bpf_verifier_env {
bool allow_ptr_leaks;
bool seen_direct_write;
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,

Просмотреть файл

@ -87,6 +87,7 @@ static inline int skb_metadata_dst_cmp(const struct sk_buff *skb_a,
void metadata_dst_free(struct metadata_dst *);
struct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type,
gfp_t flags);
void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst);
struct metadata_dst __percpu *
metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags);

Просмотреть файл

@ -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) += 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)
obj-$(CONFIG_BPF_SYSCALL) += devmap.o
ifeq ($(CONFIG_STREAM_PARSER),y)

214
kernel/bpf/disasm.c Normal file
Просмотреть файл

@ -0,0 +1,214 @@
/* 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]);
}
}

32
kernel/bpf/disasm.h Normal file
Просмотреть файл

@ -0,0 +1,32 @@
/* 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

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -322,3 +322,19 @@ metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags)
return md_dst;
}
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 @@
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <asm/cmpxchg.h>
#include <linux/filter.h>
#include <linux/ratelimit.h>
#include <linux/seccomp.h>
@ -2987,14 +2988,15 @@ static const struct bpf_func_proto *
bpf_get_skb_set_tunnel_proto(enum bpf_func_id which)
{
if (!md_dst) {
/* Race is not possible, since it's called from verifier
* that is holding verifier mutex.
*/
md_dst = metadata_dst_alloc_percpu(IP_TUNNEL_OPTS_MAX,
METADATA_IP_TUNNEL,
GFP_KERNEL);
if (!md_dst)
struct metadata_dst __percpu *tmp;
tmp = metadata_dst_alloc_percpu(IP_TUNNEL_OPTS_MAX,
METADATA_IP_TUNNEL,
GFP_KERNEL);
if (!tmp)
return NULL;
if (cmpxchg(&md_dst, NULL, tmp))
metadata_dst_free_percpu(tmp);
}
switch (which) {

Просмотреть файл

@ -11,7 +11,7 @@ SYNOPSIS
========
| **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 pin *PROG* *FILE*
| **bpftool** prog help
@ -28,9 +28,12 @@ DESCRIPTION
Output will start with program ID followed by program type and
zero or more named attributes (depending on kernel version).
**bpftool prog dump xlated** *PROG* **file** *FILE*
Dump eBPF instructions of the program from the kernel to a
file.
**bpftool prog dump xlated** *PROG* [**file** *FILE*] [**opcodes**]
Dump eBPF instructions of the program from the kernel.
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**]
Dump jited image (host machine code) of the program.

Просмотреть файл

@ -51,7 +51,7 @@ CC = gcc
CFLAGS += -O2
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)
include $(wildcard *.d)
@ -59,7 +59,10 @@ include $(wildcard *.d)
all: $(OUTPUT)bpftool
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)
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS)

Просмотреть файл

@ -36,11 +36,12 @@
#ifndef __BPF_TOOL_H
#define __BPF_TOOL_H
/* BFD and kernel.h both define GCC_VERSION, differently */
#undef GCC_VERSION
#include <stdbool.h>
#include <stdio.h>
#include <linux/bpf.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#include <linux/kernel.h>
#define err(msg...) fprintf(stderr, "Error: " msg)
#define warn(msg...) fprintf(stderr, "Warning: " msg)
@ -48,11 +49,6 @@
#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_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
#define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; })

Просмотреть файл

@ -35,6 +35,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -46,6 +47,7 @@
#include <bpf.h>
#include "main.h"
#include "disasm.h"
static const char * const prog_type_name[] = {
[BPF_PROG_TYPE_UNSPEC] = "unspec",
@ -297,11 +299,39 @@ static int do_show(int argc, char **argv)
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)
{
struct bpf_prog_info info = {};
__u32 len = sizeof(info);
bool can_disasm = false;
unsigned int buf_size;
char *filepath = NULL;
bool opcodes = false;
@ -315,7 +345,6 @@ static int do_dump(int argc, char **argv)
if (is_prefix(*argv, "jited")) {
member_len = &info.jited_prog_len;
member_ptr = &info.jited_prog_insns;
can_disasm = true;
} else if (is_prefix(*argv, "xlated")) {
member_len = &info.xlated_prog_len;
member_ptr = &info.xlated_prog_insns;
@ -346,10 +375,6 @@ static int do_dump(int argc, char **argv)
NEXT_ARG();
}
if (!filepath && !can_disasm) {
err("expected 'file' got %s\n", *argv);
return -1;
}
if (argc) {
usage();
return -1;
@ -409,7 +434,10 @@ static int do_dump(int argc, char **argv)
goto err_free;
}
} 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);
@ -430,7 +458,7 @@ static int do_help(int argc, char **argv)
{
fprintf(stderr,
"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 pin PROG FILE\n"
" %s %s help\n"

Просмотреть файл

@ -12,7 +12,7 @@ CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../i
LDLIBS += -lcap -lelf
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_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \

Просмотреть файл

@ -0,0 +1,171 @@
#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;
}