From a853a82677daf631d472b5c842d4182135cd1813 Mon Sep 17 00:00:00 2001 From: lifubang Date: Tue, 30 Apr 2024 23:41:36 +0800 Subject: [PATCH] runc exec: setupRlimits after syscall.rlimit.init() completed Issue: https://github.com/opencontainers/runc/issues/4195 Since https://go-review.googlesource.com/c/go/+/476097, there is a get/set race between runc exec and syscall.rlimit.init, so we need to call setupRlimits after syscall.rlimit.init() completed. Signed-off-by: lifubang --- libcontainer/process_linux.go | 25 +++++++++++++++++-------- libcontainer/setns_init_linux.go | 7 +++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index 93c0e313e..be8249626 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -268,20 +268,26 @@ func (p *setnsProcess) start() (retErr error) { } } } - // set rlimits, this has to be done here because we lose permissions - // to raise the limits once we enter a user-namespace - if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { - return fmt.Errorf("error setting rlimits for process: %w", err) - } + if err := utils.WriteJSON(p.comm.initSockParent, p.config); err != nil { return fmt.Errorf("error writing config to pipe: %w", err) } + var seenProcReady bool ierr := parseSync(p.comm.syncSockParent, func(sync *syncT) error { switch sync.Type { case procReady: - // This shouldn't happen. - panic("unexpected procReady in setns") + seenProcReady = true + // Set rlimits, this has to be done here because we lose permissions + // to raise the limits once we enter a user-namespace + if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { + return fmt.Errorf("error setting rlimits for ready process: %w", err) + } + + // Sync with child. + if err := writeSync(p.comm.syncSockParent, procRun); err != nil { + return err + } case procHooks: // This shouldn't happen. panic("unexpected procHooks in setns") @@ -340,6 +346,9 @@ func (p *setnsProcess) start() (retErr error) { if err := p.comm.syncSockParent.Shutdown(unix.SHUT_WR); err != nil && ierr == nil { return err } + if !seenProcReady && ierr == nil { + ierr = errors.New("procReady not received") + } // Must be done after Shutdown so the child will exit and we can wait for it. if ierr != nil { _, _ = p.wait() @@ -774,7 +783,7 @@ func (p *initProcess) start() (retErr error) { } case procReady: seenProcReady = true - // set rlimits, this has to be done here because we lose permissions + // Set rlimits, this has to be done here because we lose permissions // to raise the limits once we enter a user-namespace if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { return fmt.Errorf("error setting rlimits for ready process: %w", err) diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go index ba48604d9..18745d658 100644 --- a/libcontainer/setns_init_linux.go +++ b/libcontainer/setns_init_linux.go @@ -77,6 +77,13 @@ func (l *linuxSetnsInit) Init() error { } } + // Tell our parent that we're ready to exec. This must be done before the + // Seccomp rules have been applied, because we need to be able to read and + // write to a socket. + if err := syncParentReady(l.pipe); err != nil { + return fmt.Errorf("sync ready: %w", err) + } + if err := selinux.SetExecLabel(l.config.ProcessLabel); err != nil { return err }