mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Update p9 to support flipcall.
PiperOrigin-RevId: 268845090
This commit is contained in:
committed by
gVisor bot
parent
7c6ab6a219
commit
a8834fc555
@@ -20,11 +20,14 @@ go_library(
|
||||
"pool.go",
|
||||
"server.go",
|
||||
"transport.go",
|
||||
"transport_flipcall.go",
|
||||
"version.go",
|
||||
],
|
||||
importpath = "gvisor.dev/gvisor/pkg/p9",
|
||||
deps = [
|
||||
"//pkg/fd",
|
||||
"//pkg/fdchannel",
|
||||
"//pkg/flipcall",
|
||||
"//pkg/log",
|
||||
"//pkg/unet",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
|
||||
+263
-17
@@ -20,6 +20,8 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/flipcall"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/unet"
|
||||
)
|
||||
@@ -77,6 +79,45 @@ type Client struct {
|
||||
// fidPool is the collection of available fids.
|
||||
fidPool pool
|
||||
|
||||
// messageSize is the maximum total size of a message.
|
||||
messageSize uint32
|
||||
|
||||
// payloadSize is the maximum payload size of a read or write.
|
||||
//
|
||||
// For large reads and writes this means that the read or write is
|
||||
// broken up into buffer-size/payloadSize requests.
|
||||
payloadSize uint32
|
||||
|
||||
// version is the agreed upon version X of 9P2000.L.Google.X.
|
||||
// version 0 implies 9P2000.L.
|
||||
version uint32
|
||||
|
||||
// sendRecv is the transport function.
|
||||
//
|
||||
// This is determined dynamically based on whether or not the server
|
||||
// supports flipcall channels (preferred as it is faster and more
|
||||
// efficient, and does not require tags).
|
||||
sendRecv func(message, message) error
|
||||
|
||||
// -- below corresponds to sendRecvChannel --
|
||||
|
||||
// channelsMu protects channels.
|
||||
channelsMu sync.Mutex
|
||||
|
||||
// channelsWg is a wait group for active clients.
|
||||
channelsWg sync.WaitGroup
|
||||
|
||||
// channels are the set of initialized IPCs channels.
|
||||
channels []*channel
|
||||
|
||||
// inuse is set when the channels are actually in use.
|
||||
//
|
||||
// This is a fixed-size slice, and the entries will be nil when the
|
||||
// corresponding channel is available.
|
||||
inuse []*channel
|
||||
|
||||
// -- below corresponds to sendRecvLegacy --
|
||||
|
||||
// pending is the set of pending messages.
|
||||
pending map[Tag]*response
|
||||
pendingMu sync.Mutex
|
||||
@@ -89,19 +130,6 @@ type Client struct {
|
||||
// Whoever writes to this channel is permitted to call recv. When
|
||||
// finished calling recv, this channel should be emptied.
|
||||
recvr chan bool
|
||||
|
||||
// messageSize is the maximum total size of a message.
|
||||
messageSize uint32
|
||||
|
||||
// payloadSize is the maximum payload size of a read or write
|
||||
// request. For large reads and writes this means that the
|
||||
// read or write is broken up into buffer-size/payloadSize
|
||||
// requests.
|
||||
payloadSize uint32
|
||||
|
||||
// version is the agreed upon version X of 9P2000.L.Google.X.
|
||||
// version 0 implies 9P2000.L.
|
||||
version uint32
|
||||
}
|
||||
|
||||
// NewClient creates a new client. It performs a Tversion exchange with
|
||||
@@ -138,8 +166,15 @@ func NewClient(socket *unet.Socket, messageSize uint32, version string) (*Client
|
||||
return nil, ErrBadVersionString
|
||||
}
|
||||
for {
|
||||
// Always exchange the version using the legacy version of the
|
||||
// protocol. If the protocol supports flipcall, then we switch
|
||||
// our sendRecv function to use that functionality. Otherwise,
|
||||
// we stick to sendRecvLegacy.
|
||||
rversion := Rversion{}
|
||||
err := c.sendRecv(&Tversion{Version: versionString(requested), MSize: messageSize}, &rversion)
|
||||
err := c.sendRecvLegacy(&Tversion{
|
||||
Version: versionString(requested),
|
||||
MSize: messageSize,
|
||||
}, &rversion)
|
||||
|
||||
// The server told us to try again with a lower version.
|
||||
if err == syscall.EAGAIN {
|
||||
@@ -165,9 +200,125 @@ func NewClient(socket *unet.Socket, messageSize uint32, version string) (*Client
|
||||
c.version = version
|
||||
break
|
||||
}
|
||||
|
||||
// Can we switch to use the more advanced channels and create
|
||||
// independent channels for communication? Prefer it if possible.
|
||||
if versionSupportsFlipcall(c.version) {
|
||||
// Attempt to initialize IPC-based communication.
|
||||
for i := 0; i < channelsPerClient; i++ {
|
||||
if err := c.openChannel(i); err != nil {
|
||||
log.Warningf("error opening flipcall channel: %v", err)
|
||||
break // Stop.
|
||||
}
|
||||
}
|
||||
if len(c.channels) >= 1 {
|
||||
// At least one channel created.
|
||||
c.sendRecv = c.sendRecvChannel
|
||||
|
||||
// If we are using channels for communication, then we must poll
|
||||
// for shutdown events on the main socket. If the socket happens
|
||||
// to shutdown, then we will close the channels as well. This is
|
||||
// necessary because channels can hang forever if the server dies
|
||||
// while we're expecting a response.
|
||||
go c.watch(socket) // S/R-SAFE: not relevant.
|
||||
} else {
|
||||
// Channel setup failed; fallback.
|
||||
c.sendRecv = c.sendRecvLegacy
|
||||
}
|
||||
} else {
|
||||
// No channels available: use the legacy mechanism.
|
||||
c.sendRecv = c.sendRecvLegacy
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// watch watches the given socket and calls Close on hang up events.
|
||||
//
|
||||
// This is intended to be called as a goroutine.
|
||||
func (c *Client) watch(socket *unet.Socket) {
|
||||
events := []unix.PollFd{
|
||||
unix.PollFd{
|
||||
Fd: int32(socket.FD()),
|
||||
Events: unix.POLLHUP | unix.POLLRDHUP,
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
// Wait for a shutdown event.
|
||||
n, err := unix.Ppoll(events, nil, nil)
|
||||
if n == 0 || err == syscall.EAGAIN {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Close everything down: this will kick all active clients off any
|
||||
// pending requests. Note that Close must be safe to call concurrently,
|
||||
// and multiple times (see Close below).
|
||||
c.Close()
|
||||
}
|
||||
|
||||
// openChannel attempts to open a client channel.
|
||||
//
|
||||
// Note that this function returns naked errors which should not be propagated
|
||||
// directly to a caller. It is expected that the errors will be logged and a
|
||||
// fallback path will be used instead.
|
||||
func (c *Client) openChannel(id int) error {
|
||||
var (
|
||||
rchannel0 Rchannel
|
||||
rchannel1 Rchannel
|
||||
res = new(channel)
|
||||
)
|
||||
|
||||
// Open the data channel.
|
||||
if err := c.sendRecvLegacy(&Tchannel{
|
||||
ID: uint32(id),
|
||||
Control: 0,
|
||||
}, &rchannel0); err != nil {
|
||||
return fmt.Errorf("error handling Tchannel message: %v", err)
|
||||
}
|
||||
if rchannel0.FilePayload() == nil {
|
||||
return fmt.Errorf("missing file descriptor on primary channel")
|
||||
}
|
||||
|
||||
// We don't need to hold this.
|
||||
defer rchannel0.FilePayload().Close()
|
||||
|
||||
// Open the channel for file descriptors.
|
||||
if err := c.sendRecvLegacy(&Tchannel{
|
||||
ID: uint32(id),
|
||||
Control: 1,
|
||||
}, &rchannel1); err != nil {
|
||||
return err
|
||||
}
|
||||
if rchannel1.FilePayload() == nil {
|
||||
return fmt.Errorf("missing file descriptor on file descriptor channel")
|
||||
}
|
||||
|
||||
// Construct the endpoints.
|
||||
res.desc = flipcall.PacketWindowDescriptor{
|
||||
FD: rchannel0.FilePayload().FD(),
|
||||
Offset: int64(rchannel0.Offset),
|
||||
Length: int(rchannel0.Length),
|
||||
}
|
||||
if err := res.data.Init(flipcall.ClientSide, res.desc); err != nil {
|
||||
rchannel1.FilePayload().Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// The fds channel owns the control payload, and it will be closed when
|
||||
// the channel object is closed.
|
||||
res.fds.Init(rchannel1.FilePayload().Release())
|
||||
|
||||
// Save the channel.
|
||||
c.channelsMu.Lock()
|
||||
defer c.channelsMu.Unlock()
|
||||
c.channels = append(c.channels, res)
|
||||
c.inuse = append(c.inuse, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleOne handles a single incoming message.
|
||||
//
|
||||
// This should only be called with the token from recvr. Note that the received
|
||||
@@ -247,10 +398,10 @@ func (c *Client) waitAndRecv(done chan error) error {
|
||||
}
|
||||
}
|
||||
|
||||
// sendRecv performs a roundtrip message exchange.
|
||||
// sendRecvLegacy performs a roundtrip message exchange.
|
||||
//
|
||||
// This is called by internal functions.
|
||||
func (c *Client) sendRecv(t message, r message) error {
|
||||
func (c *Client) sendRecvLegacy(t message, r message) error {
|
||||
tag, ok := c.tagPool.Get()
|
||||
if !ok {
|
||||
return ErrOutOfTags
|
||||
@@ -296,12 +447,107 @@ func (c *Client) sendRecv(t message, r message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendRecvChannel uses channels to send a message.
|
||||
func (c *Client) sendRecvChannel(t message, r message) error {
|
||||
c.channelsMu.Lock()
|
||||
if len(c.channels) == 0 {
|
||||
// No channel available.
|
||||
c.channelsMu.Unlock()
|
||||
return c.sendRecvLegacy(t, r)
|
||||
}
|
||||
|
||||
// Find the last used channel.
|
||||
//
|
||||
// Note that we must add one to the wait group while holding the
|
||||
// channel mutex, in order for the Wait operation to be race-free
|
||||
// below. The Wait operation shuts down all in use channels and
|
||||
// waits for them to return, but must do so holding the mutex.
|
||||
idx := len(c.channels) - 1
|
||||
ch := c.channels[idx]
|
||||
c.channels = c.channels[:idx]
|
||||
c.inuse[idx] = ch
|
||||
c.channelsWg.Add(1)
|
||||
c.channelsMu.Unlock()
|
||||
|
||||
// Ensure that it's connected.
|
||||
if !ch.connected {
|
||||
ch.connected = true
|
||||
if err := ch.data.Connect(); err != nil {
|
||||
// The channel is unusable, so don't return it.
|
||||
ch.Close()
|
||||
c.channelsWg.Done()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message.
|
||||
err := ch.sendRecv(c, t, r)
|
||||
if err != nil {
|
||||
// On shutdown, we'll see ENOENT. This is a normal situation, and
|
||||
// we shouldn't generate a spurious warning message in that case.
|
||||
log.Debugf("error calling sendRecvChannel: %v", err)
|
||||
}
|
||||
c.channelsWg.Done()
|
||||
|
||||
// Return the channel.
|
||||
//
|
||||
// Note that we check the channel from the inuse slice here. This
|
||||
// prevents a race where Close is called, which clears inuse, and
|
||||
// means that we will not actually return the closed channel.
|
||||
c.channelsMu.Lock()
|
||||
if c.inuse[idx] != nil {
|
||||
c.channels = append(c.channels, ch)
|
||||
c.inuse[idx] = nil
|
||||
}
|
||||
c.channelsMu.Unlock()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Version returns the negotiated 9P2000.L.Google version number.
|
||||
func (c *Client) Version() uint32 {
|
||||
return c.version
|
||||
}
|
||||
|
||||
// Close closes the underlying socket.
|
||||
// Close closes the underlying socket and channels.
|
||||
//
|
||||
// Because Close may be called asynchronously from watch, it must be
|
||||
// safe to call concurrently and multiple times.
|
||||
func (c *Client) Close() error {
|
||||
c.channelsMu.Lock()
|
||||
defer c.channelsMu.Unlock()
|
||||
|
||||
// Close all inactive channels.
|
||||
for _, ch := range c.channels {
|
||||
ch.Shutdown()
|
||||
ch.Close()
|
||||
}
|
||||
// Close all active channels.
|
||||
for _, ch := range c.inuse {
|
||||
if ch != nil {
|
||||
log.Debugf("shutting down active channel@%p...", ch)
|
||||
ch.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for active users.
|
||||
c.channelsWg.Wait()
|
||||
|
||||
// Close all previously active channels.
|
||||
for i, ch := range c.inuse {
|
||||
if ch != nil {
|
||||
ch.Close()
|
||||
|
||||
// Clear the inuse entry here so that it will not be returned
|
||||
// to the channel slice, which is cleared below. See the
|
||||
// comment at the end of sendRecvChannel.
|
||||
c.inuse[i] = nil
|
||||
}
|
||||
}
|
||||
c.channels = nil // Prevent use again.
|
||||
|
||||
// Close the main socket. Note that operation is safe to be called
|
||||
// multiple times, unlikely the channel Close operations above, which
|
||||
// we are careful to ensure aren't called twice.
|
||||
return c.socket.Close()
|
||||
}
|
||||
|
||||
+46
-4
@@ -35,23 +35,23 @@ func TestVersion(t *testing.T) {
|
||||
go s.Handle(serverSocket)
|
||||
|
||||
// NewClient does a Tversion exchange, so this is our test for success.
|
||||
c, err := NewClient(clientSocket, 1024*1024 /* 1M message size */, HighestVersionString())
|
||||
c, err := NewClient(clientSocket, DefaultMessageSize, HighestVersionString())
|
||||
if err != nil {
|
||||
t.Fatalf("got %v, expected nil", err)
|
||||
}
|
||||
|
||||
// Check a bogus version string.
|
||||
if err := c.sendRecv(&Tversion{Version: "notokay", MSize: 1024 * 1024}, &Rversion{}); err != syscall.EINVAL {
|
||||
if err := c.sendRecv(&Tversion{Version: "notokay", MSize: DefaultMessageSize}, &Rversion{}); err != syscall.EINVAL {
|
||||
t.Errorf("got %v expected %v", err, syscall.EINVAL)
|
||||
}
|
||||
|
||||
// Check a bogus version number.
|
||||
if err := c.sendRecv(&Tversion{Version: "9P1000.L", MSize: 1024 * 1024}, &Rversion{}); err != syscall.EINVAL {
|
||||
if err := c.sendRecv(&Tversion{Version: "9P1000.L", MSize: DefaultMessageSize}, &Rversion{}); err != syscall.EINVAL {
|
||||
t.Errorf("got %v expected %v", err, syscall.EINVAL)
|
||||
}
|
||||
|
||||
// Check a too high version number.
|
||||
if err := c.sendRecv(&Tversion{Version: versionString(highestSupportedVersion + 1), MSize: 1024 * 1024}, &Rversion{}); err != syscall.EAGAIN {
|
||||
if err := c.sendRecv(&Tversion{Version: versionString(highestSupportedVersion + 1), MSize: DefaultMessageSize}, &Rversion{}); err != syscall.EAGAIN {
|
||||
t.Errorf("got %v expected %v", err, syscall.EAGAIN)
|
||||
}
|
||||
|
||||
@@ -60,3 +60,45 @@ func TestVersion(t *testing.T) {
|
||||
t.Errorf("got %v expected %v", err, syscall.EINVAL)
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkSendRecv(b *testing.B, fn func(c *Client) func(message, message) error) {
|
||||
// See above.
|
||||
serverSocket, clientSocket, err := unet.SocketPair(false)
|
||||
if err != nil {
|
||||
b.Fatalf("socketpair got err %v expected nil", err)
|
||||
}
|
||||
defer clientSocket.Close()
|
||||
|
||||
// See above.
|
||||
s := NewServer(nil)
|
||||
go s.Handle(serverSocket)
|
||||
|
||||
// See above.
|
||||
c, err := NewClient(clientSocket, DefaultMessageSize, HighestVersionString())
|
||||
if err != nil {
|
||||
b.Fatalf("got %v, expected nil", err)
|
||||
}
|
||||
|
||||
// Initialize messages.
|
||||
sendRecv := fn(c)
|
||||
tversion := &Tversion{
|
||||
Version: versionString(highestSupportedVersion),
|
||||
MSize: DefaultMessageSize,
|
||||
}
|
||||
rversion := new(Rversion)
|
||||
|
||||
// Run in a loop.
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := sendRecv(tversion, rversion); err != nil {
|
||||
b.Fatalf("got unexpected err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSendRecvLegacy(b *testing.B) {
|
||||
benchmarkSendRecv(b, func(c *Client) func(message, message) error { return c.sendRecvLegacy })
|
||||
}
|
||||
|
||||
func BenchmarkSendRecvChannel(b *testing.B) {
|
||||
benchmarkSendRecv(b, func(c *Client) func(message, message) error { return c.sendRecvChannel })
|
||||
}
|
||||
|
||||
+50
-3
@@ -305,7 +305,9 @@ func (t *Tlopen) handle(cs *connState) message {
|
||||
ref.opened = true
|
||||
ref.openFlags = t.Flags
|
||||
|
||||
return &Rlopen{QID: qid, IoUnit: ioUnit, File: osFile}
|
||||
rlopen := &Rlopen{QID: qid, IoUnit: ioUnit}
|
||||
rlopen.SetFilePayload(osFile)
|
||||
return rlopen
|
||||
}
|
||||
|
||||
func (t *Tlcreate) do(cs *connState, uid UID) (*Rlcreate, error) {
|
||||
@@ -364,7 +366,9 @@ func (t *Tlcreate) do(cs *connState, uid UID) (*Rlcreate, error) {
|
||||
// Replace the FID reference.
|
||||
cs.InsertFID(t.FID, newRef)
|
||||
|
||||
return &Rlcreate{Rlopen: Rlopen{QID: qid, IoUnit: ioUnit, File: osFile}}, nil
|
||||
rlcreate := &Rlcreate{Rlopen: Rlopen{QID: qid, IoUnit: ioUnit}}
|
||||
rlcreate.SetFilePayload(osFile)
|
||||
return rlcreate, nil
|
||||
}
|
||||
|
||||
// handle implements handler.handle.
|
||||
@@ -1287,5 +1291,48 @@ func (t *Tlconnect) handle(cs *connState) message {
|
||||
return newErr(err)
|
||||
}
|
||||
|
||||
return &Rlconnect{File: osFile}
|
||||
rlconnect := &Rlconnect{}
|
||||
rlconnect.SetFilePayload(osFile)
|
||||
return rlconnect
|
||||
}
|
||||
|
||||
// handle implements handler.handle.
|
||||
func (t *Tchannel) handle(cs *connState) message {
|
||||
// Ensure that channels are enabled.
|
||||
if err := cs.initializeChannels(); err != nil {
|
||||
return newErr(err)
|
||||
}
|
||||
|
||||
// Lookup the given channel.
|
||||
ch := cs.lookupChannel(t.ID)
|
||||
if ch == nil {
|
||||
return newErr(syscall.ENOSYS)
|
||||
}
|
||||
|
||||
// Return the payload. Note that we need to duplicate the file
|
||||
// descriptor for the channel allocator, because sending is a
|
||||
// destructive operation between sendRecvLegacy (and now the newer
|
||||
// channel send operations). Same goes for the client FD.
|
||||
rchannel := &Rchannel{
|
||||
Offset: uint64(ch.desc.Offset),
|
||||
Length: uint64(ch.desc.Length),
|
||||
}
|
||||
switch t.Control {
|
||||
case 0:
|
||||
// Open the main data channel.
|
||||
mfd, err := syscall.Dup(int(cs.channelAlloc.FD()))
|
||||
if err != nil {
|
||||
return newErr(err)
|
||||
}
|
||||
rchannel.SetFilePayload(fd.New(mfd))
|
||||
case 1:
|
||||
cfd, err := syscall.Dup(ch.client.FD())
|
||||
if err != nil {
|
||||
return newErr(err)
|
||||
}
|
||||
rchannel.SetFilePayload(fd.New(cfd))
|
||||
default:
|
||||
return newErr(syscall.EINVAL)
|
||||
}
|
||||
return rchannel
|
||||
}
|
||||
|
||||
+81
-26
@@ -64,6 +64,21 @@ type filer interface {
|
||||
SetFilePayload(*fd.FD)
|
||||
}
|
||||
|
||||
// filePayload embeds a File object.
|
||||
type filePayload struct {
|
||||
File *fd.FD
|
||||
}
|
||||
|
||||
// FilePayload returns the file payload.
|
||||
func (f *filePayload) FilePayload() *fd.FD {
|
||||
return f.File
|
||||
}
|
||||
|
||||
// SetFilePayload sets the received file.
|
||||
func (f *filePayload) SetFilePayload(file *fd.FD) {
|
||||
f.File = file
|
||||
}
|
||||
|
||||
// Tversion is a version request.
|
||||
type Tversion struct {
|
||||
// MSize is the message size to use.
|
||||
@@ -524,10 +539,7 @@ type Rlopen struct {
|
||||
// IoUnit is the recommended I/O unit.
|
||||
IoUnit uint32
|
||||
|
||||
// File may be attached via the socket.
|
||||
//
|
||||
// This is an extension specific to this package.
|
||||
File *fd.FD
|
||||
filePayload
|
||||
}
|
||||
|
||||
// Decode implements encoder.Decode.
|
||||
@@ -547,16 +559,6 @@ func (*Rlopen) Type() MsgType {
|
||||
return MsgRlopen
|
||||
}
|
||||
|
||||
// FilePayload returns the file payload.
|
||||
func (r *Rlopen) FilePayload() *fd.FD {
|
||||
return r.File
|
||||
}
|
||||
|
||||
// SetFilePayload sets the received file.
|
||||
func (r *Rlopen) SetFilePayload(file *fd.FD) {
|
||||
r.File = file
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (r *Rlopen) String() string {
|
||||
return fmt.Sprintf("Rlopen{QID: %s, IoUnit: %d, File: %v}", r.QID, r.IoUnit, r.File)
|
||||
@@ -2171,8 +2173,7 @@ func (t *Tlconnect) String() string {
|
||||
|
||||
// Rlconnect is a connect response.
|
||||
type Rlconnect struct {
|
||||
// File is a host socket.
|
||||
File *fd.FD
|
||||
filePayload
|
||||
}
|
||||
|
||||
// Decode implements encoder.Decode.
|
||||
@@ -2186,21 +2187,73 @@ func (*Rlconnect) Type() MsgType {
|
||||
return MsgRlconnect
|
||||
}
|
||||
|
||||
// FilePayload returns the file payload.
|
||||
func (r *Rlconnect) FilePayload() *fd.FD {
|
||||
return r.File
|
||||
}
|
||||
|
||||
// SetFilePayload sets the received file.
|
||||
func (r *Rlconnect) SetFilePayload(file *fd.FD) {
|
||||
r.File = file
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (r *Rlconnect) String() string {
|
||||
return fmt.Sprintf("Rlconnect{File: %v}", r.File)
|
||||
}
|
||||
|
||||
// Tchannel creates a new channel.
|
||||
type Tchannel struct {
|
||||
// ID is the channel ID.
|
||||
ID uint32
|
||||
|
||||
// Control is 0 if the Rchannel response should provide the flipcall
|
||||
// component of the channel, and 1 if the Rchannel response should
|
||||
// provide the fdchannel component of the channel.
|
||||
Control uint32
|
||||
}
|
||||
|
||||
// Decode implements encoder.Decode.
|
||||
func (t *Tchannel) Decode(b *buffer) {
|
||||
t.ID = b.Read32()
|
||||
t.Control = b.Read32()
|
||||
}
|
||||
|
||||
// Encode implements encoder.Encode.
|
||||
func (t *Tchannel) Encode(b *buffer) {
|
||||
b.Write32(t.ID)
|
||||
b.Write32(t.Control)
|
||||
}
|
||||
|
||||
// Type implements message.Type.
|
||||
func (*Tchannel) Type() MsgType {
|
||||
return MsgTchannel
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (t *Tchannel) String() string {
|
||||
return fmt.Sprintf("Tchannel{ID: %d, Control: %d}", t.ID, t.Control)
|
||||
}
|
||||
|
||||
// Rchannel is the channel response.
|
||||
type Rchannel struct {
|
||||
Offset uint64
|
||||
Length uint64
|
||||
filePayload
|
||||
}
|
||||
|
||||
// Decode implements encoder.Decode.
|
||||
func (r *Rchannel) Decode(b *buffer) {
|
||||
r.Offset = b.Read64()
|
||||
r.Length = b.Read64()
|
||||
}
|
||||
|
||||
// Encode implements encoder.Encode.
|
||||
func (r *Rchannel) Encode(b *buffer) {
|
||||
b.Write64(r.Offset)
|
||||
b.Write64(r.Length)
|
||||
}
|
||||
|
||||
// Type implements message.Type.
|
||||
func (*Rchannel) Type() MsgType {
|
||||
return MsgRchannel
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (r *Rchannel) String() string {
|
||||
return fmt.Sprintf("Rchannel{Offset: %d, Length: %d}", r.Offset, r.Length)
|
||||
}
|
||||
|
||||
const maxCacheSize = 3
|
||||
|
||||
// msgFactory is used to reduce allocations by caching messages for reuse.
|
||||
@@ -2356,4 +2409,6 @@ func init() {
|
||||
msgRegistry.register(MsgRlconnect, func() message { return &Rlconnect{} })
|
||||
msgRegistry.register(MsgTallocate, func() message { return &Tallocate{} })
|
||||
msgRegistry.register(MsgRallocate, func() message { return &Rallocate{} })
|
||||
msgRegistry.register(MsgTchannel, func() message { return &Tchannel{} })
|
||||
msgRegistry.register(MsgRchannel, func() message { return &Rchannel{} })
|
||||
}
|
||||
|
||||
@@ -378,6 +378,8 @@ const (
|
||||
MsgRlconnect = 137
|
||||
MsgTallocate = 138
|
||||
MsgRallocate = 139
|
||||
MsgTchannel = 250
|
||||
MsgRchannel = 251
|
||||
)
|
||||
|
||||
// QIDType represents the file type for QIDs.
|
||||
|
||||
@@ -315,7 +315,7 @@ func NewHarness(t *testing.T) (*Harness, *p9.Client) {
|
||||
}()
|
||||
|
||||
// Create the client.
|
||||
client, err := p9.NewClient(clientSocket, 1024, p9.HighestVersionString())
|
||||
client, err := p9.NewClient(clientSocket, p9.DefaultMessageSize, p9.HighestVersionString())
|
||||
if err != nil {
|
||||
serverSocket.Close()
|
||||
clientSocket.Close()
|
||||
|
||||
+144
-34
@@ -21,6 +21,9 @@ import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
"gvisor.dev/gvisor/pkg/fdchannel"
|
||||
"gvisor.dev/gvisor/pkg/flipcall"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/unet"
|
||||
)
|
||||
@@ -45,7 +48,6 @@ type Server struct {
|
||||
}
|
||||
|
||||
// NewServer returns a new server.
|
||||
//
|
||||
func NewServer(attacher Attacher) *Server {
|
||||
return &Server{
|
||||
attacher: attacher,
|
||||
@@ -85,6 +87,8 @@ type connState struct {
|
||||
// version 0 implies 9P2000.L.
|
||||
version uint32
|
||||
|
||||
// -- below relates to the legacy handler --
|
||||
|
||||
// recvOkay indicates that a receive may start.
|
||||
recvOkay chan bool
|
||||
|
||||
@@ -93,6 +97,20 @@ type connState struct {
|
||||
|
||||
// sendDone is signalled when a send is finished.
|
||||
sendDone chan error
|
||||
|
||||
// -- below relates to the flipcall handler --
|
||||
|
||||
// channelMu protects below.
|
||||
channelMu sync.Mutex
|
||||
|
||||
// channelWg represents active workers.
|
||||
channelWg sync.WaitGroup
|
||||
|
||||
// channelAlloc allocates channel memory.
|
||||
channelAlloc *flipcall.PacketWindowAllocator
|
||||
|
||||
// channels are the set of initialized channels.
|
||||
channels []*channel
|
||||
}
|
||||
|
||||
// fidRef wraps a node and tracks references.
|
||||
@@ -386,6 +404,99 @@ func (cs *connState) WaitTag(t Tag) {
|
||||
<-ch
|
||||
}
|
||||
|
||||
// initializeChannels initializes all channels.
|
||||
//
|
||||
// This is a no-op if channels are already initialized.
|
||||
func (cs *connState) initializeChannels() (err error) {
|
||||
cs.channelMu.Lock()
|
||||
defer cs.channelMu.Unlock()
|
||||
|
||||
// Initialize our channel allocator.
|
||||
if cs.channelAlloc == nil {
|
||||
alloc, err := flipcall.NewPacketWindowAllocator()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.channelAlloc = alloc
|
||||
}
|
||||
|
||||
// Create all the channels.
|
||||
for len(cs.channels) < channelsPerClient {
|
||||
res := &channel{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
res.desc, err = cs.channelAlloc.Allocate(channelSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := res.data.Init(flipcall.ServerSide, res.desc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
socks, err := fdchannel.NewConnectedSockets()
|
||||
if err != nil {
|
||||
res.data.Destroy() // Cleanup.
|
||||
return err
|
||||
}
|
||||
res.fds.Init(socks[0])
|
||||
res.client = fd.New(socks[1])
|
||||
|
||||
cs.channels = append(cs.channels, res)
|
||||
|
||||
// Start servicing the channel.
|
||||
//
|
||||
// When we call stop, we will close all the channels and these
|
||||
// routines should finish. We need the wait group to ensure
|
||||
// that active handlers are actually finished before cleanup.
|
||||
cs.channelWg.Add(1)
|
||||
go func() { // S/R-SAFE: Server side.
|
||||
defer cs.channelWg.Done()
|
||||
res.service(cs)
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// lookupChannel looks up the channel with given id.
|
||||
//
|
||||
// The function returns nil if no such channel is available.
|
||||
func (cs *connState) lookupChannel(id uint32) *channel {
|
||||
cs.channelMu.Lock()
|
||||
defer cs.channelMu.Unlock()
|
||||
if id >= uint32(len(cs.channels)) {
|
||||
return nil
|
||||
}
|
||||
return cs.channels[id]
|
||||
}
|
||||
|
||||
// handle handles a single message.
|
||||
func (cs *connState) handle(m message) (r message) {
|
||||
defer func() {
|
||||
if r == nil {
|
||||
// Don't allow a panic to propagate.
|
||||
recover()
|
||||
|
||||
// Include a useful log message.
|
||||
log.Warningf("panic in handler: %s", debug.Stack())
|
||||
|
||||
// Wrap in an EFAULT error; we don't really have a
|
||||
// better way to describe this kind of error. It will
|
||||
// usually manifest as a result of the test framework.
|
||||
r = newErr(syscall.EFAULT)
|
||||
}
|
||||
}()
|
||||
if handler, ok := m.(handler); ok {
|
||||
// Call the message handler.
|
||||
r = handler.handle(cs)
|
||||
} else {
|
||||
// Produce an ENOSYS error.
|
||||
r = newErr(syscall.ENOSYS)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// handleRequest handles a single request.
|
||||
//
|
||||
// The recvDone channel is signaled when recv is done (with a error if
|
||||
@@ -428,41 +539,20 @@ func (cs *connState) handleRequest() {
|
||||
}
|
||||
|
||||
// Handle the message.
|
||||
var r message // r is the response.
|
||||
defer func() {
|
||||
if r == nil {
|
||||
// Don't allow a panic to propagate.
|
||||
recover()
|
||||
r := cs.handle(m)
|
||||
|
||||
// Include a useful log message.
|
||||
log.Warningf("panic in handler: %s", debug.Stack())
|
||||
// Clear the tag before sending. That's because as soon as this hits
|
||||
// the wire, the client can legally send the same tag.
|
||||
cs.ClearTag(tag)
|
||||
|
||||
// Wrap in an EFAULT error; we don't really have a
|
||||
// better way to describe this kind of error. It will
|
||||
// usually manifest as a result of the test framework.
|
||||
r = newErr(syscall.EFAULT)
|
||||
}
|
||||
// Send back the result.
|
||||
cs.sendMu.Lock()
|
||||
err = send(cs.conn, tag, r)
|
||||
cs.sendMu.Unlock()
|
||||
cs.sendDone <- err
|
||||
|
||||
// Clear the tag before sending. That's because as soon as this
|
||||
// hits the wire, the client can legally send another message
|
||||
// with the same tag.
|
||||
cs.ClearTag(tag)
|
||||
|
||||
// Send back the result.
|
||||
cs.sendMu.Lock()
|
||||
err = send(cs.conn, tag, r)
|
||||
cs.sendMu.Unlock()
|
||||
cs.sendDone <- err
|
||||
}()
|
||||
if handler, ok := m.(handler); ok {
|
||||
// Call the message handler.
|
||||
r = handler.handle(cs)
|
||||
} else {
|
||||
// Produce an ENOSYS error.
|
||||
r = newErr(syscall.ENOSYS)
|
||||
}
|
||||
// Return the message to the cache.
|
||||
msgRegistry.put(m)
|
||||
m = nil // 'm' should not be touched after this point.
|
||||
}
|
||||
|
||||
func (cs *connState) handleRequests() {
|
||||
@@ -477,7 +567,27 @@ func (cs *connState) stop() {
|
||||
close(cs.recvDone)
|
||||
close(cs.sendDone)
|
||||
|
||||
for _, fidRef := range cs.fids {
|
||||
// Free the channels.
|
||||
cs.channelMu.Lock()
|
||||
for _, ch := range cs.channels {
|
||||
ch.Shutdown()
|
||||
}
|
||||
cs.channelWg.Wait()
|
||||
for _, ch := range cs.channels {
|
||||
ch.Close()
|
||||
}
|
||||
cs.channels = nil // Clear.
|
||||
cs.channelMu.Unlock()
|
||||
|
||||
// Free the channel memory.
|
||||
if cs.channelAlloc != nil {
|
||||
cs.channelAlloc.Destroy()
|
||||
}
|
||||
|
||||
// Close all remaining fids.
|
||||
for fid, fidRef := range cs.fids {
|
||||
delete(cs.fids, fid)
|
||||
|
||||
// Drop final reference in the FID table. Note this should
|
||||
// always close the file, since we've ensured that there are no
|
||||
// handlers running via the wait for Pending => 0 below.
|
||||
@@ -510,7 +620,7 @@ func (cs *connState) service() error {
|
||||
for i := 0; i < pending; i++ {
|
||||
<-cs.sendDone
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// This handler is now pending.
|
||||
|
||||
+4
-1
@@ -54,7 +54,10 @@ const (
|
||||
headerLength uint32 = 7
|
||||
|
||||
// maximumLength is the largest possible message.
|
||||
maximumLength uint32 = 4 * 1024 * 1024
|
||||
maximumLength uint32 = 1 << 20
|
||||
|
||||
// DefaultMessageSize is a sensible default.
|
||||
DefaultMessageSize uint32 = 64 << 10
|
||||
|
||||
// initialBufferLength is the initial data buffer we allocate.
|
||||
initialBufferLength uint32 = 64
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
// Copyright 2019 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 p9
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
"gvisor.dev/gvisor/pkg/fdchannel"
|
||||
"gvisor.dev/gvisor/pkg/flipcall"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
// channelsPerClient is the number of channels to create per client.
|
||||
//
|
||||
// While the client and server will generally agree on this number, in reality
|
||||
// it's completely up to the server. We simply define a minimum of 2, and a
|
||||
// maximum of 4, and select the number of available processes as a tie-breaker.
|
||||
// Note that we don't want the number of channels to be too large, because each
|
||||
// will account for channelSize memory used, which can be large.
|
||||
var channelsPerClient = func() int {
|
||||
n := runtime.NumCPU()
|
||||
if n < 2 {
|
||||
return 2
|
||||
}
|
||||
if n > 4 {
|
||||
return 4
|
||||
}
|
||||
return n
|
||||
}()
|
||||
|
||||
// channelSize is the channel size to create.
|
||||
//
|
||||
// We simply ensure that this is larger than the largest possible message size,
|
||||
// plus the flipcall packet header, plus the two bytes we write below.
|
||||
const channelSize = int(2 + flipcall.PacketHeaderBytes + 2 + maximumLength)
|
||||
|
||||
// channel is a fast IPC channel.
|
||||
//
|
||||
// The same object is used by both the server and client implementations. In
|
||||
// general, the client will use only the send and recv methods.
|
||||
type channel struct {
|
||||
desc flipcall.PacketWindowDescriptor
|
||||
data flipcall.Endpoint
|
||||
fds fdchannel.Endpoint
|
||||
buf buffer
|
||||
|
||||
// -- client only --
|
||||
connected bool
|
||||
|
||||
// -- server only --
|
||||
client *fd.FD
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// reset resets the channel buffer.
|
||||
func (ch *channel) reset(sz uint32) {
|
||||
ch.buf.data = ch.data.Data()[:sz]
|
||||
}
|
||||
|
||||
// service services the channel.
|
||||
func (ch *channel) service(cs *connState) error {
|
||||
rsz, err := ch.data.RecvFirst()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for rsz > 0 {
|
||||
m, err := ch.recv(nil, rsz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r := cs.handle(m)
|
||||
msgRegistry.put(m)
|
||||
rsz, err = ch.send(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil // Done.
|
||||
}
|
||||
|
||||
// Shutdown shuts down the channel.
|
||||
//
|
||||
// This must be called before Close.
|
||||
func (ch *channel) Shutdown() {
|
||||
ch.data.Shutdown()
|
||||
}
|
||||
|
||||
// Close closes the channel.
|
||||
//
|
||||
// This must only be called once, and cannot return an error. Note that
|
||||
// synchronization for this method is provided at a high-level, depending on
|
||||
// whether it is the client or server. This cannot be called while there are
|
||||
// active callers in either service or sendRecv.
|
||||
//
|
||||
// Precondition: the channel should be shutdown.
|
||||
func (ch *channel) Close() error {
|
||||
// Close all backing transports.
|
||||
ch.fds.Destroy()
|
||||
ch.data.Destroy()
|
||||
if ch.client != nil {
|
||||
ch.client.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// send sends the given message.
|
||||
//
|
||||
// The return value is the size of the received response. Not that in the
|
||||
// server case, this is the size of the next request.
|
||||
func (ch *channel) send(m message) (uint32, error) {
|
||||
if log.IsLogging(log.Debug) {
|
||||
log.Debugf("send [channel @%p] %s", ch, m.String())
|
||||
}
|
||||
|
||||
// Send any file payload.
|
||||
sentFD := false
|
||||
if filer, ok := m.(filer); ok {
|
||||
if f := filer.FilePayload(); f != nil {
|
||||
if err := ch.fds.SendFD(f.FD()); err != nil {
|
||||
return 0, syscall.EIO // Map everything to EIO.
|
||||
}
|
||||
f.Close() // Per sendRecvLegacy.
|
||||
sentFD = true // To mark below.
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the message.
|
||||
//
|
||||
// Note that IPC itself encodes the length of messages, so we don't
|
||||
// need to encode a standard 9P header. We write only the message type.
|
||||
ch.reset(0)
|
||||
|
||||
ch.buf.WriteMsgType(m.Type())
|
||||
if sentFD {
|
||||
ch.buf.Write8(1) // Incoming FD.
|
||||
} else {
|
||||
ch.buf.Write8(0) // No incoming FD.
|
||||
}
|
||||
m.Encode(&ch.buf)
|
||||
ssz := uint32(len(ch.buf.data)) // Updated below.
|
||||
|
||||
// Is there a payload?
|
||||
if payloader, ok := m.(payloader); ok {
|
||||
p := payloader.Payload()
|
||||
copy(ch.data.Data()[ssz:], p)
|
||||
ssz += uint32(len(p))
|
||||
}
|
||||
|
||||
// Perform the one-shot communication.
|
||||
n, err := ch.data.SendRecv(ssz)
|
||||
if err != nil {
|
||||
if n > 0 {
|
||||
return n, nil
|
||||
}
|
||||
return 0, syscall.EIO // See above.
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// recv decodes a message that exists on the channel.
|
||||
//
|
||||
// If the passed r is non-nil, then the type must match or an error will be
|
||||
// generated. If the passed r is nil, then a new message will be created and
|
||||
// returned.
|
||||
func (ch *channel) recv(r message, rsz uint32) (message, error) {
|
||||
// Decode the response from the inline buffer.
|
||||
ch.reset(rsz)
|
||||
t := ch.buf.ReadMsgType()
|
||||
hasFD := ch.buf.Read8() != 0
|
||||
if t == MsgRlerror {
|
||||
// Change the message type. We check for this special case
|
||||
// after decoding below, and transform into an error.
|
||||
r = &Rlerror{}
|
||||
} else if r == nil {
|
||||
nr, err := msgRegistry.get(0, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = nr // New message.
|
||||
} else if t != r.Type() {
|
||||
// Not an error and not the expected response; propagate.
|
||||
return nil, &ErrBadResponse{Got: t, Want: r.Type()}
|
||||
}
|
||||
|
||||
// Is there a payload? Set to the latter portion.
|
||||
if payloader, ok := r.(payloader); ok {
|
||||
fs := payloader.FixedSize()
|
||||
payloader.SetPayload(ch.buf.data[fs:])
|
||||
ch.buf.data = ch.buf.data[:fs]
|
||||
}
|
||||
|
||||
r.Decode(&ch.buf)
|
||||
if ch.buf.isOverrun() {
|
||||
// Nothing valid was available.
|
||||
log.Debugf("recv [got %d bytes, needed more]", rsz)
|
||||
return nil, ErrNoValidMessage
|
||||
}
|
||||
|
||||
// Read any FD result.
|
||||
if hasFD {
|
||||
if rfd, err := ch.fds.RecvFDNonblock(); err == nil {
|
||||
f := fd.New(rfd)
|
||||
if filer, ok := r.(filer); ok {
|
||||
// Set the payload.
|
||||
filer.SetFilePayload(f)
|
||||
} else {
|
||||
// Don't want the FD.
|
||||
f.Close()
|
||||
}
|
||||
} else {
|
||||
// The header bit was set but nothing came in.
|
||||
log.Warningf("expected FD, got err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Log a message.
|
||||
if log.IsLogging(log.Debug) {
|
||||
log.Debugf("recv [channel @%p] %s", ch, r.String())
|
||||
}
|
||||
|
||||
// Convert errors appropriately; see above.
|
||||
if rlerr, ok := r.(*Rlerror); ok {
|
||||
return nil, syscall.Errno(rlerr.Error)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// sendRecv sends the given message over the channel.
|
||||
//
|
||||
// This is used by the client.
|
||||
func (ch *channel) sendRecv(c *Client, m, r message) error {
|
||||
rsz, err := ch.send(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = ch.recv(r, rsz)
|
||||
return err
|
||||
}
|
||||
@@ -124,7 +124,9 @@ func TestSendRecvWithFile(t *testing.T) {
|
||||
t.Fatalf("unable to create file: %v", err)
|
||||
}
|
||||
|
||||
if err := send(client, Tag(1), &Rlopen{File: f}); err != nil {
|
||||
rlopen := &Rlopen{}
|
||||
rlopen.SetFilePayload(f)
|
||||
if err := send(client, Tag(1), rlopen); err != nil {
|
||||
t.Fatalf("send got err %v expected nil", err)
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -26,7 +26,7 @@ const (
|
||||
//
|
||||
// Clients are expected to start requesting this version number and
|
||||
// to continuously decrement it until a Tversion request succeeds.
|
||||
highestSupportedVersion uint32 = 7
|
||||
highestSupportedVersion uint32 = 8
|
||||
|
||||
// lowestSupportedVersion is the lowest supported version X in a
|
||||
// version string of the format 9P2000.L.Google.X.
|
||||
@@ -148,3 +148,10 @@ func VersionSupportsMultiUser(v uint32) bool {
|
||||
func versionSupportsTallocate(v uint32) bool {
|
||||
return v >= 7
|
||||
}
|
||||
|
||||
// versionSupportsFlipcall returns true if version v supports IPC channels from
|
||||
// the flipcall package. Note that these must be negotiated, but this version
|
||||
// string indicates that such a facility exists.
|
||||
func versionSupportsFlipcall(v uint32) bool {
|
||||
return v >= 8
|
||||
}
|
||||
|
||||
@@ -88,14 +88,24 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
seccomp.AllowValue(linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG),
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(0),
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG),
|
||||
seccomp.AllowAny{},
|
||||
},
|
||||
// Non-private variants are included for flipcall support. They are otherwise
|
||||
// unncessary, as the sentry will use only private futexes internally.
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(linux.FUTEX_WAIT),
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowAny{},
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(linux.FUTEX_WAKE),
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(0),
|
||||
},
|
||||
},
|
||||
syscall.SYS_GETPID: {},
|
||||
|
||||
@@ -17,6 +17,7 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/flipcall",
|
||||
"//pkg/log",
|
||||
"//pkg/seccomp",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
|
||||
@@ -83,6 +83,11 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(syscall.F_GETFD),
|
||||
},
|
||||
// Used by flipcall.PacketWindowAllocator.Init().
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(unix.F_ADD_SEALS),
|
||||
},
|
||||
},
|
||||
syscall.SYS_FSTAT: {},
|
||||
syscall.SYS_FSTATFS: {},
|
||||
@@ -103,6 +108,19 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(0),
|
||||
},
|
||||
// Non-private futex used for flipcall.
|
||||
seccomp.Rule{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(linux.FUTEX_WAIT),
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowAny{},
|
||||
},
|
||||
seccomp.Rule{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(linux.FUTEX_WAKE),
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowAny{},
|
||||
},
|
||||
},
|
||||
syscall.SYS_GETDENTS64: {},
|
||||
syscall.SYS_GETPID: {},
|
||||
@@ -112,6 +130,7 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
syscall.SYS_LINKAT: {},
|
||||
syscall.SYS_LSEEK: {},
|
||||
syscall.SYS_MADVISE: {},
|
||||
unix.SYS_MEMFD_CREATE: {}, /// Used by flipcall.PacketWindowAllocator.Init().
|
||||
syscall.SYS_MKDIRAT: {},
|
||||
syscall.SYS_MMAP: []seccomp.Rule{
|
||||
{
|
||||
@@ -160,6 +179,13 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
syscall.SYS_RT_SIGPROCMASK: {},
|
||||
syscall.SYS_SCHED_YIELD: {},
|
||||
syscall.SYS_SENDMSG: []seccomp.Rule{
|
||||
// Used by fdchannel.Endpoint.SendFD().
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(0),
|
||||
},
|
||||
// Used by unet.SocketWriter.WriteVec().
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowAny{},
|
||||
@@ -170,7 +196,15 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
{seccomp.AllowAny{}, seccomp.AllowValue(syscall.SHUT_RDWR)},
|
||||
},
|
||||
syscall.SYS_SIGALTSTACK: {},
|
||||
syscall.SYS_SYMLINKAT: {},
|
||||
// Used by fdchannel.NewConnectedSockets().
|
||||
syscall.SYS_SOCKETPAIR: {
|
||||
{
|
||||
seccomp.AllowValue(syscall.AF_UNIX),
|
||||
seccomp.AllowValue(syscall.SOCK_SEQPACKET | syscall.SOCK_CLOEXEC),
|
||||
seccomp.AllowValue(0),
|
||||
},
|
||||
},
|
||||
syscall.SYS_SYMLINKAT: {},
|
||||
syscall.SYS_TGKILL: []seccomp.Rule{
|
||||
{
|
||||
seccomp.AllowValue(uint64(os.Getpid())),
|
||||
|
||||
Reference in New Issue
Block a user