Set RLimits during runsc exec

PiperOrigin-RevId: 378726430
This commit is contained in:
Fabricio Voznika
2021-06-10 13:55:10 -07:00
committed by gVisor bot
parent 21169357ca
commit d81fcbf85c
3 changed files with 122 additions and 45 deletions
+8 -1
View File
@@ -99,6 +99,9 @@ type ExecArgs struct {
// PIDNamespace is the pid namespace for the process being executed.
PIDNamespace *kernel.PIDNamespace
// Limits is the limit set for the process being executed.
Limits *limits.LimitSet
}
// String prints the arguments as a string.
@@ -151,6 +154,10 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
if pidns == nil {
pidns = proc.Kernel.RootPIDNamespace()
}
limitSet := args.Limits
if limitSet == nil {
limitSet = limits.NewLimitSet()
}
initArgs := kernel.CreateProcessArgs{
Filename: args.Filename,
Argv: args.Argv,
@@ -161,7 +168,7 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
Credentials: creds,
FDTable: fdTable,
Umask: 0022,
Limits: limits.NewLimitSet(),
Limits: limitSet,
MaxSymlinkTraversals: linux.MaxSymlinkTraversals,
UTSNamespace: proc.Kernel.RootUTSNamespace(),
IPCNamespace: proc.Kernel.RootIPCNamespace(),
+6 -1
View File
@@ -963,10 +963,15 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) {
}
args.Envv = envv
}
args.PIDNamespace = tg.PIDNamespace()
args.Limits, err = createLimitSet(l.root.spec)
if err != nil {
return 0, fmt.Errorf("creating limits: %w", err)
}
// Start the process.
proc := control.Proc{Kernel: l.k}
args.PIDNamespace = tg.PIDNamespace()
newTG, tgid, ttyFile, ttyFileVFS2, err := control.ExecAsync(&proc, args)
if err != nil {
return 0, err
+108 -43
View File
@@ -47,6 +47,62 @@ import (
"gvisor.dev/gvisor/runsc/specutils"
)
func TestMain(m *testing.M) {
log.SetLevel(log.Debug)
flag.Parse()
if err := testutil.ConfigureExePath(); err != nil {
panic(err.Error())
}
specutils.MaybeRunAsRoot()
os.Exit(m.Run())
}
func execute(cont *Container, name string, arg ...string) (unix.WaitStatus, error) {
args := &control.ExecArgs{
Filename: name,
Argv: append([]string{name}, arg...),
}
return cont.executeSync(args)
}
func executeCombinedOutput(cont *Container, name string, arg ...string) ([]byte, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
defer r.Close()
args := &control.ExecArgs{
Filename: name,
Argv: append([]string{name}, arg...),
FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, w, w}},
}
ws, err := cont.executeSync(args)
w.Close()
if err != nil {
return nil, err
}
if ws != 0 {
return nil, fmt.Errorf("exec failed, status: %v", ws)
}
out, err := ioutil.ReadAll(r)
return out, err
}
// executeSync synchronously executes a new process.
func (c *Container) executeSync(args *control.ExecArgs) (unix.WaitStatus, error) {
pid, err := c.Execute(args)
if err != nil {
return 0, fmt.Errorf("error executing: %v", err)
}
ws, err := c.WaitPID(pid)
if err != nil {
return 0, fmt.Errorf("error waiting: %v", err)
}
return ws, nil
}
// waitForProcessList waits for the given process list to show up in the container.
func waitForProcessList(cont *Container, want []*control.Process) error {
cb := func() error {
@@ -2470,58 +2526,67 @@ func TestBindMountByOption(t *testing.T) {
}
}
func execute(cont *Container, name string, arg ...string) (unix.WaitStatus, error) {
args := &control.ExecArgs{
Filename: name,
Argv: append([]string{name}, arg...),
// TestRlimits sets limit to number of open files and checks that the limit
// is propagated to the container.
func TestRlimits(t *testing.T) {
file, err := ioutil.TempFile(testutil.TmpDir(), "ulimit")
if err != nil {
t.Fatal(err)
}
cmd := fmt.Sprintf("ulimit -n > %q", file.Name())
spec := testutil.NewSpecWithArgs("sh", "-c", cmd)
spec.Process.Rlimits = []specs.POSIXRlimit{
{Type: "RLIMIT_NOFILE", Hard: 1000, Soft: 100},
}
conf := testutil.TestConfig(t)
if err := run(spec, conf); err != nil {
t.Fatalf("Error running container: %v", err)
}
got, err := ioutil.ReadFile(file.Name())
if err != nil {
t.Fatal(err)
}
if want := "100\n"; string(got) != want {
t.Errorf("ulimit result, got: %q, want: %q", got, want)
}
return cont.executeSync(args)
}
func executeCombinedOutput(cont *Container, name string, arg ...string) ([]byte, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
defer r.Close()
args := &control.ExecArgs{
Filename: name,
Argv: append([]string{name}, arg...),
FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, w, w}},
}
ws, err := cont.executeSync(args)
w.Close()
if err != nil {
return nil, err
}
if ws != 0 {
return nil, fmt.Errorf("exec failed, status: %v", ws)
// TestRlimitsExec sets limit to number of open files and checks that the limit
// is propagated to exec'd processes.
func TestRlimitsExec(t *testing.T) {
spec := testutil.NewSpecWithArgs("sleep", "100")
spec.Process.Rlimits = []specs.POSIXRlimit{
{Type: "RLIMIT_NOFILE", Hard: 1000, Soft: 100},
}
out, err := ioutil.ReadAll(r)
return out, err
}
// executeSync synchronously executes a new process.
func (c *Container) executeSync(args *control.ExecArgs) (unix.WaitStatus, error) {
pid, err := c.Execute(args)
conf := testutil.TestConfig(t)
_, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
if err != nil {
return 0, fmt.Errorf("error executing: %v", err)
t.Fatalf("error setting up container: %v", err)
}
defer cleanup()
args := Args{
ID: testutil.RandomContainerID(),
Spec: spec,
BundleDir: bundleDir,
}
ws, err := c.WaitPID(pid)
cont, err := New(conf, args)
if err != nil {
return 0, fmt.Errorf("error waiting: %v", err)
t.Fatalf("error creating container: %v", err)
}
defer cont.Destroy()
if err := cont.Start(conf); err != nil {
t.Fatalf("error starting container: %v", err)
}
return ws, nil
}
func TestMain(m *testing.M) {
log.SetLevel(log.Debug)
flag.Parse()
if err := testutil.ConfigureExePath(); err != nil {
panic(err.Error())
got, err := executeCombinedOutput(cont, "/bin/sh", "-c", "ulimit -n")
if err != nil {
t.Fatal(err)
}
if want := "100\n"; string(got) != want {
t.Errorf("ulimit result, got: %q, want: %q", got, want)
}
specutils.MaybeRunAsRoot()
os.Exit(m.Run())
}