unix/stream: repair SO_SNDTIMEO

The send operations are waiting on the peer's socket buffer, but we shall
use our timeout value.  Provide a test for that.

Reported by:		phk
Reviewed by:		asomers
Differential Revision:	https://reviews.freebsd.org/D53081
Fixes:			d157927807
This commit is contained in:
Gleb Smirnoff
2025-10-14 17:29:31 -07:00
parent c8359b3271
commit ead7219352
2 changed files with 45 additions and 2 deletions
+18 -2
View File
@@ -1069,6 +1069,21 @@ uipc_stream_sbspace(struct sockbuf *sb)
return (min(space, mbspace)); return (min(space, mbspace));
} }
/*
* UNIX version of generic sbwait() for writes. We wait on peer's receive
* buffer, using our timeout.
*/
static int
uipc_stream_sbwait(struct socket *so, sbintime_t timeo)
{
struct sockbuf *sb = &so->so_rcv;
SOCK_RECVBUF_LOCK_ASSERT(so);
sb->sb_flags |= SB_WAIT;
return (msleep_sbt(&sb->sb_acc, SOCK_RECVBUF_MTX(so), PSOCK | PCATCH,
"sbwait", timeo, 0, 0));
}
static int static int
uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr, uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr,
struct uio *uio0, struct mbuf *m, struct mbuf *c, int flags, struct uio *uio0, struct mbuf *m, struct mbuf *c, int flags,
@@ -1203,7 +1218,8 @@ uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr,
error = EWOULDBLOCK; error = EWOULDBLOCK;
goto out4; goto out4;
} }
if ((error = sbwait(so2, SO_RCV)) != 0) { if ((error = uipc_stream_sbwait(so2,
so->so_snd.sb_timeo)) != 0) {
SOCK_RECVBUF_UNLOCK(so2); SOCK_RECVBUF_UNLOCK(so2);
goto out4; goto out4;
} else } else
@@ -2397,7 +2413,7 @@ uipc_sendfile_wait(struct socket *so, off_t need, int *space)
} }
if (!sockref) if (!sockref)
soref(so2); soref(so2);
error = sbwait(so2, SO_RCV); error = uipc_stream_sbwait(so2, so->so_snd.sb_timeo);
if (error == 0 && if (error == 0 &&
__predict_false(sb->sb_state & SBS_CANTRCVMORE)) __predict_false(sb->sb_state & SBS_CANTRCVMORE))
error = EPIPE; error = EPIPE;
+27
View File
@@ -1,6 +1,7 @@
/*- /*-
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
* *
* Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
* Copyright (c) 2018 Alan Somers * Copyright (c) 2018 Alan Somers
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -30,6 +31,7 @@
#include <sys/event.h> #include <sys/event.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/un.h> #include <sys/un.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@@ -490,6 +492,30 @@ ATF_TC_BODY(ourshutdown_kevent, tc)
close(sv[1]); close(sv[1]);
} }
ATF_TC_WITHOUT_HEAD(SO_SNDTIMEO);
ATF_TC_BODY(SO_SNDTIMEO, tc)
{
struct timespec tp1, tp2, rtp, sleep = { .tv_nsec = 100000000 };
int sv[2];
char buf[10];
full_socketpair(sv);
ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDTIMEO,
&(struct timeval){ .tv_usec = sleep.tv_nsec / 1000 },
sizeof(struct timeval)));
ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC_PRECISE, &tp1));
ATF_REQUIRE_EQ(-1, send(sv[0], buf, sizeof(buf), 0));
ATF_REQUIRE(errno == EAGAIN);
ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC_PRECISE, &tp2));
timespecsub(&tp2, &tp1, &rtp);
ATF_REQUIRE(timespeccmp(&rtp, &sleep, >=));
ATF_REQUIRE_EQ(sizeof(buf), recv(sv[1], buf, sizeof(buf), 0));
ATF_REQUIRE_EQ(sizeof(buf), send(sv[0], buf, sizeof(buf), 0));
close(sv[0]);
close(sv[1]);
}
ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TCS(tp)
{ {
ATF_TP_ADD_TC(tp, getpeereid); ATF_TP_ADD_TC(tp, getpeereid);
@@ -506,6 +532,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, peershutdown_wakeup_poll); ATF_TP_ADD_TC(tp, peershutdown_wakeup_poll);
ATF_TP_ADD_TC(tp, peershutdown_wakeup_kevent); ATF_TP_ADD_TC(tp, peershutdown_wakeup_kevent);
ATF_TP_ADD_TC(tp, ourshutdown_kevent); ATF_TP_ADD_TC(tp, ourshutdown_kevent);
ATF_TP_ADD_TC(tp, SO_SNDTIMEO);
return atf_no_error(); return atf_no_error();
} }