mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
unix: return zero if peer is closed
Previously, recvmsg() on a unix stream socket with its peer closed will
never return, with goroutine call trace like this:
...
2 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).block
at pkg/sentry/kernel/task_block.go:124
3 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).BlockWithDeadline
at pkg/sentry/kernel/task_block.go:69
4 in gvisor.dev/gvisor/pkg/sentry/socket/unix.(*SocketOperations).RecvMsg
at pkg/sentry/socket/unix/unix.go:612
5 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.recvFrom
at pkg/sentry/syscalls/linux/sys_socket.go:885
6 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.RecvFrom
at pkg/sentry/syscalls/linux/sys_socket.go:910
...
The issue is caused by that ErrClosedForReceive returned by
unix/transport.queue is turned into nil in
unix.(*EndpointReader).ReadToBlocks():
err.ToError()
As a result, in unix.(*SocketOperations).RecvMsg():
n == 0 and err == nil
We shall differentiate it from another case - no data to read where
ErrWouldBlock shall be returned; and return 0 immediately.
Fixes: #734
Reported-by: chenglang.hy <chenglang.hy@antfin.com>
Signed-off-by: Jianfeng Tan <henry.tjf@antfin.com>
This commit is contained in:
@@ -595,7 +595,8 @@ func (s *SocketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
|
||||
total += n
|
||||
}
|
||||
|
||||
if err != nil || !waitAll || isPacket || n >= dst.NumBytes() {
|
||||
streamPeerClosed := s.stype == linux.SOCK_STREAM && n == 0 && err == nil
|
||||
if err != nil || !waitAll || isPacket || n >= dst.NumBytes() || streamPeerClosed {
|
||||
if total > 0 {
|
||||
err = nil
|
||||
}
|
||||
|
||||
@@ -44,6 +44,32 @@ TEST_P(StreamUnixSocketPairTest, ReadOneSideClosed) {
|
||||
SyscallSucceedsWithValue(0));
|
||||
}
|
||||
|
||||
TEST_P(StreamUnixSocketPairTest, RecvmsgOneSideClosed) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
// Set timeout so that it will not wait for ever.
|
||||
struct timeval tv {
|
||||
.tv_sec = 0, .tv_usec = 10
|
||||
};
|
||||
EXPECT_THAT(
|
||||
setsockopt(sockets->second_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
|
||||
SyscallSucceeds());
|
||||
|
||||
ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds());
|
||||
|
||||
char received_data[10] = {};
|
||||
struct iovec iov;
|
||||
iov.iov_base = received_data;
|
||||
iov.iov_len = sizeof(received_data);
|
||||
struct msghdr msg = {};
|
||||
msg.msg_flags = -1;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
ASSERT_THAT(recvmsg(sockets->second_fd(), &msg, MSG_WAITALL),
|
||||
SyscallSucceedsWithValue(0));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AllUnixDomainSockets, StreamUnixSocketPairTest,
|
||||
::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>(
|
||||
|
||||
Reference in New Issue
Block a user