mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Fix lock order violations: mm.mappingMu > Task.mu.
Document this ordering in mm/mm.go. PiperOrigin-RevId: 393413203
This commit is contained in:
committed by
gVisor bot
parent
0db19ea910
commit
f7281c6cb9
@@ -107,7 +107,7 @@ func (f *execArgFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequen
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
m, err := getTaskMM(f.t)
|
||||
m, err := getTaskMMIncRef(f.t)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
+49
-67
@@ -41,10 +41,24 @@ import (
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// getTaskMM returns t's MemoryManager. If getTaskMM succeeds, the MemoryManager's
|
||||
// users count is incremented, and must be decremented by the caller when it is
|
||||
// no longer in use.
|
||||
func getTaskMM(t *kernel.Task) (*mm.MemoryManager, error) {
|
||||
// getTaskMM gets the kernel task's MemoryManager. No additional reference is
|
||||
// taken on mm here. This is safe because MemoryManager.destroy is required to
|
||||
// leave the MemoryManager in a state where it's still usable as a
|
||||
// DynamicBytesSource.
|
||||
func getTaskMM(t *kernel.Task) *mm.MemoryManager {
|
||||
var tmm *mm.MemoryManager
|
||||
t.WithMuLocked(func(t *kernel.Task) {
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
tmm = mm
|
||||
}
|
||||
})
|
||||
return tmm
|
||||
}
|
||||
|
||||
// getTaskMMIncRef returns t's MemoryManager. If getTaskMMIncRef succeeds, the
|
||||
// MemoryManager's users count is incremented, and must be decremented by the
|
||||
// caller when it is no longer in use.
|
||||
func getTaskMMIncRef(t *kernel.Task) (*mm.MemoryManager, error) {
|
||||
if t.ExitState() == kernel.TaskExitDead {
|
||||
return nil, linuxerr.ESRCH
|
||||
}
|
||||
@@ -269,21 +283,18 @@ func (e *exe) executable() (file fsbridge.File, err error) {
|
||||
if err := checkTaskState(e.t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.t.WithMuLocked(func(t *kernel.Task) {
|
||||
mm := t.MemoryManager()
|
||||
if mm == nil {
|
||||
err = linuxerr.EACCES
|
||||
return
|
||||
}
|
||||
mm := getTaskMM(e.t)
|
||||
if mm == nil {
|
||||
return nil, linuxerr.EACCES
|
||||
}
|
||||
|
||||
// The MemoryManager may be destroyed, in which case
|
||||
// MemoryManager.destroy will simply set the executable to nil
|
||||
// (with locks held).
|
||||
file = mm.Executable()
|
||||
if file == nil {
|
||||
err = linuxerr.ESRCH
|
||||
}
|
||||
})
|
||||
// The MemoryManager may be destroyed, in which case
|
||||
// MemoryManager.destroy will simply set the executable to nil
|
||||
// (with locks held).
|
||||
file = mm.Executable()
|
||||
if file == nil {
|
||||
err = linuxerr.ESRCH
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -463,7 +474,7 @@ func (m *memDataFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequen
|
||||
if dst.NumBytes() == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
mm, err := getTaskMM(m.t)
|
||||
mm, err := getTaskMMIncRef(m.t)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -494,22 +505,9 @@ func newMaps(ctx context.Context, t *kernel.Task, msrc *fs.MountSource) *fs.Inod
|
||||
return newProcInode(ctx, seqfile.NewSeqFile(ctx, &mapsData{t}), msrc, fs.SpecialFile, t)
|
||||
}
|
||||
|
||||
func (md *mapsData) mm() *mm.MemoryManager {
|
||||
var tmm *mm.MemoryManager
|
||||
md.t.WithMuLocked(func(t *kernel.Task) {
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
// No additional reference is taken on mm here. This is safe
|
||||
// because MemoryManager.destroy is required to leave the
|
||||
// MemoryManager in a state where it's still usable as a SeqSource.
|
||||
tmm = mm
|
||||
}
|
||||
})
|
||||
return tmm
|
||||
}
|
||||
|
||||
// NeedsUpdate implements seqfile.SeqSource.NeedsUpdate.
|
||||
func (md *mapsData) NeedsUpdate(generation int64) bool {
|
||||
if mm := md.mm(); mm != nil {
|
||||
if mm := getTaskMM(md.t); mm != nil {
|
||||
return mm.NeedsUpdate(generation)
|
||||
}
|
||||
return true
|
||||
@@ -517,7 +515,7 @@ func (md *mapsData) NeedsUpdate(generation int64) bool {
|
||||
|
||||
// ReadSeqFileData implements seqfile.SeqSource.ReadSeqFileData.
|
||||
func (md *mapsData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([]seqfile.SeqData, int64) {
|
||||
if mm := md.mm(); mm != nil {
|
||||
if mm := getTaskMM(md.t); mm != nil {
|
||||
return mm.ReadMapsSeqFileData(ctx, h)
|
||||
}
|
||||
return []seqfile.SeqData{}, 0
|
||||
@@ -534,22 +532,9 @@ func newSmaps(ctx context.Context, t *kernel.Task, msrc *fs.MountSource) *fs.Ino
|
||||
return newProcInode(ctx, seqfile.NewSeqFile(ctx, &smapsData{t}), msrc, fs.SpecialFile, t)
|
||||
}
|
||||
|
||||
func (sd *smapsData) mm() *mm.MemoryManager {
|
||||
var tmm *mm.MemoryManager
|
||||
sd.t.WithMuLocked(func(t *kernel.Task) {
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
// No additional reference is taken on mm here. This is safe
|
||||
// because MemoryManager.destroy is required to leave the
|
||||
// MemoryManager in a state where it's still usable as a SeqSource.
|
||||
tmm = mm
|
||||
}
|
||||
})
|
||||
return tmm
|
||||
}
|
||||
|
||||
// NeedsUpdate implements seqfile.SeqSource.NeedsUpdate.
|
||||
func (sd *smapsData) NeedsUpdate(generation int64) bool {
|
||||
if mm := sd.mm(); mm != nil {
|
||||
if mm := getTaskMM(sd.t); mm != nil {
|
||||
return mm.NeedsUpdate(generation)
|
||||
}
|
||||
return true
|
||||
@@ -557,7 +542,7 @@ func (sd *smapsData) NeedsUpdate(generation int64) bool {
|
||||
|
||||
// ReadSeqFileData implements seqfile.SeqSource.ReadSeqFileData.
|
||||
func (sd *smapsData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([]seqfile.SeqData, int64) {
|
||||
if mm := sd.mm(); mm != nil {
|
||||
if mm := getTaskMM(sd.t); mm != nil {
|
||||
return mm.ReadSmapsSeqFileData(ctx, h)
|
||||
}
|
||||
return []seqfile.SeqData{}, 0
|
||||
@@ -627,12 +612,10 @@ func (s *taskStatData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle)
|
||||
fmt.Fprintf(&buf, "%d ", linux.ClockTFromDuration(s.t.StartTime().Sub(s.t.Kernel().Timekeeper().BootTime())))
|
||||
|
||||
var vss, rss uint64
|
||||
s.t.WithMuLocked(func(t *kernel.Task) {
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
})
|
||||
if mm := getTaskMM(s.t); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
fmt.Fprintf(&buf, "%d %d ", vss, rss/hostarch.PageSize)
|
||||
|
||||
// rsslim.
|
||||
@@ -677,12 +660,10 @@ func (s *statmData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([
|
||||
}
|
||||
|
||||
var vss, rss uint64
|
||||
s.t.WithMuLocked(func(t *kernel.Task) {
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
})
|
||||
if mm := getTaskMM(s.t); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "%d %d 0 0 0 0 0\n", vss/hostarch.PageSize, rss/hostarch.PageSize)
|
||||
@@ -734,12 +715,13 @@ func (s *statusData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) (
|
||||
if fdTable := t.FDTable(); fdTable != nil {
|
||||
fds = fdTable.CurrentMaxFDs()
|
||||
}
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
data = mm.VirtualDataSize()
|
||||
}
|
||||
})
|
||||
|
||||
if mm := getTaskMM(s.t); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
data = mm.VirtualDataSize()
|
||||
}
|
||||
fmt.Fprintf(&buf, "FDSize:\t%d\n", fds)
|
||||
fmt.Fprintf(&buf, "VmSize:\t%d kB\n", vss>>10)
|
||||
fmt.Fprintf(&buf, "VmRSS:\t%d kB\n", rss>>10)
|
||||
@@ -925,7 +907,7 @@ func (f *auxvecFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequenc
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
m, err := getTaskMM(f.t)
|
||||
m, err := getTaskMMIncRef(f.t)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import (
|
||||
// Linux 3.18, the limit is five lines." - user_namespaces(7)
|
||||
const maxIDMapLines = 5
|
||||
|
||||
// mm gets the kernel task's MemoryManager. No additional reference is taken on
|
||||
// getMM gets the kernel task's MemoryManager. No additional reference is taken on
|
||||
// mm here. This is safe because MemoryManager.destroy is required to leave the
|
||||
// MemoryManager in a state where it's still usable as a DynamicBytesSource.
|
||||
func getMM(task *kernel.Task) *mm.MemoryManager {
|
||||
@@ -608,12 +608,10 @@ func (s *taskStatData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
fmt.Fprintf(buf, "%d ", linux.ClockTFromDuration(s.task.StartTime().Sub(s.task.Kernel().Timekeeper().BootTime())))
|
||||
|
||||
var vss, rss uint64
|
||||
s.task.WithMuLocked(func(t *kernel.Task) {
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
})
|
||||
if mm := getMM(s.task); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
fmt.Fprintf(buf, "%d %d ", vss, rss/hostarch.PageSize)
|
||||
|
||||
// rsslim.
|
||||
@@ -649,13 +647,10 @@ var _ dynamicInode = (*statmData)(nil)
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (s *statmData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
var vss, rss uint64
|
||||
s.task.WithMuLocked(func(t *kernel.Task) {
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
})
|
||||
|
||||
if mm := getMM(s.task); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
}
|
||||
fmt.Fprintf(buf, "%d %d 0 0 0 0 0\n", vss/hostarch.PageSize, rss/hostarch.PageSize)
|
||||
return nil
|
||||
}
|
||||
@@ -779,12 +774,12 @@ func (s *statusFD) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
if fdTable := t.FDTable(); fdTable != nil {
|
||||
fds = fdTable.CurrentMaxFDs()
|
||||
}
|
||||
if mm := t.MemoryManager(); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
data = mm.VirtualDataSize()
|
||||
}
|
||||
})
|
||||
if mm := getMM(s.task); mm != nil {
|
||||
vss = mm.VirtualMemorySize()
|
||||
rss = mm.ResidentSetSize()
|
||||
data = mm.VirtualDataSize()
|
||||
}
|
||||
// Filesystem user/group IDs aren't implemented; effective UID/GID are used
|
||||
// instead.
|
||||
fmt.Fprintf(buf, "Uid:\t%d\t%d\t%d\t%d\n", ruid, euid, suid, euid)
|
||||
@@ -945,25 +940,17 @@ func (s *exeSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDent
|
||||
return vfs.VirtualDentry{}, "", err
|
||||
}
|
||||
|
||||
var err error
|
||||
var exec fsbridge.File
|
||||
s.task.WithMuLocked(func(t *kernel.Task) {
|
||||
mm := t.MemoryManager()
|
||||
if mm == nil {
|
||||
err = linuxerr.EACCES
|
||||
return
|
||||
}
|
||||
mm := getMM(s.task)
|
||||
if mm == nil {
|
||||
return vfs.VirtualDentry{}, "", linuxerr.EACCES
|
||||
}
|
||||
|
||||
// The MemoryManager may be destroyed, in which case
|
||||
// MemoryManager.destroy will simply set the executable to nil
|
||||
// (with locks held).
|
||||
exec = mm.Executable()
|
||||
if exec == nil {
|
||||
err = linuxerr.ESRCH
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return vfs.VirtualDentry{}, "", err
|
||||
// The MemoryManager may be destroyed, in which case
|
||||
// MemoryManager.destroy will simply set the executable to nil
|
||||
// (with locks held).
|
||||
exec := mm.Executable()
|
||||
if exec == nil {
|
||||
return vfs.VirtualDentry{}, "", linuxerr.ESRCH
|
||||
}
|
||||
defer exec.DecRef(ctx)
|
||||
|
||||
|
||||
@@ -222,9 +222,15 @@ func (r *runSyscallAfterExecStop) execute(t *Task) taskRunState {
|
||||
// Update credentials to reflect the execve. This should precede switching
|
||||
// MMs to ensure that dumpability has been reset first, if needed.
|
||||
t.updateCredsForExecLocked()
|
||||
t.image.release()
|
||||
oldImage := t.image
|
||||
t.image = *r.image
|
||||
t.mu.Unlock()
|
||||
|
||||
// Don't hold t.mu while calling t.image.release(), that may
|
||||
// attempt to acquire TaskImage.MemoryManager.mappingMu, a lock order
|
||||
// violation.
|
||||
oldImage.release()
|
||||
|
||||
t.unstopVforkParent()
|
||||
t.p.FullStateChanged()
|
||||
// NOTE(b/30316266): All locks must be dropped prior to calling Activate.
|
||||
|
||||
@@ -230,9 +230,16 @@ func (*runExitMain) execute(t *Task) taskRunState {
|
||||
t.tg.pidns.owner.mu.Lock()
|
||||
t.updateRSSLocked()
|
||||
t.tg.pidns.owner.mu.Unlock()
|
||||
|
||||
// Release the task image resources. Accessing these fields must be
|
||||
// done with t.mu held, but the mm.DecUsers() call must be done outside
|
||||
// of that lock.
|
||||
t.mu.Lock()
|
||||
t.image.release()
|
||||
mm := t.image.MemoryManager
|
||||
t.image.MemoryManager = nil
|
||||
t.image.fu = nil
|
||||
t.mu.Unlock()
|
||||
mm.DecUsers(t)
|
||||
|
||||
// Releasing the MM unblocks a blocked CLONE_VFORK parent.
|
||||
t.unstopVforkParent()
|
||||
|
||||
@@ -53,7 +53,7 @@ type TaskImage struct {
|
||||
}
|
||||
|
||||
// release releases all resources held by the TaskImage. release is called by
|
||||
// the task when it execs into a new TaskImage or exits.
|
||||
// the task when it execs into a new TaskImage.
|
||||
func (image *TaskImage) release() {
|
||||
// Nil out pointers so that if the task is saved after release, it doesn't
|
||||
// follow the pointers to possibly now-invalid objects.
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
// memmap.File locks
|
||||
// mm.aioManager.mu
|
||||
// mm.AIOContext.mu
|
||||
// kernel.TaskSet.mu
|
||||
//
|
||||
// Only mm.MemoryManager.Fork is permitted to lock mm.MemoryManager.activeMu in
|
||||
// multiple mm.MemoryManagers, as it does so in a well-defined order (forked
|
||||
|
||||
Reference in New Issue
Block a user