BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next 0/2] bpf: Preserve RCU pointer nullness after unlock
@ 2026-06-20 15:17 Yiyang Chen
  2026-06-20 15:17 ` [PATCH bpf-next 1/2] bpf: Preserve nullable RCU pointer state on unlock Yiyang Chen
  2026-06-20 15:17 ` [PATCH bpf-next 2/2] selftests/bpf: Cover nullable RCU pointer use after unlock Yiyang Chen
  0 siblings, 2 replies; 5+ messages in thread
From: Yiyang Chen @ 2026-06-20 15:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Yiyang Chen, John Fastabend, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, bpf,
	linux-kselftest, linux-kernel

bpf_rcu_read_unlock() converts MEM_RCU verifier registers to
PTR_UNTRUSTED, but currently clears PTR_MAYBE_NULL at the same time.

That loses the nullable state for BTF_TYPE_SAFE_RCU_OR_NULL fields such as
skb->sk. A program can read skb->sk while in an RCU read-side critical
section, unlock RCU, and then dereference the pointer directly without the
verifier requiring an explicit NULL check.

Patch 1 preserves PTR_MAYBE_NULL when removing MEM_RCU.
Patch 2 adds a focused regression test for the unchecked dereference and a
matched null-checked control.

Yiyang Chen (2):
  bpf: Preserve nullable RCU pointer state on unlock
  selftests/bpf: Cover nullable RCU pointer use after unlock

 kernel/bpf/verifier.c                         |  2 +-
 .../selftests/bpf/prog_tests/rcu_read_lock.c  | 17 ++++++++++++++++
 .../selftests/bpf/progs/rcu_read_lock.c       | 20 +++++++++++++++++++
 3 files changed, 38 insertions(+), 1 deletion(-)


base-commit: a975094bf98ca97be9146f9d3b5681a6f9cf5ce3
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH bpf-next 1/2] bpf: Preserve nullable RCU pointer state on unlock
  2026-06-20 15:17 [PATCH bpf-next 0/2] bpf: Preserve RCU pointer nullness after unlock Yiyang Chen
@ 2026-06-20 15:17 ` Yiyang Chen
  2026-06-20 17:44   ` Alexei Starovoitov
  2026-06-20 15:17 ` [PATCH bpf-next 2/2] selftests/bpf: Cover nullable RCU pointer use after unlock Yiyang Chen
  1 sibling, 1 reply; 5+ messages in thread
From: Yiyang Chen @ 2026-06-20 15:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Yiyang Chen, John Fastabend, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, bpf,
	linux-kselftest, linux-kernel

bpf_rcu_read_unlock() converts RCU-protected verifier registers to
untrusted pointers so that programs cannot keep using RCU-trusted
references after the read-side critical section ends.

That conversion also clears PTR_MAYBE_NULL. For fields from the
BTF_TYPE_SAFE_RCU_OR_NULL allowlist, such as skb->sk, the verifier records
MEM_RCU | PTR_MAYBE_NULL while inside the RCU read-side critical section.
Clearing both flags on unlock drops the nullable state and allows a direct
post-unlock BTF member load without an explicit NULL check.

Only clear MEM_RCU during RCU unlock invalidation. Preserve PTR_MAYBE_NULL
so normal nullable-pointer checks reject direct access, while an explicit
NULL check can still refine the pointer before use.

Fixes: 30ee9821f943 ("bpf: Allowlist few fields similar to __rcu tag.")
Signed-off-by: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
---
 kernel/bpf/verifier.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2abc79dbf..e53c4bfe4 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9001,7 +9001,7 @@ static void invalidate_rcu_protected_refs(struct bpf_verifier_env *env)
 
 	bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, stack, clear_mask, ({
 		if (reg->type & MEM_RCU) {
-			reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL);
+			reg->type &= ~MEM_RCU;
 			reg->type |= PTR_UNTRUSTED;
 		}
 	}));
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH bpf-next 2/2] selftests/bpf: Cover nullable RCU pointer use after unlock
  2026-06-20 15:17 [PATCH bpf-next 0/2] bpf: Preserve RCU pointer nullness after unlock Yiyang Chen
  2026-06-20 15:17 ` [PATCH bpf-next 1/2] bpf: Preserve nullable RCU pointer state on unlock Yiyang Chen
@ 2026-06-20 15:17 ` Yiyang Chen
  2026-06-20 15:32   ` sashiko-bot
  1 sibling, 1 reply; 5+ messages in thread
From: Yiyang Chen @ 2026-06-20 15:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Yiyang Chen, John Fastabend, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, bpf,
	linux-kselftest, linux-kernel

Add coverage for nullable BTF pointers that are read under
bpf_rcu_read_lock() and then used after bpf_rcu_read_unlock().

The unchecked skb->sk dereference should be rejected because the pointer
can still be NULL after it loses MEM_RCU trust. The matched control
performs an explicit NULL check after unlock and should keep loading
successfully.

Signed-off-by: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
---
 .../selftests/bpf/prog_tests/rcu_read_lock.c  | 17 ++++++++++++++++
 .../selftests/bpf/progs/rcu_read_lock.c       | 20 +++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c
index 246eb259c..be0317a47 100644
--- a/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c
+++ b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c
@@ -72,6 +72,20 @@ static void test_rcuptr_acquire(void)
 	rcu_read_lock__destroy(skel);
 }
 
+static void test_rcuptr_null_check(void)
+{
+	struct rcu_read_lock *skel;
+
+	skel = rcu_read_lock__open();
+	if (!ASSERT_OK_PTR(skel, "skel_open"))
+		return;
+
+	bpf_program__set_autoload(skel->progs.rcu_null_check_after_unlock, true);
+	ASSERT_OK(rcu_read_lock__load(skel), "skel_load");
+
+	rcu_read_lock__destroy(skel);
+}
+
 static const char * const inproper_region_tests[] = {
 	"miss_lock",
 	"no_lock",
@@ -113,6 +127,7 @@ static void test_inproper_region(void)
 static const char * const rcuptr_misuse_tests[] = {
 	"task_untrusted_rcuptr",
 	"cross_rcu_region",
+	"rcu_null_deref_after_unlock",
 };
 
 static void test_rcuptr_misuse(void)
@@ -150,6 +165,8 @@ void test_rcu_read_lock(void)
 		test_success();
 	if (test__start_subtest("rcuptr_acquire"))
 		test_rcuptr_acquire();
+	if (test__start_subtest("rcuptr_null_check"))
+		test_rcuptr_null_check();
 	if (test__start_subtest("negative_tests_inproper_region"))
 		test_inproper_region();
 	if (test__start_subtest("negative_tests_rcuptr_misuse"))
diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c
index b4e073168..b78542706 100644
--- a/tools/testing/selftests/bpf/progs/rcu_read_lock.c
+++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c
@@ -372,6 +372,26 @@ int cross_rcu_region(void *ctx)
 	return 0;
 }
 
+SEC("?tp_btf/net_dev_queue")
+int BPF_PROG(rcu_null_check_after_unlock, struct sk_buff *skb)
+{
+	bpf_rcu_read_lock();
+	bpf_rcu_read_unlock();
+
+	if (!skb->sk)
+		return 0;
+	return skb->sk->__sk_common.skc_daddr;
+}
+
+SEC("?tp_btf/net_dev_queue")
+int BPF_PROG(rcu_null_deref_after_unlock, struct sk_buff *skb)
+{
+	bpf_rcu_read_lock();
+	bpf_rcu_read_unlock();
+
+	return skb->sk->__sk_common.skc_daddr;
+}
+
 __noinline
 static int static_subprog(void *ctx)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next 2/2] selftests/bpf: Cover nullable RCU pointer use after unlock
  2026-06-20 15:17 ` [PATCH bpf-next 2/2] selftests/bpf: Cover nullable RCU pointer use after unlock Yiyang Chen
@ 2026-06-20 15:32   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-06-20 15:32 UTC (permalink / raw)
  To: Yiyang Chen; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The newly added tests fail to test `MEM_RCU` pointer use-after-unlock behavior because the pointer is not an `__rcu` pointer and is read outside the explicit RCU critical section.
--

commit 78986d2dc799ce86278ca6d6e66b17c43c68dbeb
Author: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>

selftests/bpf: Cover nullable RCU pointer use after unlock
    
Add coverage for nullable BTF pointers that are read under
bpf_rcu_read_lock() and then used after bpf_rcu_read_unlock().

> diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c
> index b4e073168fb15..b78542706fbce 100644
> --- a/tools/testing/selftests/bpf/progs/rcu_read_lock.c
> +++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c

[ ... ]

> @@ -372,6 +372,26 @@ int cross_rcu_region(void *ctx)
>  	return 0;
>  }
>  
> +SEC("?tp_btf/net_dev_queue")
> +int BPF_PROG(rcu_null_check_after_unlock, struct sk_buff *skb)
> +{
> +	bpf_rcu_read_lock();
> +	bpf_rcu_read_unlock();
> +
> +	if (!skb->sk)
> +		return 0;
> +	return skb->sk->__sk_common.skc_daddr;
> +}

[Severity: Medium]
Since the skb->sk pointer is loaded after the explicit bpf_rcu_read_unlock()
call, does this actually test the MEM_RCU trust behavior?

Because skb->sk is not an __rcu pointer and the load occurs completely outside
the RCU critical section, it appears the lock and unlock calls here are
isolated and have no effect on the pointer's trust state in the verifier.

> +
> +SEC("?tp_btf/net_dev_queue")
> +int BPF_PROG(rcu_null_deref_after_unlock, struct sk_buff *skb)
> +{
> +	bpf_rcu_read_lock();
> +	bpf_rcu_read_unlock();
> +
> +	return skb->sk->__sk_common.skc_daddr;
> +}

[Severity: Medium]
Is this negative test being rejected by the verifier for the wrong reason?

Because skb->sk is a standard PTR_MAYBE_NULL, the verifier will reject the
dereference due to the missing NULL check regardless of the RCU unlock.
Could this mask the absence of the intended RCU reference tracking coverage?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1781968391.git.chenyy23@mails.tsinghua.edu.cn?part=2

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Preserve nullable RCU pointer state on unlock
  2026-06-20 15:17 ` [PATCH bpf-next 1/2] bpf: Preserve nullable RCU pointer state on unlock Yiyang Chen
@ 2026-06-20 17:44   ` Alexei Starovoitov
  0 siblings, 0 replies; 5+ messages in thread
From: Alexei Starovoitov @ 2026-06-20 17:44 UTC (permalink / raw)
  To: Yiyang Chen, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: John Fastabend, Martin KaFai Lau, Song Liu, Yonghong Song,
	Jiri Olsa, Emil Tsalapatis, Shuah Khan, bpf, linux-kselftest,
	linux-kernel

On Sat Jun 20, 2026 at 8:17 AM PDT, Yiyang Chen wrote:
> bpf_rcu_read_unlock() converts RCU-protected verifier registers to
> untrusted pointers so that programs cannot keep using RCU-trusted
> references after the read-side critical section ends.
>
> That conversion also clears PTR_MAYBE_NULL. For fields from the
> BTF_TYPE_SAFE_RCU_OR_NULL allowlist, such as skb->sk, the verifier records
> MEM_RCU | PTR_MAYBE_NULL while inside the RCU read-side critical section.
> Clearing both flags on unlock drops the nullable state and allows a direct
> post-unlock BTF member load without an explicit NULL check.

That's exactly the point. The code works as designed.

> Only clear MEM_RCU during RCU unlock invalidation. Preserve PTR_MAYBE_NULL
> so normal nullable-pointer checks reject direct access, while an explicit
> NULL check can still refine the pointer before use.
>
> Fixes: 30ee9821f943 ("bpf: Allowlist few fields similar to __rcu tag.")

Nothing to fix.

pw-bot: cr

> Signed-off-by: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
> ---
>  kernel/bpf/verifier.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 2abc79dbf..e53c4bfe4 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -9001,7 +9001,7 @@ static void invalidate_rcu_protected_refs(struct bpf_verifier_env *env)
>  
>  	bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, stack, clear_mask, ({
>  		if (reg->type & MEM_RCU) {
> -			reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL);
> +			reg->type &= ~MEM_RCU;
>  			reg->type |= PTR_UNTRUSTED;
>  		}
>  	}));


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-06-20 17:44 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-20 15:17 [PATCH bpf-next 0/2] bpf: Preserve RCU pointer nullness after unlock Yiyang Chen
2026-06-20 15:17 ` [PATCH bpf-next 1/2] bpf: Preserve nullable RCU pointer state on unlock Yiyang Chen
2026-06-20 17:44   ` Alexei Starovoitov
2026-06-20 15:17 ` [PATCH bpf-next 2/2] selftests/bpf: Cover nullable RCU pointer use after unlock Yiyang Chen
2026-06-20 15:32   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox