Skip to content

Commit 8a9abe0

Browse files
author
Alexei Starovoitov
committed
Merge branch 'veristat: add better support of freplace programs'
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>
2 parents 8b52cc2 + fa7cc90 commit 8a9abe0

3 files changed

Lines changed: 126 additions & 6 deletions

File tree

tools/lib/bpf/libbpf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8468,6 +8468,7 @@ int bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
84688468
return libbpf_err(-EBUSY);
84698469

84708470
prog->type = type;
8471+
prog->sec_def = NULL;
84718472
return 0;
84728473
}
84738474

tools/testing/selftests/bpf/testing_helpers.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ int bpf_prog_test_load(const char *file, enum bpf_prog_type type,
195195
goto err_out;
196196
}
197197

198-
if (type != BPF_PROG_TYPE_UNSPEC)
198+
if (type != BPF_PROG_TYPE_UNSPEC && bpf_program__type(prog) != type)
199199
bpf_program__set_type(prog, type);
200200

201201
flags = bpf_program__flags(prog) | BPF_F_TEST_RND_HI32;

tools/testing/selftests/bpf/veristat.c

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <sys/sysinfo.h>
1616
#include <sys/stat.h>
1717
#include <bpf/libbpf.h>
18+
#include <bpf/btf.h>
1819
#include <libelf.h>
1920
#include <gelf.h>
2021
#include <float.h>
@@ -135,6 +136,7 @@ static struct env {
135136
char **filenames;
136137
int filename_cnt;
137138
bool verbose;
139+
bool debug;
138140
bool quiet;
139141
int log_level;
140142
enum resfmt out_fmt;
@@ -169,7 +171,7 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va
169171
{
170172
if (!env.verbose)
171173
return 0;
172-
if (level == LIBBPF_DEBUG /* && !env.verbose */)
174+
if (level == LIBBPF_DEBUG && !env.debug)
173175
return 0;
174176
return vfprintf(stderr, format, args);
175177
}
@@ -186,6 +188,7 @@ static const struct argp_option opts[] = {
186188
{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
187189
{ "verbose", 'v', NULL, 0, "Verbose mode" },
188190
{ "log-level", 'l', "LEVEL", 0, "Verifier log level (default 0 for normal mode, 1 for verbose mode)" },
191+
{ "debug", 'd', NULL, 0, "Debug mode (turns on libbpf debug logging)" },
189192
{ "quiet", 'q', NULL, 0, "Quiet mode" },
190193
{ "emit", 'e', "SPEC", 0, "Specify stats to be emitted" },
191194
{ "sort", 's', "SPEC", 0, "Specify sort order" },
@@ -212,6 +215,10 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
212215
case 'v':
213216
env.verbose = true;
214217
break;
218+
case 'd':
219+
env.debug = true;
220+
env.verbose = true;
221+
break;
215222
case 'q':
216223
env.quiet = true;
217224
break;
@@ -772,7 +779,62 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
772779
return 0;
773780
}
774781

775-
static void fixup_obj(struct bpf_object *obj)
782+
static int guess_prog_type_by_ctx_name(const char *ctx_name,
783+
enum bpf_prog_type *prog_type,
784+
enum bpf_attach_type *attach_type)
785+
{
786+
/* We need to guess program type based on its declared context type.
787+
* This guess can't be perfect as many different program types might
788+
* share the same context type. So we can only hope to reasonably
789+
* well guess this and get lucky.
790+
*
791+
* Just in case, we support both UAPI-side type names and
792+
* kernel-internal names.
793+
*/
794+
static struct {
795+
const char *uapi_name;
796+
const char *kern_name;
797+
enum bpf_prog_type prog_type;
798+
enum bpf_attach_type attach_type;
799+
} ctx_map[] = {
800+
/* __sk_buff is most ambiguous, for now we assume cgroup_skb */
801+
{ "__sk_buff", "sk_buff", BPF_PROG_TYPE_CGROUP_SKB, BPF_CGROUP_INET_INGRESS },
802+
{ "bpf_sock", "sock", BPF_PROG_TYPE_CGROUP_SOCK, BPF_CGROUP_INET4_POST_BIND },
803+
{ "bpf_sock_addr", "bpf_sock_addr_kern", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_BIND },
804+
{ "bpf_sock_ops", "bpf_sock_ops_kern", BPF_PROG_TYPE_SOCK_OPS, BPF_CGROUP_SOCK_OPS },
805+
{ "sk_msg_md", "sk_msg", BPF_PROG_TYPE_SK_MSG, BPF_SK_MSG_VERDICT },
806+
{ "bpf_cgroup_dev_ctx", "bpf_cgroup_dev_ctx", BPF_PROG_TYPE_CGROUP_DEVICE, BPF_CGROUP_DEVICE },
807+
{ "bpf_sysctl", "bpf_sysctl_kern", BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_CGROUP_SYSCTL },
808+
{ "bpf_sockopt", "bpf_sockopt_kern", BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT },
809+
{ "sk_reuseport_md", "sk_reuseport_kern", BPF_PROG_TYPE_SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE },
810+
{ "bpf_sk_lookup", "bpf_sk_lookup_kern", BPF_PROG_TYPE_SK_LOOKUP, BPF_SK_LOOKUP },
811+
{ "xdp_md", "xdp_buff", BPF_PROG_TYPE_XDP, BPF_XDP },
812+
/* tracing types with no expected attach type */
813+
{ "bpf_user_pt_regs_t", "pt_regs", BPF_PROG_TYPE_KPROBE },
814+
{ "bpf_perf_event_data", "bpf_perf_event_data_kern", BPF_PROG_TYPE_PERF_EVENT },
815+
/* raw_tp programs use u64[] from kernel side, we don't want
816+
* to match on that, probably; so NULL for kern-side type
817+
*/
818+
{ "bpf_raw_tracepoint_args", NULL, BPF_PROG_TYPE_RAW_TRACEPOINT },
819+
};
820+
int i;
821+
822+
if (!ctx_name)
823+
return -EINVAL;
824+
825+
for (i = 0; i < ARRAY_SIZE(ctx_map); i++) {
826+
if (strcmp(ctx_map[i].uapi_name, ctx_name) == 0 ||
827+
(ctx_map[i].kern_name && strcmp(ctx_map[i].kern_name, ctx_name) == 0)) {
828+
*prog_type = ctx_map[i].prog_type;
829+
*attach_type = ctx_map[i].attach_type;
830+
return 0;
831+
}
832+
}
833+
834+
return -ESRCH;
835+
}
836+
837+
static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const char *filename)
776838
{
777839
struct bpf_map *map;
778840

@@ -792,18 +854,75 @@ static void fixup_obj(struct bpf_object *obj)
792854
bpf_map__set_max_entries(map, 1);
793855
}
794856
}
857+
858+
/* SEC(freplace) programs can't be loaded with veristat as is,
859+
* but we can try guessing their target program's expected type by
860+
* looking at the type of program's first argument and substituting
861+
* corresponding program type
862+
*/
863+
if (bpf_program__type(prog) == BPF_PROG_TYPE_EXT) {
864+
const struct btf *btf = bpf_object__btf(obj);
865+
const char *prog_name = bpf_program__name(prog);
866+
enum bpf_prog_type prog_type;
867+
enum bpf_attach_type attach_type;
868+
const struct btf_type *t;
869+
const char *ctx_name;
870+
int id;
871+
872+
if (!btf)
873+
goto skip_freplace_fixup;
874+
875+
id = btf__find_by_name_kind(btf, prog_name, BTF_KIND_FUNC);
876+
t = btf__type_by_id(btf, id);
877+
t = btf__type_by_id(btf, t->type);
878+
if (!btf_is_func_proto(t) || btf_vlen(t) != 1)
879+
goto skip_freplace_fixup;
880+
881+
/* context argument is a pointer to a struct/typedef */
882+
t = btf__type_by_id(btf, btf_params(t)[0].type);
883+
while (t && btf_is_mod(t))
884+
t = btf__type_by_id(btf, t->type);
885+
if (!t || !btf_is_ptr(t))
886+
goto skip_freplace_fixup;
887+
t = btf__type_by_id(btf, t->type);
888+
while (t && btf_is_mod(t))
889+
t = btf__type_by_id(btf, t->type);
890+
if (!t)
891+
goto skip_freplace_fixup;
892+
893+
ctx_name = btf__name_by_offset(btf, t->name_off);
894+
895+
if (guess_prog_type_by_ctx_name(ctx_name, &prog_type, &attach_type) == 0) {
896+
bpf_program__set_type(prog, prog_type);
897+
bpf_program__set_expected_attach_type(prog, attach_type);
898+
899+
if (!env.quiet) {
900+
printf("Using guessed program type '%s' for %s/%s...\n",
901+
libbpf_bpf_prog_type_str(prog_type),
902+
filename, prog_name);
903+
}
904+
} else {
905+
if (!env.quiet) {
906+
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",
907+
ctx_name, filename, prog_name);
908+
}
909+
}
910+
}
911+
skip_freplace_fixup:
912+
return;
795913
}
796914

797915
static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog)
798916
{
799917
const char *prog_name = bpf_program__name(prog);
918+
const char *base_filename = basename(filename);
800919
size_t buf_sz = sizeof(verif_log_buf);
801920
char *buf = verif_log_buf;
802921
struct verif_stats *stats;
803922
int err = 0;
804923
void *tmp;
805924

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

831950
/* increase chances of successful BPF object loading */
832-
fixup_obj(obj);
951+
fixup_obj(obj, prog, base_filename);
833952

834953
err = bpf_object__load(obj);
835954
env.progs_processed++;
836955

837-
stats->file_name = strdup(basename(filename));
956+
stats->file_name = strdup(base_filename);
838957
stats->prog_name = strdup(bpf_program__name(prog));
839958
stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
840959
parse_verif_log(buf, buf_sz, stats);

0 commit comments

Comments
 (0)