mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Return EOF, not EBADF, when receiving on a severed host UDS
SCMSender.beforeSave disconnects the endpoint on save: it shuts down and closes the host fd, leaving fd = -1. Send already handles this state via its IsSendClosed check (beforeSave sets sendShutdown), so writes fail with EPIPE, but Recv had no guard and passed -1 to recvmsg(2), leaking EBADF to applications for an fd that is still valid in their fd table. Return ErrClosedForReceive instead, which the socket layer already surfaces as read() == 0, matching Linux EOF semantics for a socket whose read side is shut down and making a severed host endpoint behave like any socket whose peer has gone away. The guard keys on fd < 0 rather than IsRecvClosed: after a local SHUT_RD on a live fd, the host socket may still hold queued data that must be drained before EOF, and host recvmsg(2) handles that correctly. PiperOrigin-RevId: 976063849
This commit is contained in:
committed by
gVisor bot
parent
152c2f12d6
commit
d894fdaee6
@@ -277,6 +277,16 @@ func (c *HostSender) Recv(ctx context.Context, data [][]byte, args RecvArgs) (Re
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
if c.fd < 0 {
|
||||
// The host fd was severed (e.g. disconnected on save), so no
|
||||
// data will ever arrive: report EOF, as Linux does for a
|
||||
// read-shutdown socket. Passing -1 to recvmsg(2) would leak
|
||||
// EBADF to the application for an fd that it rightly considers
|
||||
// valid. Keying on recvShutdown instead would be wrong: a live
|
||||
// fd may still have host-queued data to drain after SHUT_RD.
|
||||
return RecvOutput{}, false, syserr.ErrClosedForReceive
|
||||
}
|
||||
|
||||
var cm unet.ControlMessage
|
||||
if args.NumRights > 0 {
|
||||
cm.EnableFDs(int(args.NumRights))
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
@@ -99,3 +100,37 @@ func TestConnectionedEndpointClosedHostReadiness(t *testing.T) {
|
||||
t.Fatalf("Readiness(%v) for closed host endpoint = %v, want %v", mask, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSeveredRecvSend verifies that an SCMSender whose host fd was severed by
|
||||
// beforeSave reports EOF on receive and EPIPE on send, never EBADF: the
|
||||
// application's fd is still valid even though the sentry's host fd is gone.
|
||||
func TestSeveredRecvSend(t *testing.T) {
|
||||
fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Socketpair failed: %v", err)
|
||||
}
|
||||
defer unix.Close(fds[1])
|
||||
|
||||
ep, serr := NewSCMSender(fds[0], &waiter.Queue{}, "test-addr")
|
||||
if serr != nil {
|
||||
unix.Close(fds[0])
|
||||
t.Fatalf("NewSCMSender failed: %v", serr)
|
||||
}
|
||||
if err := ep.Init(); err != nil {
|
||||
unix.Close(fds[0])
|
||||
t.Fatalf("Init failed: %v", err)
|
||||
}
|
||||
|
||||
ep.beforeSave()
|
||||
if ep.fd != -1 {
|
||||
t.Fatalf("after beforeSave, ep.fd = %d, want -1", ep.fd)
|
||||
}
|
||||
|
||||
buf := [][]byte{make([]byte, 8)}
|
||||
if _, _, serr := ep.Recv(nil, buf, RecvArgs{}); serr != syserr.ErrClosedForReceive {
|
||||
t.Errorf("Recv on severed endpoint returned %v, want %v", serr, syserr.ErrClosedForReceive)
|
||||
}
|
||||
if _, _, serr := ep.Send(nil, [][]byte{{1}}, ControlMessages{}, Address{}); serr != syserr.ErrClosedForSend {
|
||||
t.Errorf("Send on severed endpoint returned %v, want %v", serr, syserr.ErrClosedForSend)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user