mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
@@ -23,6 +23,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
type scmRights struct {
|
||||
fds []int
|
||||
}
|
||||
@@ -32,8 +34,6 @@ func newSCMRights(fds []int) control.SCMRights {
|
||||
}
|
||||
|
||||
// Files implements control.SCMRights.Files.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/2017): Port to VFS2.
|
||||
func (c *scmRights) Files(ctx context.Context, max int) (control.RightsFiles, bool) {
|
||||
n := max
|
||||
var trunc bool
|
||||
@@ -93,3 +93,5 @@ func fdsToFiles(ctx context.Context, fds []int) []*fs.File {
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../fsimpl/host/control.go)
|
||||
|
||||
@@ -5,6 +5,7 @@ licenses(["notice"])
|
||||
go_library(
|
||||
name = "host",
|
||||
srcs = [
|
||||
"control.go",
|
||||
"host.go",
|
||||
"ioctl_unsafe.go",
|
||||
"tty.go",
|
||||
@@ -23,6 +24,8 @@ go_library(
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/socket/control",
|
||||
"//pkg/sentry/socket/unix/transport",
|
||||
"//pkg/sentry/unimpl",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2020 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 host
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/control"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
type scmRights struct {
|
||||
fds []int
|
||||
}
|
||||
|
||||
func newSCMRights(fds []int) control.SCMRightsVFS2 {
|
||||
return &scmRights{fds}
|
||||
}
|
||||
|
||||
// Files implements control.SCMRights.Files.
|
||||
func (c *scmRights) Files(ctx context.Context, max int) (control.RightsFilesVFS2, bool) {
|
||||
n := max
|
||||
var trunc bool
|
||||
if l := len(c.fds); n > l {
|
||||
n = l
|
||||
} else if n < l {
|
||||
trunc = true
|
||||
}
|
||||
|
||||
rf := control.RightsFilesVFS2(fdsToFiles(ctx, c.fds[:n]))
|
||||
|
||||
// Only consume converted FDs (fdsToFiles may convert fewer than n FDs).
|
||||
c.fds = c.fds[len(rf):]
|
||||
return rf, trunc
|
||||
}
|
||||
|
||||
// Clone implements transport.RightsControlMessage.Clone.
|
||||
func (c *scmRights) Clone() transport.RightsControlMessage {
|
||||
// Host rights never need to be cloned.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release implements transport.RightsControlMessage.Release.
|
||||
func (c *scmRights) Release() {
|
||||
for _, fd := range c.fds {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
c.fds = nil
|
||||
}
|
||||
|
||||
// If an error is encountered, only files created before the error will be
|
||||
// returned. This is what Linux does.
|
||||
func fdsToFiles(ctx context.Context, fds []int) []*vfs.FileDescription {
|
||||
files := make([]*vfs.FileDescription, 0, len(fds))
|
||||
for _, fd := range fds {
|
||||
// Get flags. We do it here because they may be modified
|
||||
// by subsequent functions.
|
||||
fileFlags, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_GETFL, 0)
|
||||
if errno != 0 {
|
||||
ctx.Warningf("Error retrieving host FD flags: %v", error(errno))
|
||||
break
|
||||
}
|
||||
|
||||
// Create the file backed by hostFD.
|
||||
file, err := ImportFD(ctx, kernel.KernelFromContext(ctx).HostMount(), fd, false /* isTTY */)
|
||||
if err != nil {
|
||||
ctx.Warningf("Error creating file from host FD: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
if err := file.SetStatusFlags(ctx, auth.CredentialsFromContext(ctx), uint32(fileFlags&linux.O_NONBLOCK)); err != nil {
|
||||
ctx.Warningf("Error setting flags on host FD file: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
files = append(files, file)
|
||||
}
|
||||
return files
|
||||
}
|
||||
@@ -4,7 +4,10 @@ package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "control",
|
||||
srcs = ["control.go"],
|
||||
srcs = [
|
||||
"control.go",
|
||||
"control_vfs2.go",
|
||||
],
|
||||
imports = [
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs",
|
||||
],
|
||||
@@ -18,6 +21,7 @@ go_library(
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/socket",
|
||||
"//pkg/sentry/socket/unix/transport",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/usermem",
|
||||
|
||||
@@ -41,6 +41,8 @@ type SCMCredentials interface {
|
||||
Credentials(t *kernel.Task) (kernel.ThreadID, auth.UID, auth.GID)
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// SCMRights represents a SCM_RIGHTS socket control message.
|
||||
type SCMRights interface {
|
||||
transport.RightsControlMessage
|
||||
@@ -142,6 +144,8 @@ func PackRights(t *kernel.Task, rights SCMRights, cloexec bool, buf []byte, flag
|
||||
return putCmsg(buf, flags, linux.SCM_RIGHTS, align, fds)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(./control_vfs2.go)
|
||||
|
||||
// scmCredentials represents an SCM_CREDENTIALS socket control message.
|
||||
//
|
||||
// +stateify savable
|
||||
@@ -537,11 +541,19 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (socket.Con
|
||||
}
|
||||
|
||||
if len(fds) > 0 {
|
||||
rights, err := NewSCMRights(t, fds)
|
||||
if err != nil {
|
||||
return socket.ControlMessages{}, err
|
||||
if kernel.VFS2Enabled {
|
||||
rights, err := NewSCMRightsVFS2(t, fds)
|
||||
if err != nil {
|
||||
return socket.ControlMessages{}, err
|
||||
}
|
||||
cmsgs.Unix.Rights = rights
|
||||
} else {
|
||||
rights, err := NewSCMRights(t, fds)
|
||||
if err != nil {
|
||||
return socket.ControlMessages{}, err
|
||||
}
|
||||
cmsgs.Unix.Rights = rights
|
||||
}
|
||||
cmsgs.Unix.Rights = rights
|
||||
}
|
||||
|
||||
return cmsgs, nil
|
||||
@@ -566,6 +578,8 @@ func MakeCreds(t *kernel.Task) SCMCredentials {
|
||||
return &scmCredentials{t, tcred.EffectiveKUID, tcred.EffectiveKGID}
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// New creates default control messages if needed.
|
||||
func New(t *kernel.Task, socketOrEndpoint interface{}, rights SCMRights) transport.ControlMessages {
|
||||
return transport.ControlMessages{
|
||||
@@ -573,3 +587,5 @@ func New(t *kernel.Task, socketOrEndpoint interface{}, rights SCMRights) transpo
|
||||
Rights: rights,
|
||||
}
|
||||
}
|
||||
|
||||
// LINT.ThenChange(./control_vfs2.go)
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2020 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 control
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// SCMRightsVFS2 represents a SCM_RIGHTS socket control message.
|
||||
type SCMRightsVFS2 interface {
|
||||
transport.RightsControlMessage
|
||||
|
||||
// Files returns up to max RightsFiles.
|
||||
//
|
||||
// Returned files are consumed and ownership is transferred to the caller.
|
||||
// Subsequent calls to Files will return the next files.
|
||||
Files(ctx context.Context, max int) (rf RightsFilesVFS2, truncated bool)
|
||||
}
|
||||
|
||||
// RightsFiles represents a SCM_RIGHTS socket control message. A reference is
|
||||
// maintained for each vfs.FileDescription and is release either when an FD is created or
|
||||
// when the Release method is called.
|
||||
type RightsFilesVFS2 []*vfs.FileDescription
|
||||
|
||||
// NewSCMRightsVFS2 creates a new SCM_RIGHTS socket control message
|
||||
// representation using local sentry FDs.
|
||||
func NewSCMRightsVFS2(t *kernel.Task, fds []int32) (SCMRightsVFS2, error) {
|
||||
files := make(RightsFilesVFS2, 0, len(fds))
|
||||
for _, fd := range fds {
|
||||
file := t.GetFileVFS2(fd)
|
||||
if file == nil {
|
||||
files.Release()
|
||||
return nil, syserror.EBADF
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
return &files, nil
|
||||
}
|
||||
|
||||
// Files implements SCMRights.Files.
|
||||
func (fs *RightsFilesVFS2) Files(ctx context.Context, max int) (RightsFilesVFS2, bool) {
|
||||
n := max
|
||||
var trunc bool
|
||||
if l := len(*fs); n > l {
|
||||
n = l
|
||||
} else if n < l {
|
||||
trunc = true
|
||||
}
|
||||
rf := (*fs)[:n]
|
||||
*fs = (*fs)[n:]
|
||||
return rf, trunc
|
||||
}
|
||||
|
||||
// Clone implements transport.RightsControlMessage.Clone.
|
||||
func (fs *RightsFilesVFS2) Clone() transport.RightsControlMessage {
|
||||
nfs := append(RightsFilesVFS2(nil), *fs...)
|
||||
for _, nf := range nfs {
|
||||
nf.IncRef()
|
||||
}
|
||||
return &nfs
|
||||
}
|
||||
|
||||
// Release implements transport.RightsControlMessage.Release.
|
||||
func (fs *RightsFilesVFS2) Release() {
|
||||
for _, f := range *fs {
|
||||
f.DecRef()
|
||||
}
|
||||
*fs = nil
|
||||
}
|
||||
|
||||
// rightsFDsVFS2 gets up to the specified maximum number of FDs.
|
||||
func rightsFDsVFS2(t *kernel.Task, rights SCMRightsVFS2, cloexec bool, max int) ([]int32, bool) {
|
||||
files, trunc := rights.Files(t, max)
|
||||
fds := make([]int32, 0, len(files))
|
||||
for i := 0; i < max && len(files) > 0; i++ {
|
||||
fd, err := t.NewFDFromVFS2(0, files[0], kernel.FDFlags{
|
||||
CloseOnExec: cloexec,
|
||||
})
|
||||
files[0].DecRef()
|
||||
files = files[1:]
|
||||
if err != nil {
|
||||
t.Warningf("Error inserting FD: %v", err)
|
||||
// This is what Linux does.
|
||||
break
|
||||
}
|
||||
|
||||
fds = append(fds, int32(fd))
|
||||
}
|
||||
return fds, trunc
|
||||
}
|
||||
|
||||
// PackRightsVFS2 packs as many FDs as will fit into the unused capacity of buf.
|
||||
func PackRightsVFS2(t *kernel.Task, rights SCMRightsVFS2, cloexec bool, buf []byte, flags int) ([]byte, int) {
|
||||
maxFDs := (cap(buf) - len(buf) - linux.SizeOfControlMessageHeader) / 4
|
||||
// Linux does not return any FDs if none fit.
|
||||
if maxFDs <= 0 {
|
||||
flags |= linux.MSG_CTRUNC
|
||||
return buf, flags
|
||||
}
|
||||
fds, trunc := rightsFDsVFS2(t, rights, cloexec, maxFDs)
|
||||
if trunc {
|
||||
flags |= linux.MSG_CTRUNC
|
||||
}
|
||||
align := t.Arch().Width()
|
||||
return putCmsg(buf, flags, linux.SCM_RIGHTS, align, fds)
|
||||
}
|
||||
|
||||
// NewVFS2 creates default control messages if needed.
|
||||
func NewVFS2(t *kernel.Task, socketOrEndpoint interface{}, rights SCMRightsVFS2) transport.ControlMessages {
|
||||
return transport.ControlMessages{
|
||||
Credentials: makeCreds(t, socketOrEndpoint),
|
||||
Rights: rights,
|
||||
}
|
||||
}
|
||||
@@ -804,7 +804,7 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr usermem.Addr, fla
|
||||
}
|
||||
|
||||
if cms.Unix.Rights != nil {
|
||||
controlData, mflags = control.PackRights(t, cms.Unix.Rights.(control.SCMRights), flags&linux.MSG_CMSG_CLOEXEC != 0, controlData, mflags)
|
||||
controlData, mflags = control.PackRightsVFS2(t, cms.Unix.Rights.(control.SCMRightsVFS2), flags&linux.MSG_CMSG_CLOEXEC != 0, controlData, mflags)
|
||||
}
|
||||
|
||||
// Copy the address to the caller.
|
||||
|
||||
Reference in New Issue
Block a user