mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add /proc/[pid]/maps to runsc trace procfs
Updates #7897 PiperOrigin-RevId: 478903608
This commit is contained in:
committed by
gVisor bot
parent
631477c7b8
commit
30182dc10f
@@ -117,10 +117,11 @@ func (a AccessType) Effective() AccessType {
|
||||
|
||||
// Convenient access types.
|
||||
var (
|
||||
NoAccess = AccessType{}
|
||||
Read = AccessType{Read: true}
|
||||
Write = AccessType{Write: true}
|
||||
Execute = AccessType{Execute: true}
|
||||
ReadWrite = AccessType{Read: true, Write: true}
|
||||
AnyAccess = AccessType{Read: true, Write: true, Execute: true}
|
||||
NoAccess = AccessType{}
|
||||
Read = AccessType{Read: true}
|
||||
Write = AccessType{Write: true}
|
||||
Execute = AccessType{Execute: true}
|
||||
ReadWrite = AccessType{Read: true, Write: true}
|
||||
ReadExecute = AccessType{Read: true, Execute: true}
|
||||
AnyAccess = AccessType{Read: true, Write: true, Execute: true}
|
||||
)
|
||||
|
||||
@@ -544,7 +544,7 @@ var _ dynamicInode = (*mapsData)(nil)
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *mapsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
if mm := getMM(d.task); mm != nil {
|
||||
mm.ReadMapsDataInto(ctx, buf)
|
||||
mm.ReadMapsDataInto(ctx, mm.MapsCallbackFuncForBuffer(buf))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
)
|
||||
|
||||
// MapsCallbackFunc has all the parameters required for populating an entry of /proc/[pid]/maps.
|
||||
type MapsCallbackFunc func(start, end hostarch.Addr, permissions hostarch.AccessType, private string, offset uint64, devMajor, devMinor uint32, inode uint64, path string)
|
||||
|
||||
// MemoryManager implements a virtual address space.
|
||||
//
|
||||
// +stateify savable
|
||||
|
||||
+34
-22
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/proc/seqfile"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
)
|
||||
@@ -57,9 +58,32 @@ func (mm *MemoryManager) NeedsUpdate(generation int64) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// MapsCallbackFuncForBuffer creates a /proc/[pid]/maps entry including the trailing newline.
|
||||
func (mm *MemoryManager) MapsCallbackFuncForBuffer(buf *bytes.Buffer) MapsCallbackFunc {
|
||||
return func(start, end hostarch.Addr, permissions hostarch.AccessType, private string, offset uint64, devMajor, devMinor uint32, inode uint64, path string) {
|
||||
// Do not include the guard page: fs/proc/task_mmu.c:show_map_vma() =>
|
||||
// stack_guard_page_start().
|
||||
lineLen, err := fmt.Fprintf(buf, "%08x-%08x %s%s %08x %02x:%02x %d ",
|
||||
start, end, permissions, private, offset, devMajor, devMinor, inode)
|
||||
if err != nil {
|
||||
log.Warningf("Failed to write to buffer with error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
// Per linux, we pad until the 74th character.
|
||||
for pad := 73 - lineLen; pad > 0; pad-- {
|
||||
buf.WriteByte(' ') // never returns a non-nil error
|
||||
}
|
||||
buf.WriteString(path) // never returns a non-nil error
|
||||
}
|
||||
buf.WriteByte('\n') // never returns a non-nil error
|
||||
}
|
||||
}
|
||||
|
||||
// ReadMapsDataInto is called by fsimpl/proc.mapsData.Generate to
|
||||
// implement /proc/[pid]/maps.
|
||||
func (mm *MemoryManager) ReadMapsDataInto(ctx context.Context, buf *bytes.Buffer) {
|
||||
func (mm *MemoryManager) ReadMapsDataInto(ctx context.Context, fn MapsCallbackFunc) {
|
||||
// FIXME(b/235153601): Need to replace RLockBypass with RLockBypass
|
||||
// after fixing b/235153601.
|
||||
mm.mappingMu.RLockBypass()
|
||||
@@ -67,7 +91,7 @@ func (mm *MemoryManager) ReadMapsDataInto(ctx context.Context, buf *bytes.Buffer
|
||||
var start hostarch.Addr
|
||||
|
||||
for vseg := mm.vmas.LowerBoundSegment(start); vseg.Ok(); vseg = vseg.NextSegment() {
|
||||
mm.appendVMAMapsEntryLocked(ctx, vseg, buf)
|
||||
mm.appendVMAMapsEntryLocked(ctx, vseg, fn)
|
||||
}
|
||||
|
||||
// We always emulate vsyscall, so advertise it here. Everything about a
|
||||
@@ -80,7 +104,7 @@ func (mm *MemoryManager) ReadMapsDataInto(ctx context.Context, buf *bytes.Buffer
|
||||
//
|
||||
// Artifically adjust the seqfile handle so we only output vsyscall entry once.
|
||||
if start != vsyscallEnd {
|
||||
buf.WriteString(vsyscallMapsEntry)
|
||||
fn(hostarch.Addr(0xffffffffff600000), hostarch.Addr(0xffffffffff601000), hostarch.ReadExecute, "p", 0, 0, 0, 0, "[vsyscall]")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +153,12 @@ func (mm *MemoryManager) ReadMapsSeqFileData(ctx context.Context, handle seqfile
|
||||
// Preconditions: mm.mappingMu must be locked.
|
||||
func (mm *MemoryManager) vmaMapsEntryLocked(ctx context.Context, vseg vmaIterator) []byte {
|
||||
var b bytes.Buffer
|
||||
mm.appendVMAMapsEntryLocked(ctx, vseg, &b)
|
||||
mm.appendVMAMapsEntryLocked(ctx, vseg, mm.MapsCallbackFuncForBuffer(&b))
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// Preconditions: mm.mappingMu must be locked.
|
||||
func (mm *MemoryManager) appendVMAMapsEntryLocked(ctx context.Context, vseg vmaIterator, b *bytes.Buffer) {
|
||||
func (mm *MemoryManager) appendVMAMapsEntryLocked(ctx context.Context, vseg vmaIterator, fn MapsCallbackFunc) {
|
||||
vma := vseg.ValuePtr()
|
||||
private := "p"
|
||||
if !vma.private {
|
||||
@@ -149,31 +173,19 @@ func (mm *MemoryManager) appendVMAMapsEntryLocked(ctx context.Context, vseg vmaI
|
||||
devMajor := uint32(dev >> devMinorBits)
|
||||
devMinor := uint32(dev & ((1 << devMinorBits) - 1))
|
||||
|
||||
// Do not include the guard page: fs/proc/task_mmu.c:show_map_vma() =>
|
||||
// stack_guard_page_start().
|
||||
lineLen, _ := fmt.Fprintf(b, "%08x-%08x %s%s %08x %02x:%02x %d ",
|
||||
vseg.Start(), vseg.End(), vma.realPerms, private, vma.off, devMajor, devMinor, ino)
|
||||
|
||||
// Figure out our filename or hint.
|
||||
var s string
|
||||
var path string
|
||||
if vma.hint != "" {
|
||||
s = vma.hint
|
||||
path = vma.hint
|
||||
} else if vma.id != nil {
|
||||
// FIXME(jamieliu): We are holding mm.mappingMu here, which is
|
||||
// consistent with Linux's holding mmap_sem in
|
||||
// fs/proc/task_mmu.c:show_map_vma() => fs/seq_file.c:seq_file_path().
|
||||
// However, it's not clear that fs.File.MappedName() is actually
|
||||
// consistent with this lock order.
|
||||
s = vma.id.MappedName(ctx)
|
||||
path = vma.id.MappedName(ctx)
|
||||
}
|
||||
if s != "" {
|
||||
// Per linux, we pad until the 74th character.
|
||||
for pad := 73 - lineLen; pad > 0; pad-- {
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
b.WriteString(s)
|
||||
}
|
||||
b.WriteByte('\n')
|
||||
fn(vseg.Start(), vseg.End(), vma.realPerms, private, vma.off, devMajor, devMinor, ino, path)
|
||||
}
|
||||
|
||||
// ReadSmapsDataInto is called by fsimpl/proc.smapsData.Generate to
|
||||
@@ -239,7 +251,7 @@ func (mm *MemoryManager) vmaSmapsEntryLocked(ctx context.Context, vseg vmaIterat
|
||||
}
|
||||
|
||||
func (mm *MemoryManager) vmaSmapsEntryIntoLocked(ctx context.Context, vseg vmaIterator, b *bytes.Buffer) {
|
||||
mm.appendVMAMapsEntryLocked(ctx, vseg, b)
|
||||
mm.appendVMAMapsEntryLocked(ctx, vseg, mm.MapsCallbackFuncForBuffer(b))
|
||||
vma := vseg.ValuePtr()
|
||||
|
||||
// We take mm.activeMu here in each call to vmaSmapsEntryLocked, instead of
|
||||
|
||||
@@ -9,6 +9,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/log",
|
||||
"//pkg/sentry/fsimpl/proc",
|
||||
"//pkg/sentry/kernel",
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/proc"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
@@ -65,6 +66,18 @@ type Stat struct {
|
||||
SID int32 `json:"sid"`
|
||||
}
|
||||
|
||||
// Mapping contains information for /proc/[pid]/maps.
|
||||
type Mapping struct {
|
||||
Address hostarch.AddrRange `json:"address,omitempty"`
|
||||
Permissions hostarch.AccessType `json:"permissions"`
|
||||
Private string `json:"private,omitempty"`
|
||||
Offset uint64 `json:"offset"`
|
||||
DevMajor uint32 `json:"deviceMajor,omitempty"`
|
||||
DevMinor uint32 `json:"deviceMinor,omitempty"`
|
||||
Inode uint64 `json:"inode,omitempty"`
|
||||
Pathname string `json:"pathname,omitempty"`
|
||||
}
|
||||
|
||||
// ProcessProcfsDump contains the procfs dump for one process. For more details
|
||||
// on fields that directly correspond to /proc fields, see proc(5).
|
||||
type ProcessProcfsDump struct {
|
||||
@@ -92,6 +105,8 @@ type ProcessProcfsDump struct {
|
||||
Status Status `json:"status,omitempty"`
|
||||
// Stat is /proc/[pid]/stat.
|
||||
Stat Stat `json:"stat,omitempty"`
|
||||
// Maps is /proc/[pid]/maps.
|
||||
Maps []Mapping `json:"maps,omitempty"`
|
||||
}
|
||||
|
||||
// getMM returns t's MemoryManager. On success, the MemoryManager's users count
|
||||
@@ -251,6 +266,27 @@ func getStat(t *kernel.Task, pid kernel.ThreadID, pidns *kernel.PIDNamespace) St
|
||||
}
|
||||
}
|
||||
|
||||
func getMappings(ctx context.Context, mm *mm.MemoryManager) []Mapping {
|
||||
var maps []Mapping
|
||||
mm.ReadMapsDataInto(ctx, func(start, end hostarch.Addr, permissions hostarch.AccessType, private string, offset uint64, devMajor, devMinor uint32, inode uint64, path string) {
|
||||
maps = append(maps, Mapping{
|
||||
Address: hostarch.AddrRange{
|
||||
Start: start,
|
||||
End: end,
|
||||
},
|
||||
Permissions: permissions,
|
||||
Private: private,
|
||||
Offset: offset,
|
||||
DevMajor: devMajor,
|
||||
DevMinor: devMinor,
|
||||
Inode: inode,
|
||||
Pathname: path,
|
||||
})
|
||||
})
|
||||
|
||||
return maps
|
||||
}
|
||||
|
||||
// Dump returns a procfs dump for process pid. t must be a task in process pid.
|
||||
func Dump(t *kernel.Task, pid kernel.ThreadID, pidns *kernel.PIDNamespace) (ProcessProcfsDump, error) {
|
||||
ctx := t.AsyncContext()
|
||||
@@ -282,5 +318,6 @@ func Dump(t *kernel.Task, pid kernel.ThreadID, pidns *kernel.PIDNamespace) (Proc
|
||||
Cgroup: t.GetCgroupEntries(),
|
||||
Status: getStatus(t, mm, pid, pidns),
|
||||
Stat: getStat(t, pid, pidns),
|
||||
Maps: getMappings(ctx, mm),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -439,4 +439,14 @@ func TestProcfsDump(t *testing.T) {
|
||||
if procfsDump[0].Status.VMRSS == 0 {
|
||||
t.Errorf("expected VMSize to be set")
|
||||
}
|
||||
if len(procfsDump[0].Maps) <= 0 {
|
||||
t.Errorf("no region mapped for pid:%v", procfsDump[0].Status.PID)
|
||||
}
|
||||
|
||||
maps := procfsDump[0].Maps
|
||||
for i := 0; i < len(procfsDump[0].Maps)-1; i++ {
|
||||
if maps[i].Address.Overlaps(maps[i+1].Address) {
|
||||
t.Errorf("overlapped addresses for pid:%v", procfsDump[0].Status.PID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user