From: Dudu Lu <phx0fer@gmail.com>
To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
eddyz87@gmail.com, memxor@gmail.com
Cc: netdev@vger.kernel.org, bpf@vger.kernel.org
Subject: [PATCH bpf] net: return page_pool frags to the pool in bpf_xdp_shrink_data()
Date: Wed, 21 Oct 2026 23:56:45 +0800 [thread overview]
Message-ID: <20261021155645.69120-1-phx0fer@gmail.com> (raw)
bpf_xdp_shrink_data() releases a fully-shrunk frag through
__xdp_return(netmem, mem_type, false, zc_frag), selecting the freer from
the rxq's mem type. Under generic XDP the rxq's xdp_rxq_info often has no
mem model registered (e.g. loopback), so mem_type stays
MEM_TYPE_PAGE_SHARED == 0 even though the frag was allocated from a page
pool by skb_pp_cow_data(). __xdp_return() then frees the page through
page_frag_free() instead of page_pool_put_full_netmem(), so the page is
returned to the buddy allocator while its page_pool accounting is still
live:
BUG: Bad page state in process A-146-S3_S1 pfn:4694b
page dumped because: page_pool leak
page_frag_free+0xf0/0x11c
__xdp_return+0x160/0x640
bpf_xdp_adjust_tail+0x5a4/0xbac
bpf_prog_run_generic_xdp+0x4a8/0x1204
Before falling back to __xdp_return(), try the page_pool path: if the
frag really is a page_pool page, napi_pp_put_page() recycles it through
the pool and we are done.
Fixes: 4dce1a0d7cf3 ("bpf: Support pulling non-linear xdp data")
Assisted-by: LLM
Signed-off-by: Dudu Lu <phx0fer@gmail.com>
---
Tested base
-----------
Detected on Linux 7.3-rc4 (commit 93f51579e7df), arm64 QEMU guest.
Re-checked against mainline f47d223627f8 (v7.3-341): the affected code
is unchanged between the two, so the bug is still present there.
Checked against linux-next next-20261021 (commit 9a482184d910): the
affected code is unchanged there as well, so the bug is still present in
linux-next too.
Reproducer status
-----------------
A reproducer was written and executed on an arm64 QEMU guest (generic XDP on
loopback); the kernel reported "BUG: Bad page state ... page_pool leak". The
reproducer source is not posted here per Documentation/process/security-bugs.rst;
it is available on request.
Triggering conditions
---------------------
The ability to load a generic-XDP program (CAP_BPF / CAP_NET_ADMIN) on a
device whose rxq has no mem model but whose skbs are page_pool-backed (loopback
via skb_cow_data_for_xdp()).
Notes
-----
Built and checkpatch-clean: this patch was applied on top of 7.3-rc4
(commit 93f51579e7df) and a full arm64 kernel (defconfig) was built with
it -- no new warnings or errors. scripts/checkpatch.pl reports 0 errors,
0 warnings.
net/core/filter.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/net/core/filter.c b/net/core/filter.c
index 70dc621672f2..94bc367cee12 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -36,6 +36,7 @@
#include <net/protocol.h>
#include <net/netlink.h>
#include <linux/skbuff.h>
+#include <linux/skbuff_ref.h>
#include <linux/skmsg.h>
#include <net/sock.h>
#include <net/flow_dissector.h>
@@ -4380,6 +4381,18 @@ static bool bpf_xdp_shrink_data(struct xdp_buff *xdp, skb_frag_t *frag,
}
if (release) {
+ /* The frag may be page_pool-backed even when the rxq's
+ * xdp_rxq_info carries no mem model (generic XDP on a
+ * driver that never registers one, e.g. loopback). In that
+ * case mem_type is MEM_TYPE_PAGE_SHARED and __xdp_return()
+ * would free the page through page_frag_free(), leaving the
+ * page_pool's accounting dangling -- "page_pool leak". Route
+ * page_pool pages back through the pool instead.
+ */
+ if (IS_ENABLED(CONFIG_PAGE_POOL) &&
+ mem_type == MEM_TYPE_PAGE_SHARED && napi_pp_put_page(netmem))
+ return release;
+
__xdp_return(netmem, mem_type, false, zc_frag);
} else {
if (!tail)
next reply other threads:[~2026-10-21 15:56 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 15:56 Dudu Lu [this message]
2026-10-21 16:32 ` [PATCH bpf] net: return page_pool frags to the pool in bpf_xdp_shrink_data() sashiko-bot
2026-10-21 16:59 ` bot+bpf-ci
2026-10-21 17:40 ` Alexei Starovoitov
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=20261021155645.69120-1-phx0fer@gmail.com \
--to=phx0fer@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=memxor@gmail.com \
--cc=netdev@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.