From: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
To: Jakub Kicinski <kuba@kernel.org>, willemdebruijn.kernel@gmail.com
Cc: Jakub Kicinski <kuba@kernel.org>,
netdev@vger.kernel.org, davem@davemloft.net,
edumazet@google.com, pabeni@redhat.com, horms@kernel.org,
paul@xen.org, wei.liu@kernel.org, mst@redhat.com,
axboe@kernel.dk, josef@toxicpanda.com, willemb@google.com
Subject: Re: [PATCH net-next] net: skbuff: replace ubuf_info ops ptr with type id
Date: Fri, 06 Nov 2026 16:31:01 -0500 [thread overview]
Message-ID: <willemdebruijn.kernel.10d143c35878@gmail.com> (raw)
In-Reply-To: <20261106212405.947546-1-kuba@kernel.org>
Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> net: skbuff: replace ubuf_info ops ptr with type id
>
> This commit replaces the ubuf_info_ops vtable pointer in struct ubuf_info
> with a u8 type ID. net_zcopy_complete() switches on that ID to pick one of
> the known completion callbacks, and callbacks that live in modules are
> registered through static_call. The io_uring link_skb callback becomes a
> direct call.
>
> > diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> > index 48f6764117243..3ec9851d0a10e 100644
> > --- a/net/core/skbuff.c
> > +++ b/net/core/skbuff.c
>
> [ ... ]
>
> > @@ -1898,10 +1900,72 @@ void msg_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref)
>
> [ ... ]
>
> > +void net_zcopy_complete(struct sk_buff *skb, struct ubuf_info *uarg,
> > + bool zerocopy_success)
> > +{
> > + switch (uarg->type) {
> > + case UBUF_TYPE_MSG_ZEROCOPY:
> > + msg_zerocopy_complete(skb, uarg, zerocopy_success);
> > + break;
>
> [Severity: High]
> Is the indirect call primitive described in the commit message still
> reachable through this path?
>
> The commit message says:
>
> Corrupting skb_shared_info gives a trivial one-hop indirect call
> primitive via uarg->ops->complete.
>
> Take the same threat model: an out-of-bounds write that overwrites
> destructor_arg and flags in skb_shared_info. A forged blob then needs only
> refcnt == 1, type == UBUF_TYPE_MSG_ZEROCOPY, mmp.user == NULL and len == 0.
> Completion goes:
>
> skb_zcopy_clear()
> net_zcopy_complete()
> msg_zerocopy_complete() /* refcount_dec_and_test() succeeds */
> __msg_zerocopy_callback()
>
> __msg_zerocopy_callback() derives the skb from the uarg address. A real
> msgzc uarg sits inside skb->cb, so the derived skb lands in the same
> forged region:
>
> struct sk_buff *tail, *skb = skb_from_uarg(uarg);
> struct sock_exterr_skb *serr;
> struct sock *sk = skb->sk;
> ...
> mm_unaccount_pinned_pages(&uarg->mmp);
> ...
> if (!uarg->len || sock_flag(sk, SOCK_DEAD))
> goto release;
> ...
> release:
> consume_skb(skb);
> sock_put(sk);
>
> With len == 0, sk is not dereferenced before consume_skb() runs on the
> forged skb. consume_skb() then goes through
> __kfree_skb()->skb_release_all()->skb_release_head_state(), which calls
> skb->destructor(skb).
>
> That destructor pointer comes from attacker memory at a fixed offset just
> past cb[]. sock_put(sk) only runs after the indirect call.
>
> So forging costs about the same as before: one controlled region at a
> known address plus a target. Before this patch, the same route could be
> reached by pointing a forged ops at &msg_zerocopy_ubuf_ops. Now the forged
> ops pointer is a single byte, and no data symbol address is needed.
>
> The other type IDs also hand forged memory to callbacks that trust
> container_of():
>
> UBUF_TYPE_TPACKET: tpacket_ubuf_complete() writes status through the
> forged tu->po and tu->ph, then calls sk_free(&po->sk).
>
> UBUF_TYPE_VHOST: vhost_zerocopy_complete() writes
> vq->heads[ubuf->desc].len through forged ctx and vq pointers.
>
> UBUF_TYPE_IO_URING: io_tx_ubuf_complete() queues task_work on a notif
> derived from forged memory.
>
> Would closing this need one of two changes? One is checking that
> destructor_arg points to a real object of the claimed type. The other is
> stopping msg_zerocopy completion from deriving and freeing an skb (and
> calling its destructor) from the uarg address.
Thanks. I was aware of the ability to forge the sk. But not of all paths.
This patch intends to fix one path, before moving on to the next.
Alternatively, I can try to send a single series that addresses all. It
probably offers little protection otherwise.
Though this also just removes an indirect function call from the hot
path, so arguably it can stand on its own for that too.
next prev parent reply other threads:[~2026-11-06 21:31 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 19:46 [PATCH net-next] net: skbuff: replace ubuf_info ops ptr with type id Willem de Bruijn
2026-11-06 21:24 ` Jakub Kicinski
2026-11-06 21:31 ` Willem de Bruijn [this message]
2026-11-06 22:50 ` patchwork-bot+netdevbpf
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=willemdebruijn.kernel.10d143c35878@gmail.com \
--to=willemdebruijn.kernel@gmail.com \
--cc=axboe@kernel.dk \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=josef@toxicpanda.com \
--cc=kuba@kernel.org \
--cc=mst@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=paul@xen.org \
--cc=wei.liu@kernel.org \
--cc=willemb@google.com \
/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