[PATCH bpf 1/2] bpf, sockmap: Stop reporting EPIPE on zero-length skb redirect

From: Eli Skeggs via B4 Relay

Date: Sun Oct 25 2026 - 19:48:39 EST


From: Eli Skeggs <kernel@xxxxxxxxxxxxx>

A FIN can arrive on a packet with no data. In that case, tcp_read_skb()
produces an empty skb, and the attached BPF stream_verdict program may
choose to redirect the skb to another socket. Further data redirected to
that socket from any socket gets dropped, and the target socket starts
reporting EPIPE. A userspace application may see that EPIPE and decide
the socket is no longer usable. For a BPF-based proxy, this can break a
bidirectional stream, truncating the response instead of leaving the
socket half-open.

Prior to the introduction of tcp_read_skb(), tcp_read_sock() never
passed a zero-length skb to its actor, sk_psock_verdict_recv(). When a
redirected skb landed in sk_psock_backlog(), it was always non-empty.
tcp_read_skb() deviated from the historical behavior by passing any
zero-length skbs to recv_actor(), exposing this latent bug by adding
zero-length skbs to a queue that hadn't previously received them. When
sk_psock_backlog() encounters a zero-length skb, it interprets ret == 0
as a hard error, reports EPIPE, and clears the transmit flag.

Treat a return of 0 as success when there is no data to send, while
continuing to fail on a 0 return with residual bytes. The loop exits
without incident on an empty skb, and the skb gets cleaned up as usual.
On an ingress redirect, the empty message is still queued as before, but
the target socket no longer reports EPIPE.

Fixes: 965b57b469a5 ("net: Introduce a new proto_ops ->read_skb()")
Signed-off-by: Eli Skeggs <kernel@xxxxxxxxxxxxx>
---
net/core/skmsg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/core/skmsg.c b/net/core/skmsg.c
index df385a5a961e..f9b2e3486cfb 100644
--- a/net/core/skmsg.c
+++ b/net/core/skmsg.c
@@ -716,7 +716,7 @@ static void sk_psock_backlog(struct work_struct *work)
if (!sock_flag(psock->sk, SOCK_DEAD))
ret = sk_psock_handle_skb(psock, skb, off,
len, ingress);
- if (ret <= 0) {
+ if (ret < 0 || (!ret && len)) {
if (ret == -EAGAIN) {
sk_psock_skb_state(psock, state, len, off);
/* Restore redir info we cleared before */

--
2.56.0