Re: [PATCH net] tcp: reject devmem tx with fastopen and repair
From: Kaifeng Wang
Date: Thu Oct 08 2026 - 18:04:36 EST
> Is it enough to check tp->repair only once, at the top of the function?
Agreed. As Stan and Pavel suggested, I will move the `if (binding)`
checks directly into the Fast Open and `tp->repair` branches in v2.
pw-bot: cr
On Thu, Oct 8, 2026 at 5:54 AM Pavel Begunkov <asml.silence@xxxxxxxxx> wrote:
>
> On 10/5/26 22:40, Kaifeng Wang wrote:
> > tcp_sendmsg_locked() enforces that devmem TX can only proceed if the
> > zero-copy path is active and a valid dmabuf binding exists. However,
> > subsequent branches in tcp_sendmsg_locked() can still intercept the
> > message before it reaches the devmem zero-copy loop:
> >
> > 1. TCP Fast Open (MSG_FASTOPEN or DEFER_CONNECT):
> > If TCP_FASTOPEN_CONNECT is set, the socket may have a valid dst with
> > NETIF_F_SG (so zc == MSG_ZEROCOPY and binding is present), but
> > tcp_sendmsg_fastopen() -> tcp_send_syn_data() will use
> > copy_page_from_iter() to byte-copy from the iterator. Since iov_base
> > represents dma-buf offsets rather than user virtual addresses, this
> > misinterprets offsets as user pointers and copies arbitrary user memory
> > into the SYN packet.
> >
> > 2. TCP repair mode:
> > If tp->repair is enabled with TCP_RECV_QUEUE, tcp_send_rcvq() similarly
> > calls skb_copy_datagram_from_iter(), byte-copying from the iterator.
> >
> > Neither path supports or makes sense for devmem transmission. Reject devmem
> > sends if Fast Open or repair mode is active.
> >
> > This pre-existing issue was identified by Sashiko AI review on commit
> > 125755776bc6 ("tcp: reject non zerocopy devmem tx") and has not been
> > hit in production.
> >
> > Fixes: 125755776bc6 ("tcp: reject non zerocopy devmem tx")
> > Fixes: bd61848900bf ("net: devmem: Implement TX path")
> > Signed-off-by: Kaifeng Wang <kaifengw@xxxxxxxxxx>
> > ---
> > net/ipv4/tcp.c | 4 +++-
> > 1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> > index 87ef6d5cbfeb..225e758c194a 100644
> > --- a/net/ipv4/tcp.c
> > +++ b/net/ipv4/tcp.c
> > @@ -1169,7 +1169,9 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
> > zc = MSG_SPLICE_PAGES;
> > }
> >
> > - if (!sockc_err && sockc.dmabuf_id && (zc != MSG_ZEROCOPY || !binding)) {
> > + if (!sockc_err && sockc.dmabuf_id &&
> > + (zc != MSG_ZEROCOPY || !binding || tp->repair ||
> > + (flags & MSG_FASTOPEN) || inet_test_bit(DEFER_CONNECT, sk))) {
>
> It becomes unhandy, and taking into account the issue Stan commented
> about, I think it'd make sense to do the checks under the repair and
> fast open "ifs".
>
> if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) &&
> !tcp_passive_fastopen(sk)) {
> if (binding) // fail;
> }
>
> if (unlikely(tp->repair)) {
> if (binding) // fail;
> }
>
> --
> Pavel Begunkov
>