Merge pull request #5227 from cyphar/internal-cmsg-package

libct: move cmsg helpers to new internal/cmsg package
This commit is contained in:
Rodrigo Campos Catelin
2026-04-08 11:36:32 +02:00
committed by GitHub
10 changed files with 63 additions and 20 deletions
+2 -1
View File
@@ -25,6 +25,7 @@ import (
"google.golang.org/protobuf/proto"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/runc/internal/cmsg"
"github.com/opencontainers/runc/internal/pathrs"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/utils"
@@ -1193,7 +1194,7 @@ func (c *Container) criuNotifications(resp *criurpc.CriuResp, process *Process,
defer master.Close()
// While we can access console.master, using the API is a good idea.
if err := utils.SendFile(process.ConsoleSocket, master); err != nil {
if err := cmsg.SendFile(process.ConsoleSocket, master); err != nil {
return err
}
case "status-ready":
+3 -2
View File
@@ -21,6 +21,7 @@ import (
"golang.org/x/sys/unix"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/runc/internal/cmsg"
"github.com/opencontainers/runc/internal/linux"
"github.com/opencontainers/runc/internal/pathrs"
"github.com/opencontainers/runc/libcontainer/capabilities"
@@ -406,7 +407,7 @@ func setupConsole(socket *os.File, config *initConfig, mount bool) error {
}
}
// While we can access console.master, using the API is a good idea.
if err := utils.SendRawFd(socket, pty.Name(), pty.Fd()); err != nil {
if err := cmsg.SendRawFd(socket, pty.Name(), pty.Fd()); err != nil {
return err
}
runtime.KeepAlive(pty)
@@ -728,7 +729,7 @@ func setupPidfd(socket *os.File, initType string) error {
return fmt.Errorf("failed to pidfd_open: %w", err)
}
if err := utils.SendRawFd(socket, initType, uintptr(pidFd)); err != nil {
if err := cmsg.SendRawFd(socket, initType, uintptr(pidFd)); err != nil {
unix.Close(pidFd)
return fmt.Errorf("failed to send pidfd on socket: %w", err)
}
+4 -3
View File
@@ -11,12 +11,13 @@ import (
"testing"
"time"
"golang.org/x/sys/unix"
"github.com/containerd/console"
"github.com/opencontainers/runc/internal/cmsg"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/utils"
"golang.org/x/sys/unix"
)
func TestExecIn(t *testing.T) {
@@ -272,7 +273,7 @@ func TestExecInTTY(t *testing.T) {
done := make(chan (error))
go func() {
f, err := utils.RecvFile(parent)
f, err := cmsg.RecvFile(parent)
if err != nil {
done <- fmt.Errorf("RecvFile: %w", err)
return
+2 -1
View File
@@ -26,6 +26,7 @@ import (
"github.com/opencontainers/cgroups"
"github.com/opencontainers/cgroups/fs2"
"github.com/opencontainers/runc/internal/cmsg"
"github.com/opencontainers/runc/internal/linux"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/intelrdt"
@@ -1132,7 +1133,7 @@ func sendContainerProcessState(listenerPath string, state *specs.ContainerProces
return fmt.Errorf("cannot marshall seccomp state: %w", err)
}
if err := utils.SendRawFd(socket, string(b), file.Fd()); err != nil {
if err := cmsg.SendRawFd(socket, string(b), file.Fd()); err != nil {
return fmt.Errorf("cannot send seccomp fd to %s: %w", listenerPath, err)
}
runtime.KeepAlive(file)
+4 -4
View File
@@ -8,9 +8,9 @@ import (
"os"
"strconv"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/sirupsen/logrus"
"github.com/opencontainers/runc/internal/cmsg"
)
type syncType string
@@ -102,7 +102,7 @@ func doWriteSync(pipe *syncSocket, sync syncT) error {
}
if sync.Flags&syncFlagHasFd != 0 {
logrus.Debugf("writing sync file %s", sync)
if err := utils.SendFile(pipe.File(), sync.File); err != nil {
if err := cmsg.SendFile(pipe.File(), sync.File); err != nil {
return fmt.Errorf("sending file after sync %q: %w", sync.Type, err)
}
}
@@ -149,7 +149,7 @@ func doReadSync(pipe *syncSocket) (syncT, error) {
}
if sync.Flags&syncFlagHasFd != 0 {
logrus.Debugf("reading sync file %s", sync)
file, err := utils.RecvFile(pipe.File())
file, err := cmsg.RecvFile(pipe.File())
if err != nil {
return sync, fmt.Errorf("receiving fd from sync %v failed: %w", sync.Type, err)
}
-131
View File
@@ -1,131 +0,0 @@
package utils
/*
* Copyright 2016, 2017 SUSE LLC
*
* 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.
*/
import (
"fmt"
"os"
"runtime"
"github.com/opencontainers/runc/internal/linux"
"golang.org/x/sys/unix"
)
// MaxNameLen is the maximum length of the name of a file descriptor being sent
// using SendFile. The name of the file handle returned by RecvFile will never be
// larger than this value.
const MaxNameLen = 4096
// oobSpace is the size of the oob slice required to store a single FD. Note
// that unix.UnixRights appears to make the assumption that fd is always int32,
// so sizeof(fd) = 4.
var oobSpace = unix.CmsgSpace(4)
// RecvFile waits for a file descriptor to be sent over the given AF_UNIX
// socket. The file name of the remote file descriptor will be recreated
// locally (it is sent as non-auxiliary data in the same payload).
func RecvFile(socket *os.File) (_ *os.File, Err error) {
name := make([]byte, MaxNameLen)
oob := make([]byte, oobSpace)
sockfd := socket.Fd()
var (
n, oobn int
err error
)
for {
n, oobn, _, _, err = unix.Recvmsg(int(sockfd), name, oob, unix.MSG_CMSG_CLOEXEC)
if err != unix.EINTR {
break
}
}
if err != nil {
return nil, os.NewSyscallError("recvmsg", err)
}
if n >= MaxNameLen || oobn != oobSpace {
return nil, fmt.Errorf("recvfile: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
}
// Truncate.
name = name[:n]
oob = oob[:oobn]
scms, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return nil, err
}
// We cannot control how many SCM_RIGHTS we receive, and upon receiving
// them all of the descriptors are installed in our fd table, so we need to
// parse all of the SCM_RIGHTS we received in order to close all of the
// descriptors on error.
var fds []int
defer func() {
for i, fd := range fds {
if i == 0 && Err == nil {
// Only close the first one on error.
continue
}
// Always close extra ones.
_ = unix.Close(fd)
}
}()
var lastErr error
for _, scm := range scms {
if scm.Header.Type == unix.SCM_RIGHTS {
scmFds, err := unix.ParseUnixRights(&scm)
if err != nil {
lastErr = err
} else {
fds = append(fds, scmFds...)
}
}
}
if lastErr != nil {
return nil, lastErr
}
// We do this after collecting the fds to make sure we close them all when
// returning an error here.
if len(scms) != 1 {
return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
}
if len(fds) != 1 {
return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
}
return os.NewFile(uintptr(fds[0]), string(name)), nil
}
// SendFile sends a file over the given AF_UNIX socket. file.Name() is also
// included so that if the other end uses RecvFile, the file will have the same
// name information.
func SendFile(socket, file *os.File) error {
name := file.Name()
if len(name) >= MaxNameLen {
return fmt.Errorf("sendfd: filename too long: %s", name)
}
err := SendRawFd(socket, name, file.Fd())
runtime.KeepAlive(file)
return err
}
// SendRawFd sends a specific file descriptor over the given AF_UNIX socket.
func SendRawFd(socket *os.File, msg string, fd uintptr) error {
oob := unix.UnixRights(int(fd))
return linux.Sendmsg(int(socket.Fd()), []byte(msg), oob, nil, 0)
}
+35
View File
@@ -0,0 +1,35 @@
package utils
import (
"os"
"github.com/opencontainers/runc/internal/cmsg"
)
// RecvFile waits for a file descriptor to be sent over the given AF_UNIX
// socket. The file name of the remote file descriptor will be recreated
// locally (it is sent as non-auxiliary data in the same payload).
//
// Deprecated: This method is deprecated and has been moved to an internal
// package (see [cmsg.RecvFile]). It will be removed in runc 1.6.
func RecvFile(socket *os.File) (*os.File, error) {
return cmsg.RecvFile(socket)
}
// SendFile sends a file over the given AF_UNIX socket. file.Name() is also
// included so that if the other end uses RecvFile, the file will have the same
// name information.
//
// Deprecated: This method is deprecated and has been moved to an internal
// package (see [cmsg.SendFile]). It will be removed in runc 1.6.
func SendFile(socket, file *os.File) error {
return cmsg.SendFile(socket, file)
}
// SendRawFd sends a specific file descriptor over the given AF_UNIX socket.
//
// Deprecated: This method is deprecated and has been moved to an internal
// package (see [cmsg.SendRawFd]). It will be removed in runc 1.6.
func SendRawFd(socket *os.File, msg string, fd uintptr) error {
return cmsg.SendRawFd(socket, msg, fd)
}