From: bot+bpf-ci@kernel.org
To: blbllhy@gmail.com,ast@kernel.org,daniel@iogearbox.net,martin.lau@kernel.org
Cc: ameryhung@gmail.com,davem@davemloft.net,edumazet@google.com,kuba@kernel.org,pabeni@redhat.com,horms@kernel.org,wangfushuai@baidu.com,bestswngs@gmail.com,mattbobrowski@google.com,kees@kernel.org,menglong8.dong@gmail.com,bpf@vger.kernel.org,netdev@vger.kernel.org,linux-kernel@vger.kernel.org,AutonomousCodeSecurity@microsoft.com,xmei5@asu.edu,tgopinath@linux.microsoft.com,kys@microsoft.com,blbllhy@gmail.com,ast@kernel.org,andrii@kernel.org,daniel@iogearbox.net,martin.lau@kernel.org,eddyz87@gmail.com,yonghong.song@linux.dev,clm@meta.com,ihor.solodrai@linux.dev
Subject: Re: [PATCH bpf] bpf: Fix NULL pointer dereference in __bpf_sk_storage_map_seq_show
Date: Thu, 27 Aug 2026 05:49:53 +0000 (UTC) [thread overview]
Message-ID: <43efa71e1fcb4cee5edd4d2ed696f13b902159fd0ef31a75f90803cb79284eb1@mail.kernel.org> (raw)
In-Reply-To: <20260827051859.45511-1-blbllhy@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 4801 bytes --]
> diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
> index 1d295a8769fa..7228520aa2b6 100644
> --- a/net/core/bpf_sk_storage.c
> +++ b/net/core/bpf_sk_storage.c
> @@ -806,6 +806,8 @@ static int __bpf_sk_storage_map_seq_show(struct seq_file *seq,
> ctx.map = info->map;
> if (selem) {
> sk_storage = rcu_dereference(selem->local_storage);
> + if (!sk_storage)
> + return 0;
^
Should this return SEQ_SKIP instead of 0?
Returning 0 tells bpf_seq_read() that the object was shown, but it was
actually skipped. In kernel/bpf/bpf_iter.c bpf_seq_read():
err = seq->op->show(seq, p);
if (err > 0) {
/* object is skipped, decrease seq_num, so next
* valid object can reuse the same seq_num.
*/
bpf_iter_dec_seq_num(seq);
seq->count = offs;
} else if (err < 0 || seq_has_overflowed(seq)) {
With return 0, bpf_iter_dec_seq_num() is not called, so the skipped
element consumes a seq_num that no BPF program invocation ever observes.
If the skipped element is the first one returned by
bpf_sk_storage_map_seq_start(), seq_num 0 is consumed with no program
run, the next valid element is shown with ctx->meta->seq_num == 1, and
iterator programs that emit a header on `ctx->meta->seq_num == 0`
silently lose their header. Examples that would break:
tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c:
if (ctx->meta->seq_num == 0)
BPF_SEQ_PRINTF(seq, " id refcnt ...\n");
tools/testing/selftests/bpf/progs/bpf_iter_tcp4.c:
if (ctx->meta->seq_num == 0)
BPF_SEQ_PRINTF(seq, " sl local_address ...\n");
For later elements the observable effect is a gap in the seq_num
sequence.
The other socket iterators handle this exact situation (object found
stale after handed to ->show()) by returning SEQ_SKIP:
net/ipv4/tcp_ipv4.c bpf_iter_tcp_seq_show():
if (unlikely(sk_unhashed(sk))) {
ret = SEQ_SKIP;
goto unlock;
}
net/ipv4/udp.c bpf_iter_udp_seq_show():
if (unlikely(sk_unhashed(sk))) {
ret = SEQ_SKIP;
goto unlock;
}
net/unix/af_unix.c bpf_iter_unix_seq_show():
ret = SEQ_SKIP;
Returning SEQ_SKIP is safe for the first object too: seq_num is u64
and bpf_iter_dec_seq_num()/bpf_iter_inc_seq_num() in
kernel/bpf/bpf_iter.c wrap back to 0 before the next ->show().
---
A subsystem pattern flags the following as potentially concerning:
> sk_storage = rcu_dereference(selem->local_storage);
> + if (!sk_storage)
> + return 0;
> ctx.sk = sk_storage->owner;
> ctx.value = SDATA(selem)->data;
The fix adds a single check-based abort path for a race that has no
exclusion mechanism, and the instructions that follow the abort point
still consume the contested selem.
Looking at bpf_selem_unlink_nofail() in kernel/bpf/bpf_local_storage.c,
it tears the selem down in this order:
1. b->lock: hlist_del_init_rcu(&selem->map_node);
bpf_obj_free_fields(smap->map.record, SDATA(selem)->data);
2. RCU_INIT_POINTER(SDATA(selem)->smap, NULL);
3. local_storage->lock: hlist_del_init_rcu(&selem->snode);
4. RCU_INIT_POINTER(selem->local_storage, NULL); <- new check sees this
A concurrent iterator that re-reads selem->local_storage between steps
1 and 4 still sees a non-NULL pointer and proceeds to:
ctx.sk = sk_storage->owner;
/* owner is deliberately NOT cleared in the destroy path:
* "Okay to skip clearing owner_storage and storage->owner
* in destroy() since the owner is going away" - so ctx.sk
* can be a sock already inside __sk_destruct() */
ctx.value = SDATA(selem)->data;
/* bpf_obj_free_fields() has already released the value's
* special fields (kptr/timer/etc.) at step 1 */
So the NULL check closes the specific general-protection fault in the
report (sk_storage == NULL, offset 0x88 == offsetof(struct
bpf_local_storage, owner), matching the KASAN address
0xdffffc0000000011) but does not make the iteration race-safe; it
narrows the window rather than closing it.
Worth confirming with the local-storage maintainers whether the
iterator needs to observe selem->state / SDATA(selem)->smap (or take
the same exclusion bpf_selem_unlink_nofail uses) rather than only
local_storage?
The residual window is not introduced by this patch - the same two
lines executed unconditionally before it, so relative to the parent
commit the change is a strict improvement. Practical impact of the
residual window is also limited: ctx.sk is registered
PTR_TO_BTF_ID_OR_NULL so programs must NULL-check it, and ctx.value is
PTR_TO_BUF, so a BPF program cannot dereference a released kptr read
out of it without a kfunc.
---
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/33042447773
next prev parent reply other threads:[~2026-08-27 5:49 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-27 5:18 [PATCH bpf] bpf: Fix NULL pointer dereference in __bpf_sk_storage_map_seq_show Cen Zhang (Microsoft Security FORGE Labs)
2026-08-27 5:49 ` bot+bpf-ci [this message]
2026-08-27 10:41 ` Amery Hung
2026-09-11 7:45 ` Xuanqiang Luo
2026-09-11 13:57 ` Cen Zhang (Microsoft Security FORGE Labs)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=43efa71e1fcb4cee5edd4d2ed696f13b902159fd0ef31a75f90803cb79284eb1@mail.kernel.org \
--to=bot+bpf-ci@kernel.org \
--cc=AutonomousCodeSecurity@microsoft.com \
--cc=ameryhung@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bestswngs@gmail.com \
--cc=blbllhy@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=clm@meta.com \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=eddyz87@gmail.com \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=ihor.solodrai@linux.dev \
--cc=kees@kernel.org \
--cc=kuba@kernel.org \
--cc=kys@microsoft.com \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@kernel.org \
--cc=mattbobrowski@google.com \
--cc=menglong8.dong@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=tgopinath@linux.microsoft.com \
--cc=wangfushuai@baidu.com \
--cc=xmei5@asu.edu \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox