From b4a05ce65b2257786b2e48300cdb07f425194196 Mon Sep 17 00:00:00 2001 From: Josh Seba Date: Mon, 18 Jul 2022 14:07:45 -0700 Subject: [PATCH] Add support for /proc/[pid]/limits --- pkg/sentry/fsimpl/proc/task.go | 1 + pkg/sentry/fsimpl/proc/task_files.go | 37 +++++++++++ pkg/sentry/fsimpl/proc/tasks_test.go | 1 + pkg/sentry/limits/limits.go | 97 ++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) diff --git a/pkg/sentry/fsimpl/proc/task.go b/pkg/sentry/fsimpl/proc/task.go index 33111c003..a037037c0 100644 --- a/pkg/sentry/fsimpl/proc/task.go +++ b/pkg/sentry/fsimpl/proc/task.go @@ -64,6 +64,7 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns "fdinfo": fs.newFDInfoDirInode(ctx, task), "gid_map": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0644, &idMapData{task: task, gids: true}), "io": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0400, newIO(task, isThreadGroup)), + "limits": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, &limitsData{task: task}), "maps": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, &mapsData{task: task}), "mem": fs.newMemInode(ctx, task, fs.NextIno(), 0400), "mountinfo": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, &mountInfoData{fs: fs, task: task}), diff --git a/pkg/sentry/fsimpl/proc/task_files.go b/pkg/sentry/fsimpl/proc/task_files.go index 156c420ba..c2794b5fe 100644 --- a/pkg/sentry/fsimpl/proc/task_files.go +++ b/pkg/sentry/fsimpl/proc/task_files.go @@ -529,6 +529,43 @@ func (fd *memFD) SetStat(context.Context, vfs.SetStatOptions) error { // Release implements vfs.FileDescriptionImpl.Release. func (fd *memFD) Release(context.Context) {} +// limitsData implements vfs.DynamicBytesSource for /proc/[pid]/limits. +// +// +stateify savable +type limitsData struct { + kernfs.DynamicBytesFile + + task *kernel.Task +} + +func (d *limitsData) Generate(ctx context.Context, buf *bytes.Buffer) error { + // formatting matches the kernel output from linux/fs/proc/base.c:proc_pid_limits() + fmt.Fprintf(buf, "Limit Soft Limit Hard Limit Units \n") + for _, lt := range limits.AllLimitTypes { + fmt.Fprintf(buf, "%-25s ", lt.Name()) + + l := d.task.Limits().Get(lt) + if l.Cur == limits.Infinity { + fmt.Fprintf(buf, "%-20s ", "unlimited") + } else { + fmt.Fprintf(buf, "%-20d ", l.Cur) + } + + if l.Max == limits.Infinity { + fmt.Fprintf(buf, "%-20s ", "unlimited") + } else { + fmt.Fprintf(buf, "%-20d ", l.Max) + } + + if u := lt.Unit(); u != "" { + fmt.Fprintf(buf, "%-10s", u) + } + + buf.WriteByte('\n') + } + return nil +} + // mapsData implements vfs.DynamicBytesSource for /proc/[pid]/maps. // // +stateify savable diff --git a/pkg/sentry/fsimpl/proc/tasks_test.go b/pkg/sentry/fsimpl/proc/tasks_test.go index d4cc2c507..6d0276670 100644 --- a/pkg/sentry/fsimpl/proc/tasks_test.go +++ b/pkg/sentry/fsimpl/proc/tasks_test.go @@ -78,6 +78,7 @@ var ( "fdinfo": linux.DT_DIR, "gid_map": linux.DT_REG, "io": linux.DT_REG, + "limits": linux.DT_REG, "maps": linux.DT_REG, "mem": linux.DT_REG, "mountinfo": linux.DT_REG, diff --git a/pkg/sentry/limits/limits.go b/pkg/sentry/limits/limits.go index 6dd726e02..c8a47fc31 100644 --- a/pkg/sentry/limits/limits.go +++ b/pkg/sentry/limits/limits.go @@ -43,6 +43,103 @@ const ( Rttime ) +var AllLimitTypes = []LimitType{ + CPU, + FileSize, + Data, + Stack, + Core, + Rss, + ProcessCount, + NumberOfFiles, + MemoryLocked, + AS, + Locks, + SignalsPending, + MessageQueueBytes, + Nice, + RealTimePriority, + Rttime, +} + +// Name returns the kernel name of the limit +func (lt LimitType) Name() string { + switch lt { + case CPU: + return "Max cpu time" + case FileSize: + return "Max file size" + case Data: + return "Max data size" + case Stack: + return "Max stack size" + case Core: + return "Max core file size" + case Rss: + return "Max resident set" + case ProcessCount: + return "Max processes" + case NumberOfFiles: + return "Max open files" + case MemoryLocked: + return "Max locked memory" + case AS: + return "Max address space" + case Locks: + return "Max file locks" + case SignalsPending: + return "Max pending signals" + case MessageQueueBytes: + return "Max msgqueue size" + case Nice: + return "Max nice priority" + case RealTimePriority: + return "Max realtime priority" + case Rttime: + return "Max realtime timeout" + } + return "unknown" +} + +// Unit returns the unit string for a limit +func (lt LimitType) Unit() string { + switch lt { + case CPU: + return "seconds" + case FileSize: + return "bytes" + case Data: + return "bytes" + case Stack: + return "bytes" + case Core: + return "bytes" + case Rss: + return "bytes" + case ProcessCount: + return "processes" + case NumberOfFiles: + return "files" + case MemoryLocked: + return "bytes" + case AS: + return "bytes" + case Locks: + return "locks" + case SignalsPending: + return "signals" + case MessageQueueBytes: + return "bytes" + case Nice: + return "" + case RealTimePriority: + return "" + case Rttime: + return "us" + } + return "" +} + // Infinity is a constant representing a resource with no limit. const Infinity = ^uint64(0)