mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
This is a relatively benign data race that's possible when two of the last remaining contexts are calling s.unregisterContext at the same time, and both decrement s.numContexts. One continues to release() the subprocess which puts it into the available pool, but the other one has yet to read s.released. Then, since the subprocess is in the available pool it can be retrieved with fetchAvailable, which did not lock s.mu, which is where the data race happens, because fetchAvailable flips s.released but one of the exiting contexts has yet to read it. The proper fix for this is to start holding references onto the subprocess. We cannot reuse the subprocess while any systrap.contexts are still holding onto sysmsg.ThreadContexts, so each context get's a reference. Each subprocess is held by a MemoryManager as an AddressSpace interface, which calls Release on the subprocess once. This CL also removes subprocessPool.active because it is unnecessary at this moment. PiperOrigin-RevId: 516272892
57 lines
1.7 KiB
Go
57 lines
1.7 KiB
Go
// Copyright 2023 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 systrap
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
// subprocessPool exists to solve these distinct problems:
|
|
//
|
|
// 1) Subprocesses can't always be killed properly (see subprocess.Release).
|
|
// In general it's helpful to be able to reuse subprocesses, but we must observe
|
|
// the subprocess lifecycle before we can do so (e.g. should wait for all
|
|
// contexts to be released).
|
|
//
|
|
// 2) Any seccomp filters that have been installed will apply to subprocesses
|
|
// created here. Therefore we use the intermediary (source), which is created
|
|
// on initialization of the platform.
|
|
type subprocessPool struct {
|
|
mu sync.Mutex
|
|
source *subprocess
|
|
// available stores all subprocesses that are available for reuse.
|
|
// +checklocks:mu
|
|
available []*subprocess
|
|
}
|
|
|
|
func (p *subprocessPool) markAvailable(s *subprocess) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
p.available = append(p.available, s)
|
|
}
|
|
|
|
func (p *subprocessPool) fetchAvailable() *subprocess {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
if len(p.available) > 0 {
|
|
s := p.available[len(p.available)-1]
|
|
p.available = p.available[:len(p.available)-1]
|
|
|
|
return s
|
|
}
|
|
return nil
|
|
}
|