* [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set
@ 2026-02-14 4:41 Chengkaitao
2026-02-14 4:41 ` [PATCH v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields Chengkaitao
2026-02-14 5:08 ` [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set bot+bpf-ci
0 siblings, 2 replies; 3+ messages in thread
From: Chengkaitao @ 2026-02-14 4:41 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
For the following scenario:
struct tree_node {
struct bpf_refcount ref;
struct bpf_rb_node node;
struct node_data __kptr * node_data;
u64 key;
};
This means node_data would have the type PTR_TO_BTF_ID | MEM_ALLOC |
NON_OWN_REF | MEM_RCU.
When traversing an rbtree using bpf_rbtree_left/right, if we need to
use bpf_kptr_xchg to read the __kptr pointer, we still need to follow
the remove-read-add sequence.
This patch allows us to use bpf_kptr_xchg to directly read the __kptr
pointer without any prior operations.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
Signed-off-by: Feng Yang <yangfeng@kylinos.cn>
---
kernel/bpf/verifier.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 3536a91ff8c7..5baa12af3a4e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9320,6 +9320,7 @@ static const struct bpf_reg_types percpu_btf_ptr_types = {
PTR_TO_BTF_ID | MEM_PERCPU,
PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU,
PTR_TO_BTF_ID | MEM_PERCPU | PTR_TRUSTED,
+ PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU,
}
};
static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
@@ -9491,6 +9492,7 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
case PTR_TO_BTF_ID | MEM_ALLOC:
case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
+ case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock &&
meta->func_id != BPF_FUNC_kptr_xchg) {
verifier_bug(env, "unimplemented handling of MEM_ALLOC");
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields.
2026-02-14 4:41 [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set Chengkaitao
@ 2026-02-14 4:41 ` Chengkaitao
2026-02-14 5:08 ` [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set bot+bpf-ci
1 sibling, 0 replies; 3+ messages in thread
From: Chengkaitao @ 2026-02-14 4:41 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
Allow bpf_kptr_xchg to directly operate on pointers marked with
NON_OWN_REF | MEM_RCU.
In the example demonstrated in this patch, as long as "struct
bpf_refcount ref" exists, the __kptr pointer is guaranteed to
carry the MEM_RCU flag. The ref member itself does not need to
be explicitly used.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
.../selftests/bpf/progs/rbtree_search_kptr.c | 123 ++++++++++++++++++
1 file changed, 123 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c b/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
index 069fc64b0167..610aae45e2dc 100644
--- a/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
+++ b/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
@@ -18,11 +18,21 @@ struct tree_node {
struct node_data __kptr * node_data;
};
+struct tree_node_ref {
+ struct bpf_refcount ref;
+ struct bpf_rb_node node;
+ u64 key;
+ struct node_data __kptr * node_data;
+};
+
#define private(name) SEC(".data." #name) __hidden __aligned(8)
private(A) struct bpf_rb_root root __contains(tree_node, node);
private(A) struct bpf_spin_lock lock;
+private(B) struct bpf_rb_root root_r __contains(tree_node_ref, node);
+private(B) struct bpf_spin_lock lock_r;
+
static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
{
struct tree_node *node_a, *node_b;
@@ -130,6 +140,119 @@ long rbtree_search_kptr(void *ctx)
return ret;
}
+static bool less_r(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct tree_node_ref *node_a, *node_b;
+
+ node_a = container_of(a, struct tree_node_ref, node);
+ node_b = container_of(b, struct tree_node_ref, node);
+
+ return node_a->key < node_b->key;
+}
+
+SEC("syscall")
+__retval(0)
+long rbtree_search_kptr_ref(void *ctx)
+{
+ struct tree_node_ref *tnode_r, *tnode_m;
+ struct bpf_rb_node *rb_n;
+ struct node_data __kptr * node_data;
+ int lookup_key = NR_NODES / 2;
+ int lookup_data = NR_NODES / 2;
+ int i, data, ret = 0;
+
+ for (i = 0; i < NR_NODES && can_loop; i++) {
+ tnode_r = bpf_obj_new(typeof(*tnode_r));
+ if (!tnode_r)
+ return __LINE__;
+
+ node_data = bpf_obj_new(typeof(*node_data));
+ if (!node_data) {
+ bpf_obj_drop(tnode_r);
+ return __LINE__;
+ }
+
+ tnode_r->key = i;
+ node_data->data = i;
+
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, node_data);
+ if (node_data)
+ bpf_obj_drop(node_data);
+
+ /* Unused reference */
+ tnode_m = bpf_refcount_acquire(tnode_r);
+ if (!tnode_m)
+ return __LINE__;
+
+ bpf_spin_lock(&lock_r);
+ bpf_rbtree_add(&root_r, &tnode_r->node, less_r);
+ bpf_spin_unlock(&lock_r);
+
+ bpf_obj_drop(tnode_m);
+ }
+
+ bpf_spin_lock(&lock_r);
+ rb_n = bpf_rbtree_root(&root_r);
+ while (rb_n && can_loop) {
+ tnode_r = container_of(rb_n, struct tree_node_ref, node);
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, NULL);
+ if (!node_data) {
+ ret = __LINE__;
+ goto fail;
+ }
+
+ data = node_data->data;
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, node_data);
+ if (node_data) {
+ bpf_spin_unlock(&lock_r);
+ bpf_obj_drop(node_data);
+ return __LINE__;
+ }
+
+ if (lookup_key == tnode_r->key) {
+ if (data == lookup_data)
+ break;
+
+ ret = __LINE__;
+ goto fail;
+ }
+
+ if (lookup_key < tnode_r->key)
+ rb_n = bpf_rbtree_left(&root_r, rb_n);
+ else
+ rb_n = bpf_rbtree_right(&root_r, rb_n);
+ }
+ bpf_spin_unlock(&lock_r);
+
+ while (can_loop) {
+ bpf_spin_lock(&lock_r);
+ rb_n = bpf_rbtree_first(&root_r);
+ if (!rb_n) {
+ bpf_spin_unlock(&lock_r);
+ return 0;
+ }
+
+ rb_n = bpf_rbtree_remove(&root_r, rb_n);
+ if (!rb_n) {
+ ret = __LINE__;
+ goto fail;
+ }
+ bpf_spin_unlock(&lock_r);
+
+ tnode_r = container_of(rb_n, struct tree_node_ref, node);
+
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, NULL);
+ if (node_data)
+ bpf_obj_drop(node_data);
+
+ bpf_obj_drop(tnode_r);
+ }
+
+ return 0;
+fail:
+ bpf_spin_unlock(&lock_r);
+ return ret;
+}
SEC("syscall")
__failure __msg("R1 type=scalar expected=map_value, ptr_, ptr_")
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set
2026-02-14 4:41 [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set Chengkaitao
2026-02-14 4:41 ` [PATCH v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields Chengkaitao
@ 2026-02-14 5:08 ` bot+bpf-ci
1 sibling, 0 replies; 3+ messages in thread
From: bot+bpf-ci @ 2026-02-14 5:08 UTC (permalink / raw)
To: pilgrimtao, ast, daniel, john.fastabend, andrii, martin.lau,
eddyz87, song, yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah,
yangfeng, alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, chengkaitao, ast, andrii,
daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3181 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -9300,6 +9300,7 @@
> PTR_TO_BTF_ID | MEM_PERCPU,
> PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU,
> PTR_TO_BTF_ID | MEM_PERCPU | PTR_TRUSTED,
> + PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU,
> }
> };
Was the new type added to the correct array? This hunk adds the entry
to percpu_btf_ptr_types, which backs ARG_PTR_TO_PERCPU_BTF_ID in
compatible_reg_types[]. That array is used by bpf_per_cpu_ptr and
bpf_this_cpu_ptr, not by bpf_kptr_xchg.
For bpf_kptr_xchg arg1 (ARG_KPTR_XCHG_DEST), the verifier looks up
kptr_xchg_dest_types instead:
kernel/bpf/verifier.c:check_reg_type() {
...
compatible = compatible_reg_types[base_type(arg_type)];
...
for (i = 0; i < ARRAY_SIZE(compatible->types); i++) {
expected = compatible->types[i];
...
if (type == expected)
goto found;
}
...
return -EACCES;
kptr_xchg_dest_types currently contains only:
static const struct bpf_reg_types kptr_xchg_dest_types = {
.types = {
PTR_TO_MAP_VALUE,
PTR_TO_BTF_ID | MEM_ALLOC,
PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
}
};
A register with PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU will
not match any of these entries, so check_reg_type() returns -EACCES
before ever reaching the found: label and the switch statement below.
It looks like the entry should be added to kptr_xchg_dest_types rather
than percpu_btf_ptr_types. Alexei made the same suggestion on v6 of
this series:
https://lore.kernel.org/bpf/CAADnVQJdwqeoqi+mPjQjmNbUqz2RxAjq-dVsGb7c_MCqpqmwhA@mail.gmail.com/
> @@ -9475,6 +9476,7 @@
> case PTR_TO_BTF_ID | MEM_ALLOC:
> case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
> case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
> + case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
> if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock &&
> meta->func_id != BPF_FUNC_kptr_xchg) {
This switch case is correct, but it can only be reached via the found:
label after the type-matching loop succeeds. Since the first hunk
adds the type to the wrong array, the loop never matches for
bpf_kptr_xchg and this case is unreachable for its intended purpose.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22011467184
AI-authorship-score: low
AI-authorship-explanation: The patch went through 7 revisions on the mailing list with human reviewer interaction, and the type of mistake (adding to the wrong array) is characteristic of human error in a codebase with many similarly structured type arrays.
issues-found: 1
issue-severity-score: medium
issue-severity-explanation: The new type was added to percpu_btf_ptr_types instead of kptr_xchg_dest_types, so the patch does not achieve its stated goal of allowing bpf_kptr_xchg on MEM_RCU pointers; there is no user-visible crash or security issue, but the feature is silently broken.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-02-14 5:08 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-14 4:41 [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set Chengkaitao
2026-02-14 4:41 ` [PATCH v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields Chengkaitao
2026-02-14 5:08 ` [PATCH v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set bot+bpf-ci
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox