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 <[email protected]>
---
 .../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


Reply via email to