* [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD
@ 2024-11-15 0:46 Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 1/5] bpf: add a __btf_get_by_fd helper Anton Protopopov
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: Anton Protopopov @ 2024-11-15 0:46 UTC (permalink / raw)
To: bpf; +Cc: Anton Protopopov
Add a new attribute to the bpf(BPF_PROG_LOAD) system call. If this
new attribute is non-zero, then the fd_array is considered to be a
continuous array of the fd_array_cnt length and to contain only
proper map file descriptors, or btf file descriptors, or zeroes.
This change allows maps, which aren't referenced directly by a BPF
program, to be bound to the program _and_ also to be present during
the program verification (so BPF_PROG_BIND_MAP is not enough for this
use case).
The primary reason for this change is that it is a prerequisite for
adding "instruction set" maps, which are both non-referenced by the
program and must be present during the program verification.
The first three commits add the new functionality, the fourth adds
corresponding self-tests, and the last one is a small additional fix.
Anton Protopopov (5):
bpf: add a __btf_get_by_fd helper
bpf: move map/prog compatibility checks
bpf: add fd_array_cnt attribute for prog_load
selftests/bpf: Add tests for fd_array_cnt
bpf: fix potential error return
include/linux/btf.h | 13 +
include/uapi/linux/bpf.h | 10 +
kernel/bpf/btf.c | 13 +-
kernel/bpf/core.c | 9 +-
kernel/bpf/syscall.c | 2 +-
kernel/bpf/verifier.c | 204 ++++++----
tools/include/uapi/linux/bpf.h | 10 +
.../selftests/bpf/prog_tests/fd_array.c | 374 ++++++++++++++++++
8 files changed, 557 insertions(+), 78 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/fd_array.c
--
2.34.1
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH bpf-next 1/5] bpf: add a __btf_get_by_fd helper
2024-11-15 0:46 [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Anton Protopopov
@ 2024-11-15 0:46 ` Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 2/5] bpf: move map/prog compatibility checks Anton Protopopov
` (4 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Anton Protopopov @ 2024-11-15 0:46 UTC (permalink / raw)
To: bpf; +Cc: Anton Protopopov
Add a new helper to get a pointer to a struct btf from a file
descriptor which doesn't increase a refcount.
Signed-off-by: Anton Protopopov <aspsk@isovalent.com>
---
include/linux/btf.h | 13 +++++++++++++
kernel/bpf/btf.c | 13 ++++---------
2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/include/linux/btf.h b/include/linux/btf.h
index 4214e76c9168..050051a578a8 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -4,6 +4,7 @@
#ifndef _LINUX_BTF_H
#define _LINUX_BTF_H 1
+#include <linux/file.h>
#include <linux/types.h>
#include <linux/bpfptr.h>
#include <linux/bsearch.h>
@@ -143,6 +144,18 @@ void btf_get(struct btf *btf);
void btf_put(struct btf *btf);
const struct btf_header *btf_header(const struct btf *btf);
int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, u32 uattr_sz);
+
+static inline struct btf *__btf_get_by_fd(struct fd f)
+{
+ if (fd_empty(f))
+ return ERR_PTR(-EBADF);
+
+ if (unlikely(fd_file(f)->f_op != &btf_fops))
+ return ERR_PTR(-EINVAL);
+
+ return fd_file(f)->private_data;
+}
+
struct btf *btf_get_by_fd(int fd);
int btf_get_info_by_fd(const struct btf *btf,
const union bpf_attr *attr,
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index e7a59e6462a9..ad5310fa1d3b 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7743,17 +7743,12 @@ int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
struct btf *btf_get_by_fd(int fd)
{
- struct btf *btf;
CLASS(fd, f)(fd);
+ struct btf *btf;
- if (fd_empty(f))
- return ERR_PTR(-EBADF);
-
- if (fd_file(f)->f_op != &btf_fops)
- return ERR_PTR(-EINVAL);
-
- btf = fd_file(f)->private_data;
- refcount_inc(&btf->refcnt);
+ btf = __btf_get_by_fd(f);
+ if (!IS_ERR(btf))
+ refcount_inc(&btf->refcnt);
return btf;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH bpf-next 2/5] bpf: move map/prog compatibility checks
2024-11-15 0:46 [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 1/5] bpf: add a __btf_get_by_fd helper Anton Protopopov
@ 2024-11-15 0:46 ` Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 3/5] bpf: add fd_array_cnt attribute for prog_load Anton Protopopov
` (3 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Anton Protopopov @ 2024-11-15 0:46 UTC (permalink / raw)
To: bpf; +Cc: Anton Protopopov
Move some inlined map/prog compatibility checks from the
resolve_pseudo_ldimm64() function to the dedicated
check_map_prog_compatibility() function. Call the latter function
from the add_used_map_from_fd() function directly.
This simplifies code and optimizes logic a bit, as before these
changes the check_map_prog_compatibility() function was executed on
every map usage, which doesn't make sense, as it doesn't include any
per-instruction checks, only map type vs. prog type.
(This patch also simplifies a consequent patch which will call the
add_used_map_from_fd() function from another code path.)
Signed-off-by: Anton Protopopov <aspsk@isovalent.com>
---
kernel/bpf/verifier.c | 101 +++++++++++++++++++-----------------------
1 file changed, 46 insertions(+), 55 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f4c39bb50511..45c11d9cee60 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -19075,6 +19075,12 @@ static bool is_tracing_prog_type(enum bpf_prog_type type)
}
}
+static bool bpf_map_is_cgroup_storage(struct bpf_map *map)
+{
+ return (map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
+ map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE);
+}
+
static int check_map_prog_compatibility(struct bpf_verifier_env *env,
struct bpf_map *map,
struct bpf_prog *prog)
@@ -19153,25 +19159,48 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
return -EINVAL;
}
- return 0;
-}
+ if (bpf_map_is_cgroup_storage(map) &&
+ bpf_cgroup_storage_assign(env->prog->aux, map)) {
+ verbose(env, "only one cgroup storage of each type is allowed\n");
+ return -EBUSY;
+ }
-static bool bpf_map_is_cgroup_storage(struct bpf_map *map)
-{
- return (map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
- map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE);
+ if (map->map_type == BPF_MAP_TYPE_ARENA) {
+ if (env->prog->aux->arena) {
+ verbose(env, "Only one arena per program\n");
+ return -EBUSY;
+ }
+ if (!env->allow_ptr_leaks || !env->bpf_capable) {
+ verbose(env, "CAP_BPF and CAP_PERFMON are required to use arena\n");
+ return -EPERM;
+ }
+ if (!env->prog->jit_requested) {
+ verbose(env, "JIT is required to use arena\n");
+ return -EOPNOTSUPP;
+ }
+ if (!bpf_jit_supports_arena()) {
+ verbose(env, "JIT doesn't support arena\n");
+ return -EOPNOTSUPP;
+ }
+ env->prog->aux->arena = (void *)map;
+ if (!bpf_arena_get_user_vm_start(env->prog->aux->arena)) {
+ verbose(env, "arena's user address must be set via map_extra or mmap()\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
}
/* Add map behind fd to used maps list, if it's not already there, and return
- * its index. Also set *reused to true if this map was already in the list of
- * used maps.
+ * its index.
* Returns <0 on error, or >= 0 index, on success.
*/
-static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd, bool *reused)
+static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd)
{
CLASS(fd, f)(fd);
struct bpf_map *map;
- int i;
+ int i, err;
map = __bpf_map_get(f);
if (IS_ERR(map)) {
@@ -19180,12 +19209,9 @@ static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd, bool *reus
}
/* check whether we recorded this map already */
- for (i = 0; i < env->used_map_cnt; i++) {
- if (env->used_maps[i] == map) {
- *reused = true;
+ for (i = 0; i < env->used_map_cnt; i++)
+ if (env->used_maps[i] == map)
return i;
- }
- }
if (env->used_map_cnt >= MAX_USED_MAPS) {
verbose(env, "The total number of maps per program has reached the limit of %u\n",
@@ -19193,6 +19219,10 @@ static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd, bool *reus
return -E2BIG;
}
+ err = check_map_prog_compatibility(env, map, env->prog);
+ if (err)
+ return err;
+
if (env->prog->sleepable)
atomic64_inc(&map->sleepable_refcnt);
@@ -19203,7 +19233,6 @@ static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd, bool *reus
*/
bpf_map_inc(map);
- *reused = false;
env->used_maps[env->used_map_cnt++] = map;
return env->used_map_cnt - 1;
@@ -19240,7 +19269,6 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
int map_idx;
u64 addr;
u32 fd;
- bool reused;
if (i == insn_cnt - 1 || insn[1].code != 0 ||
insn[1].dst_reg != 0 || insn[1].src_reg != 0 ||
@@ -19301,7 +19329,7 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
break;
}
- map_idx = add_used_map_from_fd(env, fd, &reused);
+ map_idx = add_used_map_from_fd(env, fd);
if (map_idx < 0)
return map_idx;
map = env->used_maps[map_idx];
@@ -19309,10 +19337,6 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
aux = &env->insn_aux_data[i];
aux->map_index = map_idx;
- err = check_map_prog_compatibility(env, map, env->prog);
- if (err)
- return err;
-
if (insn[0].src_reg == BPF_PSEUDO_MAP_FD ||
insn[0].src_reg == BPF_PSEUDO_MAP_IDX) {
addr = (unsigned long)map;
@@ -19343,39 +19367,6 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
insn[0].imm = (u32)addr;
insn[1].imm = addr >> 32;
- /* proceed with extra checks only if its newly added used map */
- if (reused)
- goto next_insn;
-
- if (bpf_map_is_cgroup_storage(map) &&
- bpf_cgroup_storage_assign(env->prog->aux, map)) {
- verbose(env, "only one cgroup storage of each type is allowed\n");
- return -EBUSY;
- }
- if (map->map_type == BPF_MAP_TYPE_ARENA) {
- if (env->prog->aux->arena) {
- verbose(env, "Only one arena per program\n");
- return -EBUSY;
- }
- if (!env->allow_ptr_leaks || !env->bpf_capable) {
- verbose(env, "CAP_BPF and CAP_PERFMON are required to use arena\n");
- return -EPERM;
- }
- if (!env->prog->jit_requested) {
- verbose(env, "JIT is required to use arena\n");
- return -EOPNOTSUPP;
- }
- if (!bpf_jit_supports_arena()) {
- verbose(env, "JIT doesn't support arena\n");
- return -EOPNOTSUPP;
- }
- env->prog->aux->arena = (void *)map;
- if (!bpf_arena_get_user_vm_start(env->prog->aux->arena)) {
- verbose(env, "arena's user address must be set via map_extra or mmap()\n");
- return -EINVAL;
- }
- }
-
next_insn:
insn++;
i++;
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH bpf-next 3/5] bpf: add fd_array_cnt attribute for prog_load
2024-11-15 0:46 [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 1/5] bpf: add a __btf_get_by_fd helper Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 2/5] bpf: move map/prog compatibility checks Anton Protopopov
@ 2024-11-15 0:46 ` Anton Protopopov
2024-11-16 3:06 ` Eduard Zingerman
2024-11-15 0:46 ` [PATCH bpf-next 4/5] selftests/bpf: Add tests for fd_array_cnt Anton Protopopov
` (2 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Anton Protopopov @ 2024-11-15 0:46 UTC (permalink / raw)
To: bpf; +Cc: Anton Protopopov
The fd_array attribute of the BPF_PROG_LOAD syscall may contain a set
of file descriptors: maps or btfs. This field was introduced as a
sparse array. Introduce a new attribute, fd_array_cnt, which, if
present, indicates that the fd_array is a continuous array of the
corresponding length.
If fd_array_cnt is non-zero, then every map in the fd_array will be
bound to the program, as if it was used by the program. This
functionality is similar to the BPF_PROG_BIND_MAP syscall, but such
maps can be used by the verifier during the program load.
Signed-off-by: Anton Protopopov <aspsk@isovalent.com>
---
include/uapi/linux/bpf.h | 10 +++
kernel/bpf/syscall.c | 2 +-
kernel/bpf/verifier.c | 107 ++++++++++++++++++++++++++++-----
tools/include/uapi/linux/bpf.h | 10 +++
4 files changed, 114 insertions(+), 15 deletions(-)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 4162afc6b5d0..2acf9b336371 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1573,6 +1573,16 @@ union bpf_attr {
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
*/
__s32 prog_token_fd;
+ /* The fd_array_cnt can be used to pass the length of the
+ * fd_array array. In this case all the [map] file descriptors
+ * passed in this array will be bound to the program, even if
+ * the maps are not referenced directly. The functionality is
+ * similar to the BPF_PROG_BIND_MAP syscall, but maps can be
+ * used by the verifier during the program load. If provided,
+ * then the fd_array[0,...,fd_array_cnt-1] is expected to be
+ * continuous.
+ */
+ __u32 fd_array_cnt;
};
struct { /* anonymous struct used by BPF_OBJ_* commands */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 58190ca724a2..7e3fbc23c742 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2729,7 +2729,7 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
}
/* last field in 'union bpf_attr' used by this command */
-#define BPF_PROG_LOAD_LAST_FIELD prog_token_fd
+#define BPF_PROG_LOAD_LAST_FIELD fd_array_cnt
static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
{
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 45c11d9cee60..2e262f6516b3 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -19192,22 +19192,10 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
return 0;
}
-/* Add map behind fd to used maps list, if it's not already there, and return
- * its index.
- * Returns <0 on error, or >= 0 index, on success.
- */
-static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd)
+static int add_used_map(struct bpf_verifier_env *env, struct bpf_map *map)
{
- CLASS(fd, f)(fd);
- struct bpf_map *map;
int i, err;
- map = __bpf_map_get(f);
- if (IS_ERR(map)) {
- verbose(env, "fd %d is not pointing to valid bpf_map\n", fd);
- return PTR_ERR(map);
- }
-
/* check whether we recorded this map already */
for (i = 0; i < env->used_map_cnt; i++)
if (env->used_maps[i] == map)
@@ -19238,6 +19226,24 @@ static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd)
return env->used_map_cnt - 1;
}
+/* Add map behind fd to used maps list, if it's not already there, and return
+ * its index.
+ * Returns <0 on error, or >= 0 index, on success.
+ */
+static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd)
+{
+ struct bpf_map *map;
+ CLASS(fd, f)(fd);
+
+ map = __bpf_map_get(f);
+ if (IS_ERR(map)) {
+ verbose(env, "fd %d is not pointing to valid bpf_map\n", fd);
+ return PTR_ERR(map);
+ }
+
+ return add_used_map(env, map);
+}
+
/* find and rewrite pseudo imm in ld_imm64 instructions:
*
* 1. if it accesses map FD, replace it with actual map pointer.
@@ -22537,6 +22543,76 @@ struct btf *bpf_get_btf_vmlinux(void)
return btf_vmlinux;
}
+/*
+ * The add_fd_from_fd_array() is executed only if fd_array_cnt is given. In
+ * this case expect that every file descriptor in the array is either a map or
+ * a BTF, or a hole (0). Everything else is considered to be trash.
+ */
+static int add_fd_from_fd_array(struct bpf_verifier_env *env, int fd)
+{
+ struct bpf_map *map;
+ CLASS(fd, f)(fd);
+ int ret;
+
+ map = __bpf_map_get(f);
+ if (IS_ERR(map)) {
+ if (!IS_ERR(__btf_get_by_fd(f)))
+ return 0;
+
+ /* allow holes */
+ if (!fd)
+ return 0;
+
+ verbose(env, "fd %d is not pointing to valid bpf_map or btf\n", fd);
+ return PTR_ERR(map);
+ }
+
+ ret = add_used_map(env, map);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int env_init_fd_array(struct bpf_verifier_env *env, union bpf_attr *attr, bpfptr_t uattr)
+{
+ int size = sizeof(int) * attr->fd_array_cnt;
+ int *copy;
+ int ret;
+ int i;
+
+ if (attr->fd_array_cnt >= MAX_USED_MAPS)
+ return -E2BIG;
+
+ env->fd_array = make_bpfptr(attr->fd_array, uattr.is_kernel);
+
+ /*
+ * The only difference between old (no fd_array_cnt is given) and new
+ * APIs is that in the latter case the fd_array is expected to be
+ * continuous and is scanned for map fds right away
+ */
+ if (!size)
+ return 0;
+
+ copy = kzalloc(size, GFP_KERNEL);
+ if (!copy)
+ return -ENOMEM;
+
+ if (copy_from_bpfptr_offset(copy, env->fd_array, 0, size)) {
+ ret = -EFAULT;
+ goto free_copy;
+ }
+
+ for (i = 0; i < attr->fd_array_cnt; i++) {
+ ret = add_fd_from_fd_array(env, copy[i]);
+ if (ret)
+ goto free_copy;
+ }
+
+free_copy:
+ kfree(copy);
+ return ret;
+}
+
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
{
u64 start_time = ktime_get_ns();
@@ -22568,7 +22644,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
env->insn_aux_data[i].orig_idx = i;
env->prog = *prog;
env->ops = bpf_verifier_ops[env->prog->type];
- env->fd_array = make_bpfptr(attr->fd_array, uattr.is_kernel);
+ ret = env_init_fd_array(env, attr, uattr);
+ if (ret)
+ goto err_free_aux_data;
env->allow_ptr_leaks = bpf_allow_ptr_leaks(env->prog->aux->token);
env->allow_uninit_stack = bpf_allow_uninit_stack(env->prog->aux->token);
@@ -22786,6 +22864,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
err_unlock:
if (!is_priv)
mutex_unlock(&bpf_verifier_lock);
+err_free_aux_data:
vfree(env->insn_aux_data);
err_free_env:
kvfree(env);
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 4162afc6b5d0..2acf9b336371 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1573,6 +1573,16 @@ union bpf_attr {
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
*/
__s32 prog_token_fd;
+ /* The fd_array_cnt can be used to pass the length of the
+ * fd_array array. In this case all the [map] file descriptors
+ * passed in this array will be bound to the program, even if
+ * the maps are not referenced directly. The functionality is
+ * similar to the BPF_PROG_BIND_MAP syscall, but maps can be
+ * used by the verifier during the program load. If provided,
+ * then the fd_array[0,...,fd_array_cnt-1] is expected to be
+ * continuous.
+ */
+ __u32 fd_array_cnt;
};
struct { /* anonymous struct used by BPF_OBJ_* commands */
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH bpf-next 4/5] selftests/bpf: Add tests for fd_array_cnt
2024-11-15 0:46 [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Anton Protopopov
` (2 preceding siblings ...)
2024-11-15 0:46 ` [PATCH bpf-next 3/5] bpf: add fd_array_cnt attribute for prog_load Anton Protopopov
@ 2024-11-15 0:46 ` Anton Protopopov
2024-11-16 3:06 ` Eduard Zingerman
2024-11-15 0:46 ` [PATCH bpf-next 5/5] bpf: fix potential error return Anton Protopopov
2024-11-16 3:06 ` [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Eduard Zingerman
5 siblings, 1 reply; 12+ messages in thread
From: Anton Protopopov @ 2024-11-15 0:46 UTC (permalink / raw)
To: bpf; +Cc: Anton Protopopov
Add a new set of tests to test the new field in PROG_LOAD-related
part of bpf_attr: fd_array_cnt.
Add the following test cases:
* fd_array_cnt/no-fd-array: program is loaded in a normal
way, without any fd_array present
* fd_array_cnt/fd-array-ok: pass two extra non-used maps,
check that they're bound to the program
* fd_array_cnt/fd-array-dup-input: pass a few extra maps,
only two of which are unique
* fd_array_cnt/fd-array-ref-maps-in-array: pass a map in
fd_array which is also referenced from within the program
* fd_array_cnt/fd-array-trash-input: pass array with some trash
* fd_array_cnt/fd-array-with-holes: pass an array with holes (fd=0)
* fd_array_cnt/fd-array-2big: pass too large array
All the tests above are using the bpf(2) syscall directly,
no libbpf involved.
Signed-off-by: Anton Protopopov <aspsk@isovalent.com>
---
.../selftests/bpf/prog_tests/fd_array.c | 374 ++++++++++++++++++
1 file changed, 374 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/fd_array.c
diff --git a/tools/testing/selftests/bpf/prog_tests/fd_array.c b/tools/testing/selftests/bpf/prog_tests/fd_array.c
new file mode 100644
index 000000000000..8b091b428e31
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/fd_array.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+
+#include <linux/btf.h>
+#include <sys/syscall.h>
+#include <bpf/bpf.h>
+
+static inline int _bpf_map_create(void)
+{
+ static union bpf_attr attr = {
+ .map_type = BPF_MAP_TYPE_ARRAY,
+ .key_size = 4,
+ .value_size = 8,
+ .max_entries = 1,
+ };
+
+ return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+#define BTF_INFO_ENC(kind, kind_flag, vlen) \
+ ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
+#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
+#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
+ ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
+#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
+ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
+ BTF_INT_ENC(encoding, bits_offset, bits)
+
+static int _btf_create(void)
+{
+ struct btf_blob {
+ struct btf_header btf_hdr;
+ __u32 types[8];
+ __u32 str;
+ } raw_btf = {
+ .btf_hdr = {
+ .magic = BTF_MAGIC,
+ .version = BTF_VERSION,
+ .hdr_len = sizeof(struct btf_header),
+ .type_len = sizeof(__u32) * 8,
+ .str_off = sizeof(__u32) * 8,
+ .str_len = sizeof(__u32),
+ },
+ .types = {
+ /* long */
+ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 64, 8), /* [1] */
+ /* unsigned long */
+ BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* [2] */
+ },
+ };
+ static union bpf_attr attr = {
+ .btf_size = sizeof(raw_btf),
+ };
+
+ attr.btf = (long)&raw_btf;
+
+ return syscall(__NR_bpf, BPF_BTF_LOAD, &attr, sizeof(attr));
+}
+
+static bool map_exists(__u32 id)
+{
+ int fd;
+
+ fd = bpf_map_get_fd_by_id(id);
+ if (fd >= 0) {
+ close(fd);
+ return true;
+ }
+ return false;
+}
+
+static inline int bpf_prog_get_map_ids(int prog_fd, __u32 *nr_map_ids, __u32 *map_ids)
+{
+ __u32 len = sizeof(struct bpf_prog_info);
+ struct bpf_prog_info info = {
+ .nr_map_ids = *nr_map_ids,
+ .map_ids = ptr_to_u64(map_ids),
+ };
+ int err;
+
+ err = bpf_prog_get_info_by_fd(prog_fd, &info, &len);
+ if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
+ return -1;
+
+ *nr_map_ids = info.nr_map_ids;
+
+ return 0;
+}
+
+static int __load_test_prog(int map_fd, int *fd_array, int fd_array_cnt)
+{
+ /* A trivial program which uses one map */
+ struct bpf_insn insns[] = {
+ BPF_LD_MAP_FD(BPF_REG_1, map_fd),
+ BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ union bpf_attr attr = {
+ .prog_type = BPF_PROG_TYPE_XDP, /* we don't care */
+ .insns = ptr_to_u64(insns),
+ .insn_cnt = ARRAY_SIZE(insns),
+ .license = ptr_to_u64("GPL"),
+ .fd_array = ptr_to_u64(fd_array),
+ .fd_array_cnt = fd_array_cnt,
+ };
+
+ return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+}
+
+static int load_test_prog(int *fd_array, int fd_array_cnt)
+{
+ int map_fd;
+ int ret;
+
+ map_fd = _bpf_map_create();
+ if (!ASSERT_GE(map_fd, 0, "_bpf_map_create"))
+ return map_fd;
+
+ ret = __load_test_prog(map_fd, fd_array, fd_array_cnt);
+ close(map_fd);
+
+ /* switch back to returning the actual value */
+ if (ret < 0)
+ return -errno;
+ return ret;
+}
+
+static bool check_expected_map_ids(int prog_fd, int expected, __u32 *map_ids, __u32 *nr_map_ids)
+{
+ int err;
+
+ err = bpf_prog_get_map_ids(prog_fd, nr_map_ids, map_ids);
+ if (!ASSERT_OK(err, "bpf_prog_get_map_ids"))
+ return false;
+ if (!ASSERT_EQ(*nr_map_ids, expected, "unexpected nr_map_ids"))
+ return false;
+
+ return true;
+}
+
+/*
+ * Load a program, which uses one map. No fd_array maps are present.
+ * On return only one map is expected to be bound to prog.
+ */
+static void check_fd_array_cnt__no_fd_array(void)
+{
+ __u32 map_ids[16];
+ __u32 nr_map_ids;
+ int prog_fd;
+
+ prog_fd = load_test_prog(NULL, 0);
+ if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
+ return;
+ nr_map_ids = ARRAY_SIZE(map_ids);
+ check_expected_map_ids(prog_fd, 1, map_ids, &nr_map_ids);
+ close(prog_fd);
+}
+
+/*
+ * Load a program, which uses one map, and pass two extra, non-equal, maps in
+ * fd_array with fd_array_cnt=2. On return three maps are expected to be bound
+ * to the program.
+ */
+static void check_fd_array_cnt__fd_array_ok(void)
+{
+ int extra_fds[128];
+ __u32 map_ids[16];
+ __u32 nr_map_ids;
+ int prog_fd;
+
+ extra_fds[0] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[0], 0, "_bpf_map_create"))
+ return;
+ extra_fds[1] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[1], 0, "_bpf_map_create"))
+ return;
+ prog_fd = load_test_prog(extra_fds, 2);
+ if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
+ return;
+ nr_map_ids = ARRAY_SIZE(map_ids);
+ if (!check_expected_map_ids(prog_fd, 3, map_ids, &nr_map_ids))
+ return;
+
+ /* maps should still exist when original file descriptors are closed */
+ close(extra_fds[0]);
+ close(extra_fds[1]);
+ if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map_ids[0] should exist"))
+ return;
+ if (!ASSERT_EQ(map_exists(map_ids[1]), true, "map_ids[1] should exist"))
+ return;
+
+ close(prog_fd);
+}
+
+/*
+ * Load a program with a few extra maps duplicated in the fd_array.
+ * After the load maps should only be referenced once.
+ */
+static void check_fd_array_cnt__duplicated_maps(void)
+{
+ int extra_fds[128];
+ __u32 map_ids[16];
+ __u32 nr_map_ids;
+ int prog_fd;
+
+ extra_fds[0] = extra_fds[2] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[0], 0, "_bpf_map_create"))
+ return;
+ extra_fds[1] = extra_fds[3] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[1], 0, "_bpf_map_create"))
+ return;
+ prog_fd = load_test_prog(extra_fds, 4);
+ if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
+ return;
+ nr_map_ids = ARRAY_SIZE(map_ids);
+ if (!check_expected_map_ids(prog_fd, 3, map_ids, &nr_map_ids))
+ return;
+
+ /* maps should still exist when original file descriptors are closed */
+ close(extra_fds[0]);
+ close(extra_fds[1]);
+ if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map should exist"))
+ return;
+ if (!ASSERT_EQ(map_exists(map_ids[1]), true, "map should exist"))
+ return;
+
+ close(prog_fd);
+}
+
+/*
+ * Check that if maps which are referenced by a program are
+ * passed in fd_array, then they will be referenced only once
+ */
+static void check_fd_array_cnt__referenced_maps_in_fd_array(void)
+{
+ int extra_fds[128];
+ __u32 map_ids[16];
+ __u32 nr_map_ids;
+ int prog_fd;
+
+ extra_fds[0] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[0], 0, "_bpf_map_create"))
+ return;
+ prog_fd = __load_test_prog(extra_fds[0], extra_fds, 1);
+ if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
+ return;
+ nr_map_ids = ARRAY_SIZE(map_ids);
+ if (!check_expected_map_ids(prog_fd, 1, map_ids, &nr_map_ids))
+ return;
+
+ /* map should still exist when original file descriptor is closed */
+ close(extra_fds[0]);
+ if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map should exist"))
+ return;
+
+ /* map should disappear when the program is closed */
+ close(prog_fd);
+}
+
+/*
+ * Test that a program with trash in fd_array can't be loaded:
+ * only map and BTF file descriptors should be accepted.
+ */
+static void check_fd_array_cnt__fd_array_with_trash(void)
+{
+ int extra_fds[128];
+ int prog_fd;
+
+ extra_fds[0] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[0], 0, "_bpf_map_create"))
+ return;
+ extra_fds[1] = _btf_create();
+ if (!ASSERT_GE(extra_fds[1], 0, "_btf_create"))
+ return;
+
+ /* trash 1: not a file descriptor */
+ extra_fds[2] = 0xbeef;
+ prog_fd = load_test_prog(extra_fds, 3);
+ if (!ASSERT_EQ(prog_fd, -EBADF, "prog should have been rejected with -EBADF"))
+ return;
+
+ /* trash 2: not a map or btf */
+ extra_fds[2] = socket(AF_INET, SOCK_STREAM, 0);
+ if (!ASSERT_GE(extra_fds[2], 0, "socket"))
+ return;
+
+ prog_fd = load_test_prog(extra_fds, 3);
+ if (!ASSERT_EQ(prog_fd, -EINVAL, "prog should have been rejected with -EINVAL"))
+ return;
+
+ close(extra_fds[2]);
+ close(extra_fds[1]);
+ close(extra_fds[0]);
+}
+
+/*
+ * Test that a program with zeroes (= holes) in fd_array can be loaded:
+ * only map and BTF file descriptors should be accepted.
+ */
+static void check_fd_array_cnt__fd_array_with_holes(void)
+{
+ int extra_fds[128];
+ int prog_fd;
+
+ extra_fds[0] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[0], 0, "_bpf_map_create"))
+ return;
+ /* punch a hole*/
+ extra_fds[1] = 0;
+ extra_fds[2] = _btf_create();
+ if (!ASSERT_GE(extra_fds[1], 0, "_btf_create"))
+ return;
+ /* punch a hole*/
+ extra_fds[3] = 0;
+
+ prog_fd = load_test_prog(extra_fds, 4);
+ ASSERT_GE(prog_fd, 0, "prog with holes should have been loaded");
+
+ close(extra_fds[2]);
+ close(extra_fds[0]);
+}
+
+
+/*
+ * Test that a program with too big fd_array can't be loaded.
+ */
+static void check_fd_array_cnt__fd_array_too_big(void)
+{
+ int extra_fds[128];
+ int prog_fd;
+ int i;
+
+ for (i = 0; i < 65; i++) {
+ extra_fds[i] = _bpf_map_create();
+ if (!ASSERT_GE(extra_fds[i], 0, "_bpf_map_create"))
+ goto cleanup_fds;
+ }
+
+ prog_fd = load_test_prog(extra_fds, 65);
+ ASSERT_EQ(prog_fd, -E2BIG, "prog should have been rejected with -E2BIG");
+
+cleanup_fds:
+ while (i > 0)
+ close(extra_fds[--i]);
+}
+
+void test_fd_array_cnt(void)
+{
+ if (test__start_subtest("no-fd-array"))
+ check_fd_array_cnt__no_fd_array();
+
+ if (test__start_subtest("fd-array-ok"))
+ check_fd_array_cnt__fd_array_ok();
+
+ if (test__start_subtest("fd-array-dup-input"))
+ check_fd_array_cnt__duplicated_maps();
+
+ if (test__start_subtest("fd-array-ref-maps-in-array"))
+ check_fd_array_cnt__referenced_maps_in_fd_array();
+
+ if (test__start_subtest("fd-array-trash-input"))
+ check_fd_array_cnt__fd_array_with_trash();
+
+ if (test__start_subtest("fd-array-with-holes"))
+ check_fd_array_cnt__fd_array_with_holes();
+
+ if (test__start_subtest("fd-array-2big"))
+ check_fd_array_cnt__fd_array_too_big();
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH bpf-next 5/5] bpf: fix potential error return
2024-11-15 0:46 [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Anton Protopopov
` (3 preceding siblings ...)
2024-11-15 0:46 ` [PATCH bpf-next 4/5] selftests/bpf: Add tests for fd_array_cnt Anton Protopopov
@ 2024-11-15 0:46 ` Anton Protopopov
2024-11-16 3:06 ` [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Eduard Zingerman
5 siblings, 0 replies; 12+ messages in thread
From: Anton Protopopov @ 2024-11-15 0:46 UTC (permalink / raw)
To: bpf; +Cc: Anton Protopopov, Jiri Olsa
The bpf_remove_insns() function returns WARN_ON_ONCE(error), where
error is a result of bpf_adj_branches(), and thus should be always 0
However, if for any reason it is not 0, then it will be converted to
boolean by WARN_ON_ONCE and returned to user space as 1, not an actual
error value. Fix this by returning the original err after the WARN check.
Signed-off-by: Anton Protopopov <aspsk@isovalent.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
---
kernel/bpf/core.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 14d9288441f2..a15059918768 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -539,6 +539,8 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt)
{
+ int err;
+
/* Branch offsets can't overflow when program is shrinking, no need
* to call bpf_adj_branches(..., true) here
*/
@@ -546,7 +548,12 @@ int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt)
sizeof(struct bpf_insn) * (prog->len - off - cnt));
prog->len -= cnt;
- return WARN_ON_ONCE(bpf_adj_branches(prog, off, off + cnt, off, false));
+ err = bpf_adj_branches(prog, off, off + cnt, off, false);
+ WARN_ON_ONCE(err);
+ if (err)
+ return err;
+
+ return 0;
}
static void bpf_prog_kallsyms_del_subprogs(struct bpf_prog *fp)
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD
2024-11-15 0:46 [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Anton Protopopov
` (4 preceding siblings ...)
2024-11-15 0:46 ` [PATCH bpf-next 5/5] bpf: fix potential error return Anton Protopopov
@ 2024-11-16 3:06 ` Eduard Zingerman
2024-11-17 21:22 ` Anton Protopopov
5 siblings, 1 reply; 12+ messages in thread
From: Eduard Zingerman @ 2024-11-16 3:06 UTC (permalink / raw)
To: Anton Protopopov, bpf
On Fri, 2024-11-15 at 00:46 +0000, Anton Protopopov wrote:
> Add a new attribute to the bpf(BPF_PROG_LOAD) system call. If this
> new attribute is non-zero, then the fd_array is considered to be a
> continuous array of the fd_array_cnt length and to contain only
> proper map file descriptors, or btf file descriptors, or zeroes.
>
> This change allows maps, which aren't referenced directly by a BPF
> program, to be bound to the program _and_ also to be present during
> the program verification (so BPF_PROG_BIND_MAP is not enough for this
> use case).
>
> The primary reason for this change is that it is a prerequisite for
> adding "instruction set" maps, which are both non-referenced by the
> program and must be present during the program verification.
>
> The first three commits add the new functionality, the fourth adds
> corresponding self-tests, and the last one is a small additional fix.
When I apply this series on top of [1] (there is a small merge conflict),
I get an error message from KASAN, the message is at the end of this email.
Probably triggered by processing of preloaded BPF programs.
Also added a few nits for individual patches.
[1] fab974e64874 ("libbpf: Fix memory leak in bpf_program__attach_uprobe_multi")
---
[ 1.107455] ------------[ cut here ]------------
[ 1.107545] Trying to vfree() nonexistent vm area (000000003f161725)
[ 1.107640] WARNING: CPU: 6 PID: 1 at mm/vmalloc.c:3345 vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
[ 1.107731] Modules linked in:
[ 1.107922] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-2.fc40 04/01/2014
[ 1.108057] RIP: 0010:vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
[ 1.108123] Code: ea 03 42 80 3c 22 00 0f 85 2d 04 00 00 48 8b 38 48 85 ff 0f 85 76 ff ff ff 0f 0b 4c 89 e6 48 c7 c7 60 47 94 83 e8 5e b2 83 ff <0f> 0b 48 83 c4 60 5b 5d 41 5c 41 5d 41 5e 41 5f e9 34 f8 dd 01 89
All code
========
0: ea (bad)
1: 03 42 80 add -0x80(%rdx),%eax
4: 3c 22 cmp $0x22,%al
6: 00 0f add %cl,(%rdi)
8: 85 2d 04 00 00 48 test %ebp,0x48000004(%rip) # 0x48000012
e: 8b 38 mov (%rax),%edi
10: 48 85 ff test %rdi,%rdi
13: 0f 85 76 ff ff ff jne 0xffffffffffffff8f
19: 0f 0b ud2
1b: 4c 89 e6 mov %r12,%rsi
1e: 48 c7 c7 60 47 94 83 mov $0xffffffff83944760,%rdi
25: e8 5e b2 83 ff call 0xffffffffff83b288
2a:* 0f 0b ud2 <-- trapping instruction
2c: 48 83 c4 60 add $0x60,%rsp
30: 5b pop %rbx
31: 5d pop %rbp
32: 41 5c pop %r12
34: 41 5d pop %r13
36: 41 5e pop %r14
38: 41 5f pop %r15
3a: e9 34 f8 dd 01 jmp 0x1ddf873
3f: 89 .byte 0x89
Code starting with the faulting instruction
===========================================
0: 0f 0b ud2
2: 48 83 c4 60 add $0x60,%rsp
6: 5b pop %rbx
7: 5d pop %rbp
8: 41 5c pop %r12
a: 41 5d pop %r13
c: 41 5e pop %r14
e: 41 5f pop %r15
10: e9 34 f8 dd 01 jmp 0x1ddf849
15: 89 .byte 0x89
[ 1.108379] RSP: 0018:ffff88810034f368 EFLAGS: 00010296
[ 1.108459] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
[ 1.108576] RDX: 0000000000000000 RSI: 0000000000000004 RDI: 0000000000000001
[ 1.108682] RBP: 0000000000000000 R08: 0000000000000000 R09: fffffbfff08dfaa4
[ 1.108791] R10: 0000000000000003 R11: ffffffff8475a8f0 R12: ffffc900001d6000
[ 1.108896] R13: ffff888104e5064c R14: ffffc900001d49c0 R15: 0000000000000005
[ 1.108999] FS: 0000000000000000(0000) GS:ffff88815b300000(0000) knlGS:0000000000000000
[ 1.109104] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1.109234] CR2: 0000000000000000 CR3: 0000000004698000 CR4: 0000000000750ef0
[ 1.109352] PKRU: 55555554
[ 1.109397] Call Trace:
[ 1.109442] <TASK>
[ 1.109489] ? __warn.cold (kernel/panic.c:748)
[ 1.109564] ? vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
[ 1.109623] ? report_bug (lib/bug.c:180 lib/bug.c:219)
[ 1.109710] ? handle_bug (arch/x86/kernel/traps.c:285)
[ 1.109775] ? exc_invalid_op (arch/x86/kernel/traps.c:309 (discriminator 1))
[ 1.109838] ? asm_exc_invalid_op (./arch/x86/include/asm/idtentry.h:621)
[ 1.109914] ? vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
[ 1.109982] ? vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
[ 1.110047] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.110128] ? kfree (mm/slub.c:4579 (discriminator 3) mm/slub.c:4727 (discriminator 3))
[ 1.110191] ? bpf_check (kernel/bpf/verifier.c:22799 (discriminator 1))
[ 1.110252] ? bpf_check (kernel/bpf/verifier.c:22859)
[ 1.110317] bpf_check (kernel/bpf/verifier.c:22861)
[ 1.110382] ? kasan_save_stack (mm/kasan/common.c:49)
[ 1.110443] ? kasan_save_track (mm/kasan/common.c:60 (discriminator 1) mm/kasan/common.c:69 (discriminator 1))
[ 1.110515] ? __pfx_bpf_check (kernel/bpf/verifier.c:22606)
[ 1.110612] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.110690] ? kasan_save_track (mm/kasan/common.c:60 (discriminator 1) mm/kasan/common.c:69 (discriminator 1))
[ 1.110746] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.110820] ? __kasan_kmalloc (mm/kasan/common.c:377 mm/kasan/common.c:394)
[ 1.110885] ? bpf_prog_load (kernel/bpf/syscall.c:2947)
[ 1.110942] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.111015] bpf_prog_load (kernel/bpf/syscall.c:2947)
[ 1.111073] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.111163] ? __pfx_bpf_prog_load (kernel/bpf/syscall.c:2735)
[ 1.111240] ? lock_acquire (kernel/locking/lockdep.c:5798)
[ 1.111315] ? __pfx_bpf_check_uarg_tail_zero (kernel/bpf/syscall.c:87)
[ 1.111401] __sys_bpf (kernel/bpf/syscall.c:5759)
[ 1.111464] ? __pfx___sys_bpf (kernel/bpf/syscall.c:5721)
[ 1.111522] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.111610] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.111690] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.111766] ? kern_sys_bpf (kernel/bpf/syscall.c:5909)
[ 1.111837] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.111912] ? skel_map_update_elem.constprop.0 (./tools/lib/bpf/skel_internal.h:239)
[ 1.111989] ? __pfx_skel_map_update_elem.constprop.0 (./tools/lib/bpf/skel_internal.h:239)
[ 1.112089] kern_sys_bpf (kernel/bpf/syscall.c:5909)
[ 1.112156] ? __pfx_kern_sys_bpf (kernel/bpf/syscall.c:5909)
[ 1.112226] bpf_load_and_run.constprop.0 (./tools/lib/bpf/skel_internal.h:342)
[ 1.112303] ? __pfx_bpf_load_and_run.constprop.0 (./tools/lib/bpf/skel_internal.h:309)
[ 1.112402] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.112480] ? kasan_save_track (mm/kasan/common.c:60 (discriminator 1) mm/kasan/common.c:69 (discriminator 1))
[ 1.112550] load (kernel/bpf/preload/bpf_preload_kern.c:46 kernel/bpf/preload/bpf_preload_kern.c:78)
[ 1.112614] ? __pfx_load (kernel/bpf/preload/bpf_preload_kern.c:75)
[ 1.112673] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
[ 1.112750] ? _raw_spin_unlock_irqrestore (./include/linux/spinlock_api_smp.h:152 (discriminator 3) kernel/locking/spinlock.c:194 (discriminator 3))
[ 1.112837] ? __pfx_crypto_kfunc_init (kernel/bpf/crypto.c:374)
[ 1.112920] ? __pfx_load (kernel/bpf/preload/bpf_preload_kern.c:75)
[ 1.112981] do_one_initcall (init/main.c:1269)
[ 1.113045] ? __pfx_do_one_initcall (init/main.c:1260)
[ 1.113131] ? __kmalloc_noprof (./include/trace/events/kmem.h:54 (discriminator 2) mm/slub.c:4265 (discriminator 2) mm/slub.c:4276 (discriminator 2))
[ 1.113191] ? kernel_init_freeable (init/main.c:1341 init/main.c:1366 init/main.c:1580)
[ 1.113277] kernel_init_freeable (init/main.c:1330 (discriminator 3) init/main.c:1347 (discriminator 3) init/main.c:1366 (discriminator 3) init/main.c:1580 (discriminator 3))
[ 1.113359] ? __pfx_kernel_init (init/main.c:1461)
[ 1.113426] kernel_init (init/main.c:1471)
[ 1.113486] ? __pfx_kernel_init (init/main.c:1461)
[ 1.113554] ret_from_fork (arch/x86/kernel/process.c:147)
[ 1.113616] ? __pfx_kernel_init (init/main.c:1461)
[ 1.113677] ret_from_fork_asm (arch/x86/entry/entry_64.S:257)
[ 1.113752] </TASK>
[ 1.113796] irq event stamp: 168993
[ 1.113857] hardirqs last enabled at (169001): __up_console_sem (./arch/x86/include/asm/irqflags.h:42 ./arch/x86/include/asm/irqflags.h:97 ./arch/x86/include/asm/irqflags.h:155 kernel/printk/printk.c:344)
[ 1.113992] hardirqs last disabled at (169008): __up_console_sem (kernel/printk/printk.c:342 (discriminator 3))
[ 1.114128] softirqs last enabled at (168746): irq_exit_rcu (kernel/softirq.c:589 kernel/softirq.c:428 kernel/softirq.c:637 kernel/softirq.c:649)
[ 1.114264] softirqs last disabled at (168741): irq_exit_rcu (kernel/softirq.c:589 kernel/softirq.c:428 kernel/softirq.c:637 kernel/softirq.c:649)
[ 1.114399] ---[ end trace 0000000000000000 ]---
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next 3/5] bpf: add fd_array_cnt attribute for prog_load
2024-11-15 0:46 ` [PATCH bpf-next 3/5] bpf: add fd_array_cnt attribute for prog_load Anton Protopopov
@ 2024-11-16 3:06 ` Eduard Zingerman
2024-11-17 21:24 ` Anton Protopopov
0 siblings, 1 reply; 12+ messages in thread
From: Eduard Zingerman @ 2024-11-16 3:06 UTC (permalink / raw)
To: Anton Protopopov, bpf
On Fri, 2024-11-15 at 00:46 +0000, Anton Protopopov wrote:
[...]
> @@ -22537,6 +22543,76 @@ struct btf *bpf_get_btf_vmlinux(void)
> return btf_vmlinux;
> }
>
> +/*
> + * The add_fd_from_fd_array() is executed only if fd_array_cnt is given. In
> + * this case expect that every file descriptor in the array is either a map or
> + * a BTF, or a hole (0). Everything else is considered to be trash.
> + */
> +static int add_fd_from_fd_array(struct bpf_verifier_env *env, int fd)
> +{
> + struct bpf_map *map;
> + CLASS(fd, f)(fd);
> + int ret;
> +
> + map = __bpf_map_get(f);
> + if (IS_ERR(map)) {
> + if (!IS_ERR(__btf_get_by_fd(f)))
> + return 0;
> +
> + /* allow holes */
> + if (!fd)
> + return 0;
> +
> + verbose(env, "fd %d is not pointing to valid bpf_map or btf\n", fd);
> + return PTR_ERR(map);
> + }
> +
> + ret = add_used_map(env, map);
> + if (ret < 0)
> + return ret;
> + return 0;
> +}
Nit: keeping this function "flat" would allow easier extension, if necessary.
E.g.:
static int add_fd_from_fd_array(struct bpf_verifier_env *env, int fd)
{
struct bpf_map *map;
CLASS(fd, f)(fd);
int ret;
/* allow holes */
if (!fd) {
return 0;
}
map = __bpf_map_get(f);
if (!IS_ERR(map)) {
ret = add_used_map(env, map);
return ret < 0 ? ret : 0;
}
if (!IS_ERR(__btf_get_by_fd(f))) {
return 0;
}
verbose(env, "fd %d is not pointing to valid bpf_map or btf\n", fd);
return -EINVAL;
}
> +
> +static int env_init_fd_array(struct bpf_verifier_env *env, union bpf_attr *attr, bpfptr_t uattr)
> +{
> + int size = sizeof(int) * attr->fd_array_cnt;
> + int *copy;
> + int ret;
> + int i;
> +
> + if (attr->fd_array_cnt >= MAX_USED_MAPS)
> + return -E2BIG;
> +
> + env->fd_array = make_bpfptr(attr->fd_array, uattr.is_kernel);
> +
> + /*
> + * The only difference between old (no fd_array_cnt is given) and new
> + * APIs is that in the latter case the fd_array is expected to be
> + * continuous and is scanned for map fds right away
> + */
> + if (!size)
> + return 0;
> +
> + copy = kzalloc(size, GFP_KERNEL);
> + if (!copy)
> + return -ENOMEM;
> +
> + if (copy_from_bpfptr_offset(copy, env->fd_array, 0, size)) {
> + ret = -EFAULT;
> + goto free_copy;
> + }
> +
> + for (i = 0; i < attr->fd_array_cnt; i++) {
> + ret = add_fd_from_fd_array(env, copy[i]);
> + if (ret)
> + goto free_copy;
> + }
> +
> +free_copy:
> + kfree(copy);
> + return ret;
> +}
> +
> int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
> {
> u64 start_time = ktime_get_ns();
[...]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next 4/5] selftests/bpf: Add tests for fd_array_cnt
2024-11-15 0:46 ` [PATCH bpf-next 4/5] selftests/bpf: Add tests for fd_array_cnt Anton Protopopov
@ 2024-11-16 3:06 ` Eduard Zingerman
2024-11-17 21:30 ` Anton Protopopov
0 siblings, 1 reply; 12+ messages in thread
From: Eduard Zingerman @ 2024-11-16 3:06 UTC (permalink / raw)
To: Anton Protopopov, bpf
On Fri, 2024-11-15 at 00:46 +0000, Anton Protopopov wrote:
[...]
> @@ -0,0 +1,374 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <test_progs.h>
> +
> +#include <linux/btf.h>
> +#include <sys/syscall.h>
> +#include <bpf/bpf.h>
> +
> +static inline int _bpf_map_create(void)
> +{
> + static union bpf_attr attr = {
> + .map_type = BPF_MAP_TYPE_ARRAY,
> + .key_size = 4,
> + .value_size = 8,
> + .max_entries = 1,
> + };
> +
> + return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
> +}
> +
> +#define BTF_INFO_ENC(kind, kind_flag, vlen) \
> + ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
> +#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
> +#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
> + ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
> +#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
> + BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
> + BTF_INT_ENC(encoding, bits_offset, bits)
Nit: these macro are already defined in tools/testing/selftests/bpf/test_btf.h .
> +
> +static int _btf_create(void)
> +{
> + struct btf_blob {
> + struct btf_header btf_hdr;
> + __u32 types[8];
> + __u32 str;
> + } raw_btf = {
> + .btf_hdr = {
> + .magic = BTF_MAGIC,
> + .version = BTF_VERSION,
> + .hdr_len = sizeof(struct btf_header),
> + .type_len = sizeof(__u32) * 8,
> + .str_off = sizeof(__u32) * 8,
> + .str_len = sizeof(__u32),
Nit: offsetof(struct btf_blob, str), sizeof(raw_btf.str), sizeof(raw_btf.types)
are legal in this position.
> + },
> + .types = {
> + /* long */
> + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 64, 8), /* [1] */
> + /* unsigned long */
> + BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* [2] */
> + },
> + };
> + static union bpf_attr attr = {
> + .btf_size = sizeof(raw_btf),
> + };
> +
> + attr.btf = (long)&raw_btf;
> +
> + return syscall(__NR_bpf, BPF_BTF_LOAD, &attr, sizeof(attr));
> +}
[...]
> +static void check_fd_array_cnt__fd_array_ok(void)
> +{
> + int extra_fds[128];
> + __u32 map_ids[16];
> + __u32 nr_map_ids;
> + int prog_fd;
> +
> + extra_fds[0] = _bpf_map_create();
> + if (!ASSERT_GE(extra_fds[0], 0, "_bpf_map_create"))
> + return;
> + extra_fds[1] = _bpf_map_create();
> + if (!ASSERT_GE(extra_fds[1], 0, "_bpf_map_create"))
> + return;
> + prog_fd = load_test_prog(extra_fds, 2);
> + if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
> + return;
> + nr_map_ids = ARRAY_SIZE(map_ids);
> + if (!check_expected_map_ids(prog_fd, 3, map_ids, &nr_map_ids))
Nit: should probably close prog_fd and extra_fds (and in tests below).
> + return;
> +
> + /* maps should still exist when original file descriptors are closed */
> + close(extra_fds[0]);
> + close(extra_fds[1]);
> + if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map_ids[0] should exist"))
> + return;
> + if (!ASSERT_EQ(map_exists(map_ids[1]), true, "map_ids[1] should exist"))
> + return;
> +
> + close(prog_fd);
> +}
[...]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD
2024-11-16 3:06 ` [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Eduard Zingerman
@ 2024-11-17 21:22 ` Anton Protopopov
0 siblings, 0 replies; 12+ messages in thread
From: Anton Protopopov @ 2024-11-17 21:22 UTC (permalink / raw)
To: Eduard Zingerman; +Cc: bpf
On 24/11/15 07:06PM, Eduard Zingerman wrote:
> On Fri, 2024-11-15 at 00:46 +0000, Anton Protopopov wrote:
> > Add a new attribute to the bpf(BPF_PROG_LOAD) system call. If this
> > new attribute is non-zero, then the fd_array is considered to be a
> > continuous array of the fd_array_cnt length and to contain only
> > proper map file descriptors, or btf file descriptors, or zeroes.
> >
> > This change allows maps, which aren't referenced directly by a BPF
> > program, to be bound to the program _and_ also to be present during
> > the program verification (so BPF_PROG_BIND_MAP is not enough for this
> > use case).
> >
> > The primary reason for this change is that it is a prerequisite for
> > adding "instruction set" maps, which are both non-referenced by the
> > program and must be present during the program verification.
> >
> > The first three commits add the new functionality, the fourth adds
> > corresponding self-tests, and the last one is a small additional fix.
>
> When I apply this series on top of [1] (there is a small merge conflict),
> I get an error message from KASAN, the message is at the end of this email.
> Probably triggered by processing of preloaded BPF programs.
Thanks for pointing to this warning. Unluckily, I can't reproduce it locally,
and neither I have a conflict (I've rebased my branch on top of the current
master, which contains [1]). Could you please tell me which environment you
were using to trigger it? Is this BPF CI?
> Also added a few nits for individual patches.
Thanks for looking! I will reply there.
> [1] fab974e64874 ("libbpf: Fix memory leak in bpf_program__attach_uprobe_multi")
>
> ---
>
> [ 1.107455] ------------[ cut here ]------------
> [ 1.107545] Trying to vfree() nonexistent vm area (000000003f161725)
> [ 1.107640] WARNING: CPU: 6 PID: 1 at mm/vmalloc.c:3345 vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
> [ 1.107731] Modules linked in:
> [ 1.107922] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-2.fc40 04/01/2014
> [ 1.108057] RIP: 0010:vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
> [ 1.108123] Code: ea 03 42 80 3c 22 00 0f 85 2d 04 00 00 48 8b 38 48 85 ff 0f 85 76 ff ff ff 0f 0b 4c 89 e6 48 c7 c7 60 47 94 83 e8 5e b2 83 ff <0f> 0b 48 83 c4 60 5b 5d 41 5c 41 5d 41 5e 41 5f e9 34 f8 dd 01 89
> All code
> ========
> 0: ea (bad)
> 1: 03 42 80 add -0x80(%rdx),%eax
> 4: 3c 22 cmp $0x22,%al
> 6: 00 0f add %cl,(%rdi)
> 8: 85 2d 04 00 00 48 test %ebp,0x48000004(%rip) # 0x48000012
> e: 8b 38 mov (%rax),%edi
> 10: 48 85 ff test %rdi,%rdi
> 13: 0f 85 76 ff ff ff jne 0xffffffffffffff8f
> 19: 0f 0b ud2
> 1b: 4c 89 e6 mov %r12,%rsi
> 1e: 48 c7 c7 60 47 94 83 mov $0xffffffff83944760,%rdi
> 25: e8 5e b2 83 ff call 0xffffffffff83b288
> 2a:* 0f 0b ud2 <-- trapping instruction
> 2c: 48 83 c4 60 add $0x60,%rsp
> 30: 5b pop %rbx
> 31: 5d pop %rbp
> 32: 41 5c pop %r12
> 34: 41 5d pop %r13
> 36: 41 5e pop %r14
> 38: 41 5f pop %r15
> 3a: e9 34 f8 dd 01 jmp 0x1ddf873
> 3f: 89 .byte 0x89
>
> Code starting with the faulting instruction
> ===========================================
> 0: 0f 0b ud2
> 2: 48 83 c4 60 add $0x60,%rsp
> 6: 5b pop %rbx
> 7: 5d pop %rbp
> 8: 41 5c pop %r12
> a: 41 5d pop %r13
> c: 41 5e pop %r14
> e: 41 5f pop %r15
> 10: e9 34 f8 dd 01 jmp 0x1ddf849
> 15: 89 .byte 0x89
> [ 1.108379] RSP: 0018:ffff88810034f368 EFLAGS: 00010296
> [ 1.108459] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
> [ 1.108576] RDX: 0000000000000000 RSI: 0000000000000004 RDI: 0000000000000001
> [ 1.108682] RBP: 0000000000000000 R08: 0000000000000000 R09: fffffbfff08dfaa4
> [ 1.108791] R10: 0000000000000003 R11: ffffffff8475a8f0 R12: ffffc900001d6000
> [ 1.108896] R13: ffff888104e5064c R14: ffffc900001d49c0 R15: 0000000000000005
> [ 1.108999] FS: 0000000000000000(0000) GS:ffff88815b300000(0000) knlGS:0000000000000000
> [ 1.109104] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 1.109234] CR2: 0000000000000000 CR3: 0000000004698000 CR4: 0000000000750ef0
> [ 1.109352] PKRU: 55555554
> [ 1.109397] Call Trace:
> [ 1.109442] <TASK>
> [ 1.109489] ? __warn.cold (kernel/panic.c:748)
> [ 1.109564] ? vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
> [ 1.109623] ? report_bug (lib/bug.c:180 lib/bug.c:219)
> [ 1.109710] ? handle_bug (arch/x86/kernel/traps.c:285)
> [ 1.109775] ? exc_invalid_op (arch/x86/kernel/traps.c:309 (discriminator 1))
> [ 1.109838] ? asm_exc_invalid_op (./arch/x86/include/asm/idtentry.h:621)
> [ 1.109914] ? vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
> [ 1.109982] ? vfree (mm/vmalloc.c:3345 (discriminator 1) mm/vmalloc.c:3326 (discriminator 1))
> [ 1.110047] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.110128] ? kfree (mm/slub.c:4579 (discriminator 3) mm/slub.c:4727 (discriminator 3))
> [ 1.110191] ? bpf_check (kernel/bpf/verifier.c:22799 (discriminator 1))
> [ 1.110252] ? bpf_check (kernel/bpf/verifier.c:22859)
> [ 1.110317] bpf_check (kernel/bpf/verifier.c:22861)
> [ 1.110382] ? kasan_save_stack (mm/kasan/common.c:49)
> [ 1.110443] ? kasan_save_track (mm/kasan/common.c:60 (discriminator 1) mm/kasan/common.c:69 (discriminator 1))
> [ 1.110515] ? __pfx_bpf_check (kernel/bpf/verifier.c:22606)
> [ 1.110612] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.110690] ? kasan_save_track (mm/kasan/common.c:60 (discriminator 1) mm/kasan/common.c:69 (discriminator 1))
> [ 1.110746] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.110820] ? __kasan_kmalloc (mm/kasan/common.c:377 mm/kasan/common.c:394)
> [ 1.110885] ? bpf_prog_load (kernel/bpf/syscall.c:2947)
> [ 1.110942] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.111015] bpf_prog_load (kernel/bpf/syscall.c:2947)
> [ 1.111073] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.111163] ? __pfx_bpf_prog_load (kernel/bpf/syscall.c:2735)
> [ 1.111240] ? lock_acquire (kernel/locking/lockdep.c:5798)
> [ 1.111315] ? __pfx_bpf_check_uarg_tail_zero (kernel/bpf/syscall.c:87)
> [ 1.111401] __sys_bpf (kernel/bpf/syscall.c:5759)
> [ 1.111464] ? __pfx___sys_bpf (kernel/bpf/syscall.c:5721)
> [ 1.111522] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.111610] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.111690] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.111766] ? kern_sys_bpf (kernel/bpf/syscall.c:5909)
> [ 1.111837] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.111912] ? skel_map_update_elem.constprop.0 (./tools/lib/bpf/skel_internal.h:239)
> [ 1.111989] ? __pfx_skel_map_update_elem.constprop.0 (./tools/lib/bpf/skel_internal.h:239)
> [ 1.112089] kern_sys_bpf (kernel/bpf/syscall.c:5909)
> [ 1.112156] ? __pfx_kern_sys_bpf (kernel/bpf/syscall.c:5909)
> [ 1.112226] bpf_load_and_run.constprop.0 (./tools/lib/bpf/skel_internal.h:342)
> [ 1.112303] ? __pfx_bpf_load_and_run.constprop.0 (./tools/lib/bpf/skel_internal.h:309)
> [ 1.112402] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.112480] ? kasan_save_track (mm/kasan/common.c:60 (discriminator 1) mm/kasan/common.c:69 (discriminator 1))
> [ 1.112550] load (kernel/bpf/preload/bpf_preload_kern.c:46 kernel/bpf/preload/bpf_preload_kern.c:78)
> [ 1.112614] ? __pfx_load (kernel/bpf/preload/bpf_preload_kern.c:75)
> [ 1.112673] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:182)
> [ 1.112750] ? _raw_spin_unlock_irqrestore (./include/linux/spinlock_api_smp.h:152 (discriminator 3) kernel/locking/spinlock.c:194 (discriminator 3))
> [ 1.112837] ? __pfx_crypto_kfunc_init (kernel/bpf/crypto.c:374)
> [ 1.112920] ? __pfx_load (kernel/bpf/preload/bpf_preload_kern.c:75)
> [ 1.112981] do_one_initcall (init/main.c:1269)
> [ 1.113045] ? __pfx_do_one_initcall (init/main.c:1260)
> [ 1.113131] ? __kmalloc_noprof (./include/trace/events/kmem.h:54 (discriminator 2) mm/slub.c:4265 (discriminator 2) mm/slub.c:4276 (discriminator 2))
> [ 1.113191] ? kernel_init_freeable (init/main.c:1341 init/main.c:1366 init/main.c:1580)
> [ 1.113277] kernel_init_freeable (init/main.c:1330 (discriminator 3) init/main.c:1347 (discriminator 3) init/main.c:1366 (discriminator 3) init/main.c:1580 (discriminator 3))
> [ 1.113359] ? __pfx_kernel_init (init/main.c:1461)
> [ 1.113426] kernel_init (init/main.c:1471)
> [ 1.113486] ? __pfx_kernel_init (init/main.c:1461)
> [ 1.113554] ret_from_fork (arch/x86/kernel/process.c:147)
> [ 1.113616] ? __pfx_kernel_init (init/main.c:1461)
> [ 1.113677] ret_from_fork_asm (arch/x86/entry/entry_64.S:257)
> [ 1.113752] </TASK>
> [ 1.113796] irq event stamp: 168993
> [ 1.113857] hardirqs last enabled at (169001): __up_console_sem (./arch/x86/include/asm/irqflags.h:42 ./arch/x86/include/asm/irqflags.h:97 ./arch/x86/include/asm/irqflags.h:155 kernel/printk/printk.c:344)
> [ 1.113992] hardirqs last disabled at (169008): __up_console_sem (kernel/printk/printk.c:342 (discriminator 3))
> [ 1.114128] softirqs last enabled at (168746): irq_exit_rcu (kernel/softirq.c:589 kernel/softirq.c:428 kernel/softirq.c:637 kernel/softirq.c:649)
> [ 1.114264] softirqs last disabled at (168741): irq_exit_rcu (kernel/softirq.c:589 kernel/softirq.c:428 kernel/softirq.c:637 kernel/softirq.c:649)
> [ 1.114399] ---[ end trace 0000000000000000 ]---
>
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next 3/5] bpf: add fd_array_cnt attribute for prog_load
2024-11-16 3:06 ` Eduard Zingerman
@ 2024-11-17 21:24 ` Anton Protopopov
0 siblings, 0 replies; 12+ messages in thread
From: Anton Protopopov @ 2024-11-17 21:24 UTC (permalink / raw)
To: Eduard Zingerman; +Cc: bpf
On 24/11/15 07:06PM, Eduard Zingerman wrote:
> On Fri, 2024-11-15 at 00:46 +0000, Anton Protopopov wrote:
>
> [...]
>
> > @@ -22537,6 +22543,76 @@ struct btf *bpf_get_btf_vmlinux(void)
> > return btf_vmlinux;
> > }
> >
> > +/*
> > + * The add_fd_from_fd_array() is executed only if fd_array_cnt is given. In
> > + * this case expect that every file descriptor in the array is either a map or
> > + * a BTF, or a hole (0). Everything else is considered to be trash.
> > + */
> > +static int add_fd_from_fd_array(struct bpf_verifier_env *env, int fd)
> > +{
> > + struct bpf_map *map;
> > + CLASS(fd, f)(fd);
> > + int ret;
> > +
> > + map = __bpf_map_get(f);
> > + if (IS_ERR(map)) {
> > + if (!IS_ERR(__btf_get_by_fd(f)))
> > + return 0;
> > +
> > + /* allow holes */
> > + if (!fd)
> > + return 0;
> > +
> > + verbose(env, "fd %d is not pointing to valid bpf_map or btf\n", fd);
> > + return PTR_ERR(map);
> > + }
> > +
> > + ret = add_used_map(env, map);
> > + if (ret < 0)
> > + return ret;
> > + return 0;
> > +}
>
> Nit: keeping this function "flat" would allow easier extension, if necessary.
> E.g.:
>
> static int add_fd_from_fd_array(struct bpf_verifier_env *env, int fd)
> {
> struct bpf_map *map;
> CLASS(fd, f)(fd);
> int ret;
>
> /* allow holes */
> if (!fd) {
> return 0;
> }
> map = __bpf_map_get(f);
> if (!IS_ERR(map)) {
> ret = add_used_map(env, map);
> return ret < 0 ? ret : 0;
> }
> if (!IS_ERR(__btf_get_by_fd(f))) {
> return 0;
> }
> verbose(env, "fd %d is not pointing to valid bpf_map or btf\n", fd);
> return -EINVAL;
> }
Thanks, this makes sense, I will change it to a flat version in v2.
> [...]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next 4/5] selftests/bpf: Add tests for fd_array_cnt
2024-11-16 3:06 ` Eduard Zingerman
@ 2024-11-17 21:30 ` Anton Protopopov
0 siblings, 0 replies; 12+ messages in thread
From: Anton Protopopov @ 2024-11-17 21:30 UTC (permalink / raw)
To: Eduard Zingerman; +Cc: bpf
On 24/11/15 07:06PM, Eduard Zingerman wrote:
> On Fri, 2024-11-15 at 00:46 +0000, Anton Protopopov wrote:
>
> [...]
>
> > @@ -0,0 +1,374 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +
> > +#include <linux/btf.h>
> > +#include <sys/syscall.h>
> > +#include <bpf/bpf.h>
> > +
> > +static inline int _bpf_map_create(void)
> > +{
> > + static union bpf_attr attr = {
> > + .map_type = BPF_MAP_TYPE_ARRAY,
> > + .key_size = 4,
> > + .value_size = 8,
> > + .max_entries = 1,
> > + };
> > +
> > + return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
> > +}
> > +
> > +#define BTF_INFO_ENC(kind, kind_flag, vlen) \
> > + ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
> > +#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
> > +#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
> > + ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
> > +#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
> > + BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
> > + BTF_INT_ENC(encoding, bits_offset, bits)
>
> Nit: these macro are already defined in tools/testing/selftests/bpf/test_btf.h .
Ok, right, I will move them to some common place in v2.
> > +
> > +static int _btf_create(void)
> > +{
> > + struct btf_blob {
> > + struct btf_header btf_hdr;
> > + __u32 types[8];
> > + __u32 str;
> > + } raw_btf = {
> > + .btf_hdr = {
> > + .magic = BTF_MAGIC,
> > + .version = BTF_VERSION,
> > + .hdr_len = sizeof(struct btf_header),
> > + .type_len = sizeof(__u32) * 8,
> > + .str_off = sizeof(__u32) * 8,
> > + .str_len = sizeof(__u32),
>
> Nit: offsetof(struct btf_blob, str), sizeof(raw_btf.str), sizeof(raw_btf.types)
> are legal in this position.
Ok, thanks. (I've copy-pasted this part from some other test, will
change the similar code at the source as well then.)
> > + },
> > + .types = {
> > + /* long */
> > + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 64, 8), /* [1] */
> > + /* unsigned long */
> > + BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* [2] */
> > + },
> > + };
> > + static union bpf_attr attr = {
> > + .btf_size = sizeof(raw_btf),
> > + };
> > +
> > + attr.btf = (long)&raw_btf;
> > +
> > + return syscall(__NR_bpf, BPF_BTF_LOAD, &attr, sizeof(attr));
> > +}
>
> [...]
>
> > +static void check_fd_array_cnt__fd_array_ok(void)
> > +{
> > + int extra_fds[128];
> > + __u32 map_ids[16];
> > + __u32 nr_map_ids;
> > + int prog_fd;
> > +
> > + extra_fds[0] = _bpf_map_create();
> > + if (!ASSERT_GE(extra_fds[0], 0, "_bpf_map_create"))
> > + return;
> > + extra_fds[1] = _bpf_map_create();
> > + if (!ASSERT_GE(extra_fds[1], 0, "_bpf_map_create"))
> > + return;
> > + prog_fd = load_test_prog(extra_fds, 2);
> > + if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
> > + return;
> > + nr_map_ids = ARRAY_SIZE(map_ids);
> > + if (!check_expected_map_ids(prog_fd, 3, map_ids, &nr_map_ids))
>
> Nit: should probably close prog_fd and extra_fds (and in tests below).
Ah, thanks! I will also check the other tests in this file for the
same bugs.
>
> > + return;
> > +
> > + /* maps should still exist when original file descriptors are closed */
> > + close(extra_fds[0]);
> > + close(extra_fds[1]);
> > + if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map_ids[0] should exist"))
> > + return;
> > + if (!ASSERT_EQ(map_exists(map_ids[1]), true, "map_ids[1] should exist"))
> > + return;
> > +
> > + close(prog_fd);
> > +}
>
> [...]
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2024-11-17 21:27 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-15 0:46 [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 1/5] bpf: add a __btf_get_by_fd helper Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 2/5] bpf: move map/prog compatibility checks Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 3/5] bpf: add fd_array_cnt attribute for prog_load Anton Protopopov
2024-11-16 3:06 ` Eduard Zingerman
2024-11-17 21:24 ` Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 4/5] selftests/bpf: Add tests for fd_array_cnt Anton Protopopov
2024-11-16 3:06 ` Eduard Zingerman
2024-11-17 21:30 ` Anton Protopopov
2024-11-15 0:46 ` [PATCH bpf-next 5/5] bpf: fix potential error return Anton Protopopov
2024-11-16 3:06 ` [PATCH bpf-next 0/5] Add fd_array_cnt attribute for BPF_PROG_LOAD Eduard Zingerman
2024-11-17 21:22 ` Anton Protopopov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).