mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Merge pull request #736 from tanjianfeng:fix-unix
PiperOrigin-RevId: 275114157
This commit is contained in:
@@ -385,3 +385,6 @@ func (c *ConnectedEndpoint) RecvMaxQueueSize() int64 {
|
||||
func (c *ConnectedEndpoint) Release() {
|
||||
c.ref.DecRefWithDestructor(c.close)
|
||||
}
|
||||
|
||||
// CloseUnread implements transport.ConnectedEndpoint.CloseUnread.
|
||||
func (c *ConnectedEndpoint) CloseUnread() {}
|
||||
|
||||
@@ -220,6 +220,11 @@ func (e *connectionedEndpoint) Close() {
|
||||
case e.Connected():
|
||||
e.connected.CloseSend()
|
||||
e.receiver.CloseRecv()
|
||||
// Still have unread data? If yes, we set this into the write
|
||||
// end so that the peer can get ECONNRESET) when it does read.
|
||||
if e.receiver.RecvQueuedSize() > 0 {
|
||||
e.connected.CloseUnread()
|
||||
}
|
||||
c = e.connected
|
||||
r = e.receiver
|
||||
e.connected = nil
|
||||
|
||||
@@ -33,6 +33,7 @@ type queue struct {
|
||||
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
closed bool
|
||||
unread bool
|
||||
used int64
|
||||
limit int64
|
||||
dataList messageList
|
||||
@@ -161,6 +162,9 @@ func (q *queue) Dequeue() (e *message, notify bool, err *syserr.Error) {
|
||||
err := syserr.ErrWouldBlock
|
||||
if q.closed {
|
||||
err = syserr.ErrClosedForReceive
|
||||
if q.unread {
|
||||
err = syserr.ErrConnectionReset
|
||||
}
|
||||
}
|
||||
q.mu.Unlock()
|
||||
|
||||
@@ -188,7 +192,9 @@ func (q *queue) Peek() (*message, *syserr.Error) {
|
||||
if q.dataList.Front() == nil {
|
||||
err := syserr.ErrWouldBlock
|
||||
if q.closed {
|
||||
err = syserr.ErrClosedForReceive
|
||||
if err = syserr.ErrClosedForReceive; q.unread {
|
||||
err = syserr.ErrConnectionReset
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -208,3 +214,11 @@ func (q *queue) QueuedSize() int64 {
|
||||
func (q *queue) MaxQueueSize() int64 {
|
||||
return q.limit
|
||||
}
|
||||
|
||||
// CloseUnread sets flag to indicate that the peer is closed (not shutdown)
|
||||
// with unread data. So if read on this queue shall return ECONNRESET error.
|
||||
func (q *queue) CloseUnread() {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
q.unread = true
|
||||
}
|
||||
|
||||
@@ -608,6 +608,10 @@ type ConnectedEndpoint interface {
|
||||
// Release releases any resources owned by the ConnectedEndpoint. It should
|
||||
// be called before droping all references to a ConnectedEndpoint.
|
||||
Release()
|
||||
|
||||
// CloseUnread sets the fact that this end is closed with unread data to
|
||||
// the peer socket.
|
||||
CloseUnread()
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
@@ -711,6 +715,11 @@ func (e *connectedEndpoint) Release() {
|
||||
e.writeQueue.DecRef()
|
||||
}
|
||||
|
||||
// CloseUnread implements ConnectedEndpoint.CloseUnread.
|
||||
func (e *connectedEndpoint) CloseUnread() {
|
||||
e.writeQueue.CloseUnread()
|
||||
}
|
||||
|
||||
// baseEndpoint is an embeddable unix endpoint base used in both the connected and connectionless
|
||||
// unix domain socket Endpoint implementations.
|
||||
//
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
@@ -44,6 +46,50 @@ 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));
|
||||
}
|
||||
|
||||
TEST_P(StreamUnixSocketPairTest, ReadOneSideClosedWithUnreadData) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
char buf[10] = {};
|
||||
ASSERT_THAT(RetryEINTR(write)(sockets->second_fd(), buf, sizeof(buf)),
|
||||
SyscallSucceedsWithValue(sizeof(buf)));
|
||||
|
||||
ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
|
||||
|
||||
ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)),
|
||||
SyscallSucceedsWithValue(0));
|
||||
|
||||
ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds());
|
||||
|
||||
ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)),
|
||||
SyscallFailsWithErrno(ECONNRESET));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AllUnixDomainSockets, StreamUnixSocketPairTest,
|
||||
::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>(
|
||||
|
||||
Reference in New Issue
Block a user