diff --git a/lib/client/reexec/reexec.go b/lib/client/reexec/reexec.go index bd382e251d9..62703fbe360 100644 --- a/lib/client/reexec/reexec.go +++ b/lib/client/reexec/reexec.go @@ -99,6 +99,9 @@ func RunForkAuthenticate(ctx context.Context, params ForkAuthenticateParams) err disownR.Close() }() + // disownW and killR are going to stay alive until the defer or until the + // close after a successful start, so we can pass them to + // configureReexecForOS here signalFd, killFd := configureReexecForOS(cmd, disownW, killR) cmd.Args = append(cmd.Args, params.GetArgs(signalFd, killFd)...) cmd.Args[0] = os.Args[0] diff --git a/lib/client/reexec/reexec_darwin.go b/lib/client/reexec/reexec_darwin.go index f4ee34d3753..cc00506118e 100644 --- a/lib/client/reexec/reexec_darwin.go +++ b/lib/client/reexec/reexec_darwin.go @@ -33,7 +33,8 @@ func getExecutable() (string, error) { } // configureReexecForOS configures the command with files to inherit and -// os-specific tweaks. +// os-specific tweaks. The signaling files should be kept alive until the +// command is started, then the caller should close them. func configureReexecForOS(cmd *exec.Cmd, signal, kill *os.File) (signalFd, killFd uint64) { cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, diff --git a/lib/client/reexec/reexec_linux.go b/lib/client/reexec/reexec_linux.go index e5d843f4ce8..8a3704e289d 100644 --- a/lib/client/reexec/reexec_linux.go +++ b/lib/client/reexec/reexec_linux.go @@ -30,7 +30,8 @@ func getExecutable() (string, error) { } // configureReexecForOS configures the command with files to inherit and -// os-specific tweaks. +// os-specific tweaks. The signaling files should be kept alive until the +// command is started, then the caller should close them. func configureReexecForOS(cmd *exec.Cmd, signal, kill *os.File) (signalFd, killFd uint64) { cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, diff --git a/lib/client/reexec/reexec_windows.go b/lib/client/reexec/reexec_windows.go index be690592c4d..d048d24e575 100644 --- a/lib/client/reexec/reexec_windows.go +++ b/lib/client/reexec/reexec_windows.go @@ -21,7 +21,6 @@ package reexec import ( "os" "os/exec" - "runtime" "syscall" "github.com/gravitational/trace" @@ -34,10 +33,9 @@ func getExecutable() (string, error) { } // configureReexecForOS configures the command with files to inherit and -// os-specific tweaks. +// os-specific tweaks. The signaling files should be kept alive until the +// command is started, then the caller should close them. func configureReexecForOS(cmd *exec.Cmd, signal, kill *os.File) (signalFd, killFd uint64) { - // Prevent handle from being closed when signal is garbage collected. - runtime.SetFinalizer(signal, nil) cmd.SysProcAttr = &syscall.SysProcAttr{ AdditionalInheritedHandles: []syscall.Handle{ syscall.Handle(signal.Fd()), diff --git a/lib/utils/fanoutbuffer/buffer.go b/lib/utils/fanoutbuffer/buffer.go index 5b72e316ac6..36dba634cc0 100644 --- a/lib/utils/fanoutbuffer/buffer.go +++ b/lib/utils/fanoutbuffer/buffer.go @@ -396,6 +396,10 @@ func (c *Cursor[T]) closeLocked() { if c.closed { return } + // reset the finalizer that was set in (*Buffer).NewCursor, if it's still + // set - i.e. if this call is not the result of finalizeCursor but it's an + // explicit Close + runtime.SetFinalizer(c, nil) c.closed = true diff --git a/lib/utils/log/writer_finalizer.go b/lib/utils/log/writer_finalizer.go index b90de0e8e3f..a3b82929ac5 100644 --- a/lib/utils/log/writer_finalizer.go +++ b/lib/utils/log/writer_finalizer.go @@ -34,7 +34,7 @@ func NewWriterFinalizer[T io.WriteCloser](writer T) *WriterFinalizer[T] { wr := &WriterFinalizer[T]{ writer: writer, } - runtime.SetFinalizer(wr, (*WriterFinalizer[T]).Close) + runtime.AddCleanup(wr, func(writer T) { _ = writer.Close() }, writer) return wr }