Use sync.Gate in p9.connState.

sync.WaitGroup.Add(positive delta) is illegal if the WaitGroup counter is zero
and WaitGroup.Wait() may be called concurrently. This is problematic for
p9.connState.pendingWg, which counts inflight requests (so transitions from
zero are normal) and is waited-upon when receiving from the underlying Unix
domain socket returns an error, e.g. during connection shutdown. (Even if the
socket has been closed, new requests can still be concurrently received via
flipcall channels.)

PiperOrigin-RevId: 359416057
This commit is contained in:
Jamie Liu 2021-02-24 18:14:32 -08:00 committed by gVisor bot
parent 1d2975ffbe
commit 0462dfe9f8
1 changed files with 13 additions and 8 deletions

View File

@ -81,8 +81,8 @@ type connState struct {
// version 0 implies 9P2000.L. // version 0 implies 9P2000.L.
version uint32 version uint32
// pendingWg counts requests that are still being handled. // reqGate counts requests that are still being handled.
pendingWg sync.WaitGroup reqGate sync.Gate
// -- below relates to the legacy handler -- // -- below relates to the legacy handler --
@ -481,9 +481,13 @@ func (cs *connState) lookupChannel(id uint32) *channel {
// handle handles a single message. // handle handles a single message.
func (cs *connState) handle(m message) (r message) { func (cs *connState) handle(m message) (r message) {
cs.pendingWg.Add(1) if !cs.reqGate.Enter() {
// connState.stop() has been called; the connection is shutting down.
r = newErr(syscall.ECONNRESET)
return
}
defer func() { defer func() {
cs.pendingWg.Done() cs.reqGate.Leave()
if r == nil { if r == nil {
// Don't allow a panic to propagate. // Don't allow a panic to propagate.
err := recover() err := recover()
@ -594,10 +598,11 @@ func (cs *connState) handleRequests() {
} }
func (cs *connState) stop() { func (cs *connState) stop() {
// Wait for completion of all inflight requests. This is mostly so that if // Stop new requests from proceeding, and wait for completion of all
// a request is stuck, the sandbox supervisor has the opportunity to kill // inflight requests. This is mostly so that if a request is stuck, the
// us with SIGABRT to get a stack dump of the offending handler. // sandbox supervisor has the opportunity to kill us with SIGABRT to get a
cs.pendingWg.Wait() // stack dump of the offending handler.
cs.reqGate.Close()
// Free the channels. // Free the channels.
cs.channelMu.Lock() cs.channelMu.Lock()