mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
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
137 lines
4.7 KiB
Go
137 lines
4.7 KiB
Go
// Copyright 2026 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package transport
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"gvisor.dev/gvisor/pkg/syserr"
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
|
)
|
|
|
|
// TestSCMSenderBeforeSaveIdempotent verifies that calling beforeSave
|
|
// on an SCMSender whose host file descriptor is already closed (fd < 0)
|
|
// is a safe no-op and does not panic in fdnotifier.RemoveFD.
|
|
func TestSCMSenderBeforeSaveIdempotent(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])
|
|
|
|
q := &waiter.Queue{}
|
|
ep, sysErr := NewSCMSender(fds[0], q, "test-addr")
|
|
if sysErr != nil {
|
|
unix.Close(fds[0])
|
|
t.Fatalf("NewSCMSender failed: %v", sysErr)
|
|
}
|
|
if err := ep.Init(); err != nil {
|
|
unix.Close(fds[0])
|
|
t.Fatalf("Init failed: %v", err)
|
|
}
|
|
|
|
// First beforeSave cleans up the host FD and sets ep.fd = -1.
|
|
ep.beforeSave()
|
|
if ep.fd != -1 {
|
|
t.Fatalf("after beforeSave, ep.fd = %d, want -1", ep.fd)
|
|
}
|
|
|
|
// Second beforeSave must be idempotent and must not panic on fdnotifier.RemoveFD(-1).
|
|
ep.beforeSave()
|
|
}
|
|
|
|
// TestHostSenderClosedHostReadiness verifies that HostReadiness reports
|
|
// hangup and readable events when the host socket is closed (fd < 0) so that pollers
|
|
// are notified of EOF / disconnection.
|
|
func TestHostSenderClosedHostReadiness(t *testing.T) {
|
|
c := &HostSender{fd: -1}
|
|
|
|
mask := waiter.ReadableEvents | waiter.EventHUp | waiter.EventRdHUp | waiter.WritableEvents
|
|
got := c.HostReadiness(mask)
|
|
want := (waiter.ReadableEvents | waiter.WritableEvents | waiter.EventHUp | waiter.EventRdHUp) & mask
|
|
if got != want {
|
|
t.Fatalf("HostReadiness(%v) when fd < 0 = %v, want %v", mask, got, want)
|
|
}
|
|
|
|
// A severed socket reports writable.
|
|
if gotOut := c.HostReadiness(waiter.EventOut); gotOut != waiter.EventOut {
|
|
t.Errorf("HostReadiness(EventOut) when fd < 0 = %v, want %v", gotOut, waiter.EventOut)
|
|
}
|
|
|
|
// Querying EventHUp explicitly must return EventHUp.
|
|
if gotHup := c.HostReadiness(waiter.EventHUp); gotHup != waiter.EventHUp {
|
|
t.Errorf("HostReadiness(EventHUp) when fd < 0 = %v, want %v", gotHup, waiter.EventHUp)
|
|
}
|
|
|
|
// Querying EventIn explicitly must return EventIn.
|
|
if gotIn := c.HostReadiness(waiter.EventIn); gotIn != waiter.EventIn {
|
|
t.Errorf("HostReadiness(EventIn) when fd < 0 = %v, want %v", gotIn, waiter.EventIn)
|
|
}
|
|
}
|
|
|
|
// TestConnectionedEndpointClosedHostReadiness verifies that connectionedEndpoint.Readiness
|
|
// properly propagates hangup and readable events when backed by a closed HostSender.
|
|
func TestConnectionedEndpointClosedHostReadiness(t *testing.T) {
|
|
c := &HostSender{fd: -1}
|
|
ep := &connectionedEndpoint{
|
|
baseEndpoint: baseEndpoint{
|
|
receiver: c,
|
|
peer: c,
|
|
},
|
|
}
|
|
|
|
mask := waiter.EventIn | waiter.EventHUp | waiter.EventRdHUp | waiter.EventOut
|
|
got := ep.Readiness(mask)
|
|
want := (waiter.ReadableEvents | waiter.WritableEvents | waiter.EventHUp | waiter.EventRdHUp) & mask
|
|
if got != want {
|
|
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)
|
|
}
|
|
}
|