Skip to content
This repository has been archived by the owner on Jun 18, 2024. It is now read-only.

Commit

Permalink
Merge branch 'veristat: add better support of freplace programs'
Browse files Browse the repository at this point in the history
Andrii Nakryiko says:

====================

Teach veristat how to deal with freplace BPF programs. As they can't be
directly loaded by veristat without custom user-space part that sets correct
target program FD, veristat always fails freplace programs. This patch set
teaches veristat to guess target program type that will be inherited by
freplace program itself, and subtitute it for BPF_PROG_TYPE_EXT (freplace) one
for the purposes of BPF verification.

Patch #1 fixes bug in libbpf preventing overriding freplace with specific
program type.

Patch #2 adds convenient -d flag to request veristat to emit libbpf debug
logs. It help debugging why a specific BPF program fails to load, if the
problem is not due to BPF verification itself.

v3->v4:
  - fix optional kern_name check when guessing prog type (Alexei);
v2->v3:
  - fix bpf_obj_id selftest that uses legacy bpf_prog_test_load() helper,
    which always sets program type programmatically; teach the helper to do it
    only if actually necessary (Stanislav);
v1->v2:
  - fix compilation error reported by old GCC (my GCC v11 doesn't produce even
    a warning) and Clang (see CI failure at [0]):

GCC version:

  veristat.c: In function ‘fixup_obj’:
  veristat.c:908:1: error: label at end of compound statement
    908 | skip_freplace_fixup:
        | ^~~~~~~~~~~~~~~~~~~

Clang version:

  veristat.c:909:1: error: label at end of compound statement is a C2x extension [-Werror,-Wc2x-extensions]
  }
  ^
  1 error generated.

  [0] https://github.com/kernel-patches/bpf/actions/runs/4515972059/jobs/7953845335
====================

Acked-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Alexei Starovoitov committed Mar 30, 2023
2 parents 8b52cc2 + fa7cc90 commit 8a9abe0
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 6 deletions.
1 change: 1 addition & 0 deletions tools/lib/bpf/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -8468,6 +8468,7 @@ int bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
return libbpf_err(-EBUSY);

prog->type = type;
prog->sec_def = NULL;
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion tools/testing/selftests/bpf/testing_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ int bpf_prog_test_load(const char *file, enum bpf_prog_type type,
goto err_out;
}

if (type != BPF_PROG_TYPE_UNSPEC)
if (type != BPF_PROG_TYPE_UNSPEC && bpf_program__type(prog) != type)
bpf_program__set_type(prog, type);

flags = bpf_program__flags(prog) | BPF_F_TEST_RND_HI32;
Expand Down
129 changes: 124 additions & 5 deletions tools/testing/selftests/bpf/veristat.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <sys/sysinfo.h>
#include <sys/stat.h>
#include <bpf/libbpf.h>
#include <bpf/btf.h>
#include <libelf.h>
#include <gelf.h>
#include <float.h>
Expand Down Expand Up @@ -135,6 +136,7 @@ static struct env {
char **filenames;
int filename_cnt;
bool verbose;
bool debug;
bool quiet;
int log_level;
enum resfmt out_fmt;
Expand Down Expand Up @@ -169,7 +171,7 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va
{
if (!env.verbose)
return 0;
if (level == LIBBPF_DEBUG /* && !env.verbose */)
if (level == LIBBPF_DEBUG && !env.debug)
return 0;
return vfprintf(stderr, format, args);
}
Expand All @@ -186,6 +188,7 @@ static const struct argp_option opts[] = {
{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
{ "verbose", 'v', NULL, 0, "Verbose mode" },
{ "log-level", 'l', "LEVEL", 0, "Verifier log level (default 0 for normal mode, 1 for verbose mode)" },
{ "debug", 'd', NULL, 0, "Debug mode (turns on libbpf debug logging)" },
{ "quiet", 'q', NULL, 0, "Quiet mode" },
{ "emit", 'e', "SPEC", 0, "Specify stats to be emitted" },
{ "sort", 's', "SPEC", 0, "Specify sort order" },
Expand All @@ -212,6 +215,10 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
case 'v':
env.verbose = true;
break;
case 'd':
env.debug = true;
env.verbose = true;
break;
case 'q':
env.quiet = true;
break;
Expand Down Expand Up @@ -772,7 +779,62 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
return 0;
}

static void fixup_obj(struct bpf_object *obj)
static int guess_prog_type_by_ctx_name(const char *ctx_name,
enum bpf_prog_type *prog_type,
enum bpf_attach_type *attach_type)
{
/* We need to guess program type based on its declared context type.
* This guess can't be perfect as many different program types might
* share the same context type. So we can only hope to reasonably
* well guess this and get lucky.
*
* Just in case, we support both UAPI-side type names and
* kernel-internal names.
*/
static struct {
const char *uapi_name;
const char *kern_name;
enum bpf_prog_type prog_type;
enum bpf_attach_type attach_type;
} ctx_map[] = {
/* __sk_buff is most ambiguous, for now we assume cgroup_skb */
{ "__sk_buff", "sk_buff", BPF_PROG_TYPE_CGROUP_SKB, BPF_CGROUP_INET_INGRESS },
{ "bpf_sock", "sock", BPF_PROG_TYPE_CGROUP_SOCK, BPF_CGROUP_INET4_POST_BIND },
{ "bpf_sock_addr", "bpf_sock_addr_kern", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_BIND },
{ "bpf_sock_ops", "bpf_sock_ops_kern", BPF_PROG_TYPE_SOCK_OPS, BPF_CGROUP_SOCK_OPS },
{ "sk_msg_md", "sk_msg", BPF_PROG_TYPE_SK_MSG, BPF_SK_MSG_VERDICT },
{ "bpf_cgroup_dev_ctx", "bpf_cgroup_dev_ctx", BPF_PROG_TYPE_CGROUP_DEVICE, BPF_CGROUP_DEVICE },
{ "bpf_sysctl", "bpf_sysctl_kern", BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_CGROUP_SYSCTL },
{ "bpf_sockopt", "bpf_sockopt_kern", BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT },
{ "sk_reuseport_md", "sk_reuseport_kern", BPF_PROG_TYPE_SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE },
{ "bpf_sk_lookup", "bpf_sk_lookup_kern", BPF_PROG_TYPE_SK_LOOKUP, BPF_SK_LOOKUP },
{ "xdp_md", "xdp_buff", BPF_PROG_TYPE_XDP, BPF_XDP },
/* tracing types with no expected attach type */
{ "bpf_user_pt_regs_t", "pt_regs", BPF_PROG_TYPE_KPROBE },
{ "bpf_perf_event_data", "bpf_perf_event_data_kern", BPF_PROG_TYPE_PERF_EVENT },
/* raw_tp programs use u64[] from kernel side, we don't want
* to match on that, probably; so NULL for kern-side type
*/
{ "bpf_raw_tracepoint_args", NULL, BPF_PROG_TYPE_RAW_TRACEPOINT },
};
int i;

if (!ctx_name)
return -EINVAL;

for (i = 0; i < ARRAY_SIZE(ctx_map); i++) {
if (strcmp(ctx_map[i].uapi_name, ctx_name) == 0 ||
(ctx_map[i].kern_name && strcmp(ctx_map[i].kern_name, ctx_name) == 0)) {
*prog_type = ctx_map[i].prog_type;
*attach_type = ctx_map[i].attach_type;
return 0;
}
}

return -ESRCH;
}

static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const char *filename)
{
struct bpf_map *map;

Expand All @@ -792,18 +854,75 @@ static void fixup_obj(struct bpf_object *obj)
bpf_map__set_max_entries(map, 1);
}
}

/* SEC(freplace) programs can't be loaded with veristat as is,
* but we can try guessing their target program's expected type by
* looking at the type of program's first argument and substituting
* corresponding program type
*/
if (bpf_program__type(prog) == BPF_PROG_TYPE_EXT) {
const struct btf *btf = bpf_object__btf(obj);
const char *prog_name = bpf_program__name(prog);
enum bpf_prog_type prog_type;
enum bpf_attach_type attach_type;
const struct btf_type *t;
const char *ctx_name;
int id;

if (!btf)
goto skip_freplace_fixup;

id = btf__find_by_name_kind(btf, prog_name, BTF_KIND_FUNC);
t = btf__type_by_id(btf, id);
t = btf__type_by_id(btf, t->type);
if (!btf_is_func_proto(t) || btf_vlen(t) != 1)
goto skip_freplace_fixup;

/* context argument is a pointer to a struct/typedef */
t = btf__type_by_id(btf, btf_params(t)[0].type);
while (t && btf_is_mod(t))
t = btf__type_by_id(btf, t->type);
if (!t || !btf_is_ptr(t))
goto skip_freplace_fixup;
t = btf__type_by_id(btf, t->type);
while (t && btf_is_mod(t))
t = btf__type_by_id(btf, t->type);
if (!t)
goto skip_freplace_fixup;

ctx_name = btf__name_by_offset(btf, t->name_off);

if (guess_prog_type_by_ctx_name(ctx_name, &prog_type, &attach_type) == 0) {
bpf_program__set_type(prog, prog_type);
bpf_program__set_expected_attach_type(prog, attach_type);

if (!env.quiet) {
printf("Using guessed program type '%s' for %s/%s...\n",
libbpf_bpf_prog_type_str(prog_type),
filename, prog_name);
}
} else {
if (!env.quiet) {
printf("Failed to guess program type for freplace program with context type name '%s' for %s/%s. Consider using canonical type names to help veristat...\n",
ctx_name, filename, prog_name);
}
}
}
skip_freplace_fixup:
return;
}

static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog)
{
const char *prog_name = bpf_program__name(prog);
const char *base_filename = basename(filename);
size_t buf_sz = sizeof(verif_log_buf);
char *buf = verif_log_buf;
struct verif_stats *stats;
int err = 0;
void *tmp;

if (!should_process_file_prog(basename(filename), bpf_program__name(prog))) {
if (!should_process_file_prog(base_filename, bpf_program__name(prog))) {
env.progs_skipped++;
return 0;
}
Expand All @@ -829,12 +948,12 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
verif_log_buf[0] = '\0';

/* increase chances of successful BPF object loading */
fixup_obj(obj);
fixup_obj(obj, prog, base_filename);

err = bpf_object__load(obj);
env.progs_processed++;

stats->file_name = strdup(basename(filename));
stats->file_name = strdup(base_filename);
stats->prog_name = strdup(bpf_program__name(prog));
stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
parse_verif_log(buf, buf_sz, stats);
Expand Down

0 comments on commit 8a9abe0

Please sign in to comment.