github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/p9/server.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package p9
    16  
    17  import (
    18  	"io"
    19  	"runtime/debug"
    20  
    21  	"golang.org/x/sys/unix"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/atomicbitops"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/fd"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/fdchannel"
    26  	"github.com/nicocha30/gvisor-ligolo/pkg/flipcall"
    27  	"github.com/nicocha30/gvisor-ligolo/pkg/log"
    28  	"github.com/nicocha30/gvisor-ligolo/pkg/sync"
    29  	"github.com/nicocha30/gvisor-ligolo/pkg/unet"
    30  )
    31  
    32  // Server is a 9p2000.L server.
    33  type Server struct {
    34  	// attacher provides the attach function.
    35  	attacher Attacher
    36  
    37  	options AttacherOptions
    38  
    39  	// pathTree is the full set of paths opened on this server.
    40  	//
    41  	// These may be across different connections, but rename operations
    42  	// must be serialized globally for safely. There is a single pathTree
    43  	// for the entire server, and not per connection.
    44  	pathTree *pathNode
    45  
    46  	// renameMu is a global lock protecting rename operations. With this
    47  	// lock, we can be certain that any given rename operation can safely
    48  	// acquire two path nodes in any order, as all other concurrent
    49  	// operations acquire at most a single node.
    50  	renameMu sync.RWMutex
    51  }
    52  
    53  // NewServer returns a new server. attacher may be nil.
    54  func NewServer(attacher Attacher) *Server {
    55  	opts := AttacherOptions{}
    56  	if attacher != nil {
    57  		opts = attacher.ServerOptions()
    58  	}
    59  	return &Server{
    60  		attacher: attacher,
    61  		options:  opts,
    62  		pathTree: newPathNode(),
    63  	}
    64  }
    65  
    66  // connState is the state for a single connection.
    67  type connState struct {
    68  	// server is the backing server.
    69  	server *Server
    70  
    71  	// fids is the set of active FIDs.
    72  	//
    73  	// This is used to find FIDs for files.
    74  	fidMu sync.Mutex
    75  	fids  map[FID]*fidRef
    76  
    77  	// tags is the set of active tags.
    78  	//
    79  	// The given channel is closed when the
    80  	// tag is finished with processing.
    81  	tagMu sync.Mutex
    82  	tags  map[Tag]chan struct{}
    83  
    84  	// messageSize is the maximum message size. The server does not
    85  	// do automatic splitting of messages.
    86  	messageSize atomicbitops.Uint32
    87  
    88  	// version is the agreed upon version X of 9P2000.L.Google.X.
    89  	// version 0 implies 9P2000.L.
    90  	version atomicbitops.Uint32
    91  
    92  	// reqGate counts requests that are still being handled.
    93  	reqGate sync.Gate
    94  
    95  	//	-- below relates to the legacy handler --
    96  
    97  	// recvMu serializes receiving from conn.
    98  	recvMu sync.Mutex
    99  
   100  	// recvIdle is the number of goroutines in handleRequests() attempting to
   101  	// lock recvMu so that they can receive from conn.
   102  	recvIdle atomicbitops.Int32
   103  
   104  	// If recvShutdown is true, at least one goroutine has observed a
   105  	// connection error while receiving from conn, and all goroutines in
   106  	// handleRequests() should exit immediately. recvShutdown is protected by
   107  	// recvMu.
   108  	recvShutdown bool
   109  
   110  	// sendMu serializes sending to conn.
   111  	sendMu sync.Mutex
   112  
   113  	// conn is the connection used by the legacy transport.
   114  	conn *unet.Socket
   115  
   116  	//	-- below relates to the flipcall handler --
   117  
   118  	// channelMu protects below.
   119  	channelMu sync.Mutex
   120  
   121  	// channelWg represents active workers.
   122  	channelWg sync.WaitGroup
   123  
   124  	// channelAlloc allocates channel memory.
   125  	channelAlloc *flipcall.PacketWindowAllocator
   126  
   127  	// channels are the set of initialized channels.
   128  	channels []*channel
   129  }
   130  
   131  // fidRef wraps a node and tracks references.
   132  type fidRef struct {
   133  	// server is the associated server.
   134  	server *Server
   135  
   136  	// file is the associated File.
   137  	file File
   138  
   139  	// refs is an active refence count.
   140  	//
   141  	// The node above will be closed only when refs reaches zero.
   142  	refs atomicbitops.Int64
   143  
   144  	// opened indicates whether this has been opened already.
   145  	//
   146  	// This is updated in handlers.go.
   147  	//
   148  	// opened is protected by pathNode.opMu or renameMu (for write).
   149  	opened bool
   150  
   151  	// mode is the fidRef's mode from the walk. Only the type bits are
   152  	// valid, the permissions may change. This is used to sanity check
   153  	// operations on this element, and prevent walks across
   154  	// non-directories.
   155  	mode FileMode
   156  
   157  	// openFlags is the mode used in the open.
   158  	//
   159  	// This is updated in handlers.go.
   160  	//
   161  	// openFlags is protected by pathNode.opMu or renameMu (for write).
   162  	openFlags OpenFlags
   163  
   164  	// pathNode is the current pathNode for this FID.
   165  	pathNode *pathNode
   166  
   167  	// parent is the parent fidRef. We hold on to a parent reference to
   168  	// ensure that hooks, such as Renamed, can be executed safely by the
   169  	// server code.
   170  	//
   171  	// Note that parent cannot be changed without holding both the global
   172  	// rename lock and a writable lock on the associated pathNode for this
   173  	// fidRef. Holding either of these locks is sufficient to examine
   174  	// parent safely.
   175  	//
   176  	// The parent will be nil for root fidRefs, and non-nil otherwise. The
   177  	// method maybeParent can be used to return a cyclical reference, and
   178  	// isRoot should be used to check for root over looking at parent
   179  	// directly.
   180  	parent *fidRef
   181  }
   182  
   183  // IncRef increases the references on a fid.
   184  func (f *fidRef) IncRef() {
   185  	f.refs.Add(1)
   186  }
   187  
   188  // DecRef should be called when you're finished with a fid.
   189  func (f *fidRef) DecRef() {
   190  	if f.refs.Add(-1) == 0 {
   191  		f.file.Close()
   192  
   193  		// Drop the parent reference.
   194  		//
   195  		// Since this fidRef is guaranteed to be non-discoverable when
   196  		// the references reach zero, we don't need to worry about
   197  		// clearing the parent.
   198  		if f.parent != nil {
   199  			// If we've been previously deleted, this removing this
   200  			// ref is a no-op. That's expected.
   201  			f.parent.pathNode.removeChild(f)
   202  			f.parent.DecRef()
   203  		}
   204  	}
   205  }
   206  
   207  // TryIncRef returns true if a new reference is taken on the fid, and false if
   208  // the fid has been destroyed.
   209  func (f *fidRef) TryIncRef() bool {
   210  	for {
   211  		r := f.refs.Load()
   212  		if r <= 0 {
   213  			return false
   214  		}
   215  		if f.refs.CompareAndSwap(r, r+1) {
   216  			return true
   217  		}
   218  	}
   219  }
   220  
   221  // isDeleted returns true if this fidRef has been deleted.
   222  //
   223  // Precondition: this must be called via safelyRead, safelyWrite or
   224  // safelyGlobal.
   225  func (f *fidRef) isDeleted() bool {
   226  	return f.pathNode.deleted.Load() != 0
   227  }
   228  
   229  // isRoot indicates whether this is a root fid.
   230  func (f *fidRef) isRoot() bool {
   231  	return f.parent == nil
   232  }
   233  
   234  // maybeParent returns a cyclic reference for roots, and the parent otherwise.
   235  func (f *fidRef) maybeParent() *fidRef {
   236  	if f.parent != nil {
   237  		return f.parent
   238  	}
   239  	return f // Root has itself.
   240  }
   241  
   242  // notifyDelete marks all fidRefs as deleted.
   243  //
   244  // Precondition: this must be called via safelyWrite or safelyGlobal.
   245  func notifyDelete(pn *pathNode) {
   246  	pn.deleted.Store(1)
   247  
   248  	// Call on all subtrees.
   249  	pn.forEachChildNode(func(pn *pathNode) {
   250  		notifyDelete(pn)
   251  	})
   252  }
   253  
   254  // markChildDeleted marks all children below the given name as deleted.
   255  //
   256  // Precondition: this must be called via safelyWrite or safelyGlobal.
   257  func (f *fidRef) markChildDeleted(name string) {
   258  	if origPathNode := f.pathNode.removeWithName(name, nil); origPathNode != nil {
   259  		// Mark all children as deleted.
   260  		notifyDelete(origPathNode)
   261  	}
   262  }
   263  
   264  // notifyNameChange calls the relevant Renamed method on all nodes in the path,
   265  // recursively. Note that this applies only for subtrees, as these
   266  // notifications do not apply to the actual file whose name has changed.
   267  //
   268  // Precondition: this must be called via safelyGlobal.
   269  func notifyNameChange(pn *pathNode) {
   270  	// Call on all local references.
   271  	pn.forEachChildRef(func(ref *fidRef, name string) {
   272  		ref.file.Renamed(ref.parent.file, name)
   273  	})
   274  
   275  	// Call on all subtrees.
   276  	pn.forEachChildNode(func(pn *pathNode) {
   277  		notifyNameChange(pn)
   278  	})
   279  }
   280  
   281  // renameChildTo renames the given child to the target.
   282  //
   283  // Precondition: this must be called via safelyGlobal.
   284  func (f *fidRef) renameChildTo(oldName string, target *fidRef, newName string) {
   285  	target.markChildDeleted(newName)
   286  	origPathNode := f.pathNode.removeWithName(oldName, func(ref *fidRef) {
   287  		// N.B. DecRef can take f.pathNode's parent's childMu. This is
   288  		// allowed because renameMu is held for write via safelyGlobal.
   289  		ref.parent.DecRef() // Drop original reference.
   290  		ref.parent = target // Change parent.
   291  		ref.parent.IncRef() // Acquire new one.
   292  		if f.pathNode == target.pathNode {
   293  			target.pathNode.addChildLocked(ref, newName)
   294  		} else {
   295  			target.pathNode.addChild(ref, newName)
   296  		}
   297  		ref.file.Renamed(target.file, newName)
   298  	})
   299  
   300  	if origPathNode != nil {
   301  		// Replace the previous (now deleted) path node.
   302  		target.pathNode.addPathNodeFor(newName, origPathNode)
   303  		// Call Renamed on all children.
   304  		notifyNameChange(origPathNode)
   305  	}
   306  }
   307  
   308  // safelyRead executes the given operation with the local path node locked.
   309  // This implies that paths will not change during the operation.
   310  func (f *fidRef) safelyRead(fn func() error) (err error) {
   311  	f.server.renameMu.RLock()
   312  	defer f.server.renameMu.RUnlock()
   313  	f.pathNode.opMu.RLock()
   314  	defer f.pathNode.opMu.RUnlock()
   315  	return fn()
   316  }
   317  
   318  // safelyWrite executes the given operation with the local path node locked in
   319  // a writable fashion. This implies some paths may change.
   320  func (f *fidRef) safelyWrite(fn func() error) (err error) {
   321  	f.server.renameMu.RLock()
   322  	defer f.server.renameMu.RUnlock()
   323  	f.pathNode.opMu.Lock()
   324  	defer f.pathNode.opMu.Unlock()
   325  	return fn()
   326  }
   327  
   328  // safelyGlobal executes the given operation with the global path lock held.
   329  func (f *fidRef) safelyGlobal(fn func() error) (err error) {
   330  	f.server.renameMu.Lock()
   331  	defer f.server.renameMu.Unlock()
   332  	return fn()
   333  }
   334  
   335  // LookupFID finds the given FID.
   336  //
   337  // You should call fid.DecRef when you are finished using the fid.
   338  func (cs *connState) LookupFID(fid FID) (*fidRef, bool) {
   339  	cs.fidMu.Lock()
   340  	defer cs.fidMu.Unlock()
   341  	fidRef, ok := cs.fids[fid]
   342  	if ok {
   343  		fidRef.IncRef()
   344  		return fidRef, true
   345  	}
   346  	return nil, false
   347  }
   348  
   349  // InsertFID installs the given FID.
   350  //
   351  // This fid starts with a reference count of one. If a FID exists in
   352  // the slot already it is closed, per the specification.
   353  func (cs *connState) InsertFID(fid FID, newRef *fidRef) {
   354  	cs.fidMu.Lock()
   355  	defer cs.fidMu.Unlock()
   356  	origRef, ok := cs.fids[fid]
   357  	if ok {
   358  		defer origRef.DecRef()
   359  	}
   360  	newRef.IncRef()
   361  	cs.fids[fid] = newRef
   362  }
   363  
   364  // DeleteFID removes the given FID.
   365  //
   366  // This simply removes it from the map and drops a reference.
   367  func (cs *connState) DeleteFID(fid FID) bool {
   368  	cs.fidMu.Lock()
   369  	defer cs.fidMu.Unlock()
   370  	fidRef, ok := cs.fids[fid]
   371  	if !ok {
   372  		return false
   373  	}
   374  	delete(cs.fids, fid)
   375  	fidRef.DecRef()
   376  	return true
   377  }
   378  
   379  // StartTag starts handling the tag.
   380  //
   381  // False is returned if this tag is already active.
   382  func (cs *connState) StartTag(t Tag) bool {
   383  	cs.tagMu.Lock()
   384  	defer cs.tagMu.Unlock()
   385  	_, ok := cs.tags[t]
   386  	if ok {
   387  		return false
   388  	}
   389  	cs.tags[t] = make(chan struct{})
   390  	return true
   391  }
   392  
   393  // ClearTag finishes handling a tag.
   394  func (cs *connState) ClearTag(t Tag) {
   395  	cs.tagMu.Lock()
   396  	defer cs.tagMu.Unlock()
   397  	ch, ok := cs.tags[t]
   398  	if !ok {
   399  		// Should never happen.
   400  		panic("unused tag cleared")
   401  	}
   402  	delete(cs.tags, t)
   403  
   404  	// Notify.
   405  	close(ch)
   406  }
   407  
   408  // WaitTag waits for a tag to finish.
   409  func (cs *connState) WaitTag(t Tag) {
   410  	cs.tagMu.Lock()
   411  	ch, ok := cs.tags[t]
   412  	cs.tagMu.Unlock()
   413  	if !ok {
   414  		return
   415  	}
   416  
   417  	// Wait for close.
   418  	<-ch
   419  }
   420  
   421  // initializeChannels initializes all channels.
   422  //
   423  // This is a no-op if channels are already initialized.
   424  func (cs *connState) initializeChannels() (err error) {
   425  	cs.channelMu.Lock()
   426  	defer cs.channelMu.Unlock()
   427  
   428  	// Initialize our channel allocator.
   429  	if cs.channelAlloc == nil {
   430  		alloc, err := flipcall.NewPacketWindowAllocator()
   431  		if err != nil {
   432  			return err
   433  		}
   434  		cs.channelAlloc = alloc
   435  	}
   436  
   437  	// Create all the channels.
   438  	for len(cs.channels) < channelsPerClient {
   439  		res := &channel{
   440  			done: make(chan struct{}),
   441  		}
   442  
   443  		res.desc, err = cs.channelAlloc.Allocate(channelSize)
   444  		if err != nil {
   445  			return err
   446  		}
   447  		if err := res.data.Init(flipcall.ServerSide, res.desc); err != nil {
   448  			return err
   449  		}
   450  
   451  		socks, err := fdchannel.NewConnectedSockets()
   452  		if err != nil {
   453  			res.data.Destroy() // Cleanup.
   454  			return err
   455  		}
   456  		res.fds.Init(socks[0])
   457  		res.client = fd.New(socks[1])
   458  
   459  		cs.channels = append(cs.channels, res)
   460  
   461  		// Start servicing the channel.
   462  		//
   463  		// When we call stop, we will close all the channels and these
   464  		// routines should finish. We need the wait group to ensure
   465  		// that active handlers are actually finished before cleanup.
   466  		cs.channelWg.Add(1)
   467  		go func() { // S/R-SAFE: Server side.
   468  			defer cs.channelWg.Done()
   469  			if err := res.service(cs); err != nil {
   470  				// Don't log flipcall.ShutdownErrors, which we expect to be
   471  				// returned during server shutdown.
   472  				if _, ok := err.(flipcall.ShutdownError); !ok {
   473  					log.Warningf("p9.channel.service: %v", err)
   474  				}
   475  			}
   476  		}()
   477  	}
   478  
   479  	return nil
   480  }
   481  
   482  // lookupChannel looks up the channel with given id.
   483  //
   484  // The function returns nil if no such channel is available.
   485  func (cs *connState) lookupChannel(id uint32) *channel {
   486  	cs.channelMu.Lock()
   487  	defer cs.channelMu.Unlock()
   488  	if id >= uint32(len(cs.channels)) {
   489  		return nil
   490  	}
   491  	return cs.channels[id]
   492  }
   493  
   494  // handle handles a single message.
   495  func (cs *connState) handle(m message) (r message) {
   496  	if !cs.reqGate.Enter() {
   497  		// connState.stop() has been called; the connection is shutting down.
   498  		r = newErrFromLinuxerr(linuxerr.ECONNRESET)
   499  		return
   500  	}
   501  	defer func() {
   502  		cs.reqGate.Leave()
   503  		if r == nil {
   504  			// Don't allow a panic to propagate.
   505  			err := recover()
   506  
   507  			// Include a useful log message.
   508  			log.Warningf("panic in handler: %v\n%s", err, debug.Stack())
   509  
   510  			// Wrap in an EREMOTEIO error; we don't really have a
   511  			// better way to describe this kind of error. It will
   512  			// usually manifest as a result of the test framework.
   513  			r = newErrFromLinuxerr(linuxerr.EREMOTEIO)
   514  		}
   515  	}()
   516  	if handler, ok := m.(handler); ok {
   517  		// Call the message handler.
   518  		r = handler.handle(cs)
   519  		// TODO(b/34162363):This is only here to make sure the server works with
   520  		// only linuxerr Errors, as the handlers work with both client and server.
   521  		// It will be removed a followup, when all the unix.Errno errors are
   522  		// replaced with linuxerr.
   523  		if rlError, ok := r.(*Rlerror); ok {
   524  			e := linuxerr.ErrorFromUnix(unix.Errno(rlError.Error))
   525  			r = newErrFromLinuxerr(e)
   526  		}
   527  	} else {
   528  		// Produce an ENOSYS error.
   529  		r = newErrFromLinuxerr(linuxerr.ENOSYS)
   530  	}
   531  	return
   532  }
   533  
   534  // handleRequest handles a single request. It returns true if the caller should
   535  // continue handling requests and false if it should terminate.
   536  func (cs *connState) handleRequest() bool {
   537  	// Obtain the right to receive a message from cs.conn.
   538  	cs.recvIdle.Add(1)
   539  	cs.recvMu.Lock()
   540  	cs.recvIdle.Add(-1)
   541  
   542  	if cs.recvShutdown {
   543  		// Another goroutine already detected a connection problem; exit
   544  		// immediately.
   545  		cs.recvMu.Unlock()
   546  		return false
   547  	}
   548  
   549  	messageSize := cs.messageSize.Load()
   550  	if messageSize == 0 {
   551  		// Default or not yet negotiated.
   552  		messageSize = maximumLength
   553  	}
   554  
   555  	// Receive a message.
   556  	tag, m, err := recv(cs.conn, messageSize, msgRegistry.get)
   557  	if errSocket, ok := err.(ErrSocket); ok {
   558  		// Connection problem; stop serving.
   559  		log.Debugf("p9.recv: %v", errSocket.error)
   560  		cs.recvShutdown = true
   561  		cs.recvMu.Unlock()
   562  		return false
   563  	}
   564  
   565  	// Ensure that another goroutine is available to receive from cs.conn.
   566  	if cs.recvIdle.Load() == 0 {
   567  		go cs.handleRequests() // S/R-SAFE: Irrelevant.
   568  	}
   569  	cs.recvMu.Unlock()
   570  
   571  	// Deal with other errors.
   572  	if err != nil && err != io.EOF {
   573  		// If it's not a connection error, but some other protocol error,
   574  		// we can send a response immediately.
   575  		cs.sendMu.Lock()
   576  		err := send(cs.conn, tag, newErrFromLinuxerr(err))
   577  		cs.sendMu.Unlock()
   578  		if err != nil {
   579  			log.Debugf("p9.send: %v", err)
   580  		}
   581  		return true
   582  	}
   583  
   584  	// Try to start the tag.
   585  	if !cs.StartTag(tag) {
   586  		// Nothing we can do at this point; client is bogus.
   587  		log.Debugf("no valid tag [%05d]", tag)
   588  		return true
   589  	}
   590  
   591  	// Handle the message.
   592  	r := cs.handle(m)
   593  
   594  	// Clear the tag before sending. That's because as soon as this hits
   595  	// the wire, the client can legally send the same tag.
   596  	cs.ClearTag(tag)
   597  
   598  	// Send back the result.
   599  	cs.sendMu.Lock()
   600  	err = send(cs.conn, tag, r)
   601  	cs.sendMu.Unlock()
   602  	if err != nil {
   603  		log.Debugf("p9.send: %v", err)
   604  	}
   605  
   606  	// Return the message to the cache.
   607  	msgRegistry.put(m)
   608  
   609  	return true
   610  }
   611  
   612  func (cs *connState) handleRequests() {
   613  	for {
   614  		if !cs.handleRequest() {
   615  			return
   616  		}
   617  	}
   618  }
   619  
   620  func (cs *connState) stop() {
   621  	// Stop new requests from proceeding, and wait for completion of all
   622  	// inflight requests. This is mostly so that if a request is stuck, the
   623  	// sandbox supervisor has the opportunity to kill us with SIGABRT to get a
   624  	// stack dump of the offending handler.
   625  	cs.reqGate.Close()
   626  
   627  	// Free the channels.
   628  	cs.channelMu.Lock()
   629  	for _, ch := range cs.channels {
   630  		ch.Shutdown()
   631  	}
   632  	cs.channelWg.Wait()
   633  	for _, ch := range cs.channels {
   634  		ch.Close()
   635  	}
   636  	cs.channels = nil // Clear.
   637  	cs.channelMu.Unlock()
   638  
   639  	// Free the channel memory.
   640  	if cs.channelAlloc != nil {
   641  		cs.channelAlloc.Destroy()
   642  	}
   643  
   644  	// Ensure the connection is closed.
   645  	cs.conn.Close()
   646  
   647  	// Close all remaining fids.
   648  	for fid, fidRef := range cs.fids {
   649  		delete(cs.fids, fid)
   650  
   651  		// Drop final reference in the FID table. Note this should
   652  		// always close the file, since we've ensured that there are no
   653  		// handlers running via the wait for Pending => 0 below.
   654  		fidRef.DecRef()
   655  	}
   656  }
   657  
   658  // Handle handles a single connection.
   659  func (s *Server) Handle(conn *unet.Socket) error {
   660  	cs := &connState{
   661  		server: s,
   662  		fids:   make(map[FID]*fidRef),
   663  		tags:   make(map[Tag]chan struct{}),
   664  		conn:   conn,
   665  	}
   666  	defer cs.stop()
   667  
   668  	// Serve requests from conn in the current goroutine; handleRequests() will
   669  	// create more goroutines as needed.
   670  	cs.handleRequests()
   671  
   672  	return nil
   673  }
   674  
   675  // Serve handles requests from the bound socket.
   676  //
   677  // The passed serverSocket _must_ be created in packet mode.
   678  func (s *Server) Serve(serverSocket *unet.ServerSocket) error {
   679  	var wg sync.WaitGroup
   680  	defer wg.Wait()
   681  
   682  	for {
   683  		conn, err := serverSocket.Accept()
   684  		if err != nil {
   685  			// Something went wrong.
   686  			//
   687  			// Socket closed?
   688  			return err
   689  		}
   690  
   691  		wg.Add(1)
   692  		go func(conn *unet.Socket) { // S/R-SAFE: Irrelevant.
   693  			s.Handle(conn)
   694  			wg.Done()
   695  		}(conn)
   696  	}
   697  }