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 }