MSan runs into a few false positives in libbpf. They all come from the fact that MSan does not know anything about the bpf syscall, particularly, what it writes to. Add libbpf_mark_defined() function to mark memory modified by the bpf syscall. Use the abstract name (it could be e.g. libbpf_msan_unpoison()), because it can be used for valgrind in the future as well. Signed-off-by: Ilya Leoshkevich <iii@xxxxxxxxxxxxx> --- tools/lib/bpf/bpf.c | 70 +++++++++++++++++++++++++++++++-- tools/lib/bpf/btf.c | 1 + tools/lib/bpf/libbpf.c | 1 + tools/lib/bpf/libbpf_internal.h | 14 +++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 9aff98f42a3d..12fdc4ce1780 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -92,6 +92,10 @@ int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size, int attempts) fd = sys_bpf_fd(BPF_PROG_LOAD, attr, size); } while (fd < 0 && errno == EAGAIN && --attempts > 0); + if (attr->log_buf) + libbpf_mark_defined((void *)(unsigned long)attr->log_buf, + attr->log_size); + return fd; } @@ -395,6 +399,33 @@ int bpf_map_update_elem(int fd, const void *key, const void *value, return libbpf_err_errno(ret); } +static void mark_map_value_defined(int fd, void *value) +{ +#ifdef HAVE_LIBBPF_MARK_DEFINED + struct bpf_map_info info; + size_t size = 0; + __u32 info_len; + int num_cpus; + int err; + + info_len = sizeof(info); + err = bpf_obj_get_info_by_fd(fd, &info, &info_len); + if (!err) { + size = info.value_size; + if (info.type == BPF_MAP_TYPE_PERCPU_ARRAY || + info.type == BPF_MAP_TYPE_PERCPU_HASH || + info.type == BPF_MAP_TYPE_LRU_PERCPU_HASH || + info.type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) { + num_cpus = libbpf_num_possible_cpus(); + if (num_cpus > 0) + size *= num_cpus; + } + } + if (size) + libbpf_mark_defined(value, size); +#endif +} + int bpf_map_lookup_elem(int fd, const void *key, void *value) { const size_t attr_sz = offsetofend(union bpf_attr, flags); @@ -407,6 +438,8 @@ int bpf_map_lookup_elem(int fd, const void *key, void *value) attr.value = ptr_to_u64(value); ret = sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, attr_sz); + if (!ret) + mark_map_value_defined(fd, value); return libbpf_err_errno(ret); } @@ -423,6 +456,8 @@ int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, __u64 flags) attr.flags = flags; ret = sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, attr_sz); + if (!ret) + mark_map_value_defined(fd, value); return libbpf_err_errno(ret); } @@ -438,6 +473,8 @@ int bpf_map_lookup_and_delete_elem(int fd, const void *key, void *value) attr.value = ptr_to_u64(value); ret = sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, attr_sz); + if (!ret) + mark_map_value_defined(fd, value); return libbpf_err_errno(ret); } @@ -454,6 +491,8 @@ int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key, void *value, _ attr.flags = flags; ret = sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, attr_sz); + if (!ret) + mark_map_value_defined(fd, value); return libbpf_err_errno(ret); } @@ -823,10 +862,12 @@ int bpf_prog_query_opts(int target_fd, { const size_t attr_sz = offsetofend(union bpf_attr, query); union bpf_attr attr; + __u32 *prog_ids; int ret; if (!OPTS_VALID(opts, bpf_prog_query_opts)) return libbpf_err(-EINVAL); + prog_ids = OPTS_GET(opts, prog_ids, NULL); memset(&attr, 0, attr_sz); @@ -834,11 +875,15 @@ int bpf_prog_query_opts(int target_fd, attr.query.attach_type = type; attr.query.query_flags = OPTS_GET(opts, query_flags, 0); attr.query.prog_cnt = OPTS_GET(opts, prog_cnt, 0); - attr.query.prog_ids = ptr_to_u64(OPTS_GET(opts, prog_ids, NULL)); + attr.query.prog_ids = ptr_to_u64(prog_ids); attr.query.prog_attach_flags = ptr_to_u64(OPTS_GET(opts, prog_attach_flags, NULL)); ret = sys_bpf(BPF_PROG_QUERY, &attr, attr_sz); + if (!ret && prog_ids) + libbpf_mark_defined(prog_ids, + attr.query.prog_cnt * sizeof(*prog_ids)); + OPTS_SET(opts, attach_flags, attr.query.attach_flags); OPTS_SET(opts, prog_cnt, attr.query.prog_cnt); @@ -868,10 +913,14 @@ int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts) { const size_t attr_sz = offsetofend(union bpf_attr, test); union bpf_attr attr; + void *data_out; + void *ctx_out; int ret; if (!OPTS_VALID(opts, bpf_test_run_opts)) return libbpf_err(-EINVAL); + data_out = OPTS_GET(opts, data_out, NULL); + ctx_out = OPTS_GET(opts, ctx_out, NULL); memset(&attr, 0, attr_sz); attr.test.prog_fd = prog_fd; @@ -885,12 +934,19 @@ int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts) attr.test.data_size_in = OPTS_GET(opts, data_size_in, 0); attr.test.data_size_out = OPTS_GET(opts, data_size_out, 0); attr.test.ctx_in = ptr_to_u64(OPTS_GET(opts, ctx_in, NULL)); - attr.test.ctx_out = ptr_to_u64(OPTS_GET(opts, ctx_out, NULL)); + attr.test.ctx_out = ptr_to_u64(ctx_out); attr.test.data_in = ptr_to_u64(OPTS_GET(opts, data_in, NULL)); - attr.test.data_out = ptr_to_u64(OPTS_GET(opts, data_out, NULL)); + attr.test.data_out = ptr_to_u64(data_out); ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, attr_sz); + if (!ret) { + if (data_out) + libbpf_mark_defined(data_out, attr.test.data_size_out); + if (ctx_out) + libbpf_mark_defined(ctx_out, attr.test.ctx_size_out); + } + OPTS_SET(opts, data_size_out, attr.test.data_size_out); OPTS_SET(opts, ctx_size_out, attr.test.ctx_size_out); OPTS_SET(opts, duration, attr.test.duration); @@ -1039,8 +1095,10 @@ int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len) attr.info.info = ptr_to_u64(info); err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, attr_sz); - if (!err) + if (!err) { *info_len = attr.info.info_len; + libbpf_mark_defined(info, attr.info.info_len); + } return libbpf_err_errno(err); } @@ -1103,6 +1161,8 @@ int bpf_btf_load(const void *btf_data, size_t btf_size, const struct bpf_btf_loa attr.btf_log_level = 1; fd = sys_bpf_fd(BPF_BTF_LOAD, &attr, attr_sz); } + if (log_buf) + libbpf_mark_defined(log_buf, attr.btf_log_size); return libbpf_err_errno(fd); } @@ -1122,6 +1182,8 @@ int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len, attr.task_fd_query.buf_len = *buf_len; err = sys_bpf(BPF_TASK_FD_QUERY, &attr, attr_sz); + if (!err && buf) + libbpf_mark_defined(buf, attr.task_fd_query.buf_len + 1); *buf_len = attr.task_fd_query.buf_len; *prog_id = attr.task_fd_query.prog_id; diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 64841117fbb2..24a957604a97 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -1388,6 +1388,7 @@ struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf) goto exit_free; } + libbpf_mark_defined(ptr, btf_info.btf_size); btf = btf_new(ptr, btf_info.btf_size, base_btf); exit_free: diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 4191a78b2815..3e32ae5f0cce 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -5443,6 +5443,7 @@ static int load_module_btfs(struct bpf_object *obj) pr_warn("failed to get BTF object #%d info: %d\n", id, err); goto err_out; } + libbpf_mark_defined(name, info.name_len + 1); /* ignore non-module BTFs */ if (!info.kernel_btf || strcmp(name, "vmlinux") == 0) { diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index fbaf68335394..4e4622f66fdf 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -577,4 +577,18 @@ static inline bool is_pow_of_2(size_t x) #define PROG_LOAD_ATTEMPTS 5 int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size, int attempts); +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#define LIBBPF_MSAN +#endif +#endif + +#ifdef LIBBPF_MSAN +#define HAVE_LIBBPF_MARK_DEFINED +#include <sanitizer/msan_interface.h> +#define libbpf_mark_defined __msan_unpoison +#else +static inline void libbpf_mark_defined(void *s, size_t n) {} +#endif + #endif /* __LIBBPF_LIBBPF_INTERNAL_H */ -- 2.39.1