Merge pull request #1274 from cyphar/further-CVE-2016-9962-cleanup

libcontainer: init: only pass stateDirFd when creating a container
This commit is contained in:
Michael Crosby 2017-02-02 11:11:42 -08:00 committed by GitHub
commit 9073486547
5 changed files with 41 additions and 39 deletions

View File

@ -296,21 +296,29 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
if err != nil { if err != nil {
return nil, newSystemErrorWithCause(err, "creating new init pipe") return nil, newSystemErrorWithCause(err, "creating new init pipe")
} }
rootDir, err := os.Open(c.root) cmd, err := c.commandTemplate(p, childPipe)
if err != nil {
return nil, err
}
cmd, err := c.commandTemplate(p, childPipe, rootDir)
if err != nil { if err != nil {
return nil, newSystemErrorWithCause(err, "creating new command template") return nil, newSystemErrorWithCause(err, "creating new command template")
} }
if !doInit { if !doInit {
return c.newSetnsProcess(p, cmd, parentPipe, childPipe, rootDir) return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
} }
// We only set up rootDir if we're not doing a `runc exec`. The reason for
// this is to avoid cases where a racing, unprivileged process inside the
// container can get access to the statedir file descriptor (which would
// allow for container rootfs escape).
rootDir, err := os.Open(c.root)
if err != nil {
return nil, err
}
cmd.ExtraFiles = append(cmd.ExtraFiles, rootDir)
cmd.Env = append(cmd.Env,
fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir) return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir)
} }
func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File) (*exec.Cmd, error) { func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) {
cmd := exec.Command(c.initArgs[0], c.initArgs[1:]...) cmd := exec.Command(c.initArgs[0], c.initArgs[1:]...)
cmd.Stdin = p.Stdin cmd.Stdin = p.Stdin
cmd.Stdout = p.Stdout cmd.Stdout = p.Stdout
@ -319,10 +327,9 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File
if cmd.SysProcAttr == nil { if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.SysProcAttr = &syscall.SysProcAttr{}
} }
cmd.ExtraFiles = append(p.ExtraFiles, childPipe, rootDir) cmd.ExtraFiles = append(p.ExtraFiles, childPipe)
cmd.Env = append(cmd.Env, cmd.Env = append(cmd.Env,
fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-2), fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
// NOTE: when running a container with no PID namespace and the parent process spawning the container is // NOTE: when running a container with no PID namespace and the parent process spawning the container is
// PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason // PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason
// even with the parent still running. // even with the parent still running.
@ -360,7 +367,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
}, nil }, nil
} }
func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*setnsProcess, error) { func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*setnsProcess, error) {
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns)) cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns))
state, err := c.currentState() state, err := c.currentState()
if err != nil { if err != nil {
@ -382,7 +389,6 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
config: c.newInitConfig(p), config: c.newInitConfig(p),
process: p, process: p,
bootstrapData: data, bootstrapData: data,
rootDir: rootDir,
}, nil }, nil
} }

View File

@ -222,29 +222,33 @@ func (l *LinuxFactory) Type() string {
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state // StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
// This is a low level implementation detail of the reexec and should not be consumed externally // This is a low level implementation detail of the reexec and should not be consumed externally
func (l *LinuxFactory) StartInitialization() (err error) { func (l *LinuxFactory) StartInitialization() (err error) {
var pipefd, rootfd int var (
for _, pair := range []struct { pipefd, rootfd int
k string envInitPipe = os.Getenv("_LIBCONTAINER_INITPIPE")
v *int envStateDir = os.Getenv("_LIBCONTAINER_STATEDIR")
}{ )
{"_LIBCONTAINER_INITPIPE", &pipefd},
{"_LIBCONTAINER_STATEDIR", &rootfd},
} {
s := os.Getenv(pair.k) // Get the INITPIPE.
pipefd, err = strconv.Atoi(envInitPipe)
i, err := strconv.Atoi(s)
if err != nil { if err != nil {
return fmt.Errorf("unable to convert %s=%s to int", pair.k, s) return fmt.Errorf("unable to convert _LIBCONTAINER_INITPIPE=%s to int: %s", envInitPipe, err)
}
*pair.v = i
} }
var ( var (
pipe = os.NewFile(uintptr(pipefd), "pipe") pipe = os.NewFile(uintptr(pipefd), "pipe")
it = initType(os.Getenv("_LIBCONTAINER_INITTYPE")) it = initType(os.Getenv("_LIBCONTAINER_INITTYPE"))
) )
defer pipe.Close() defer pipe.Close()
// Only init processes have STATEDIR.
rootfd = -1
if it == initStandard {
rootfd, err = strconv.Atoi(envStateDir)
if err != nil {
return fmt.Errorf("unable to convert _LIBCONTAINER_STATEDIR=%s to int: %s", envStateDir, err)
}
}
// clear the current process's environment to clean any libcontainer // clear the current process's environment to clean any libcontainer
// specific env vars. // specific env vars.
os.Clearenv() os.Clearenv()

View File

@ -79,7 +79,6 @@ func newContainerInit(t initType, pipe *os.File, stateDirFD int) (initer, error)
return &linuxSetnsInit{ return &linuxSetnsInit{
pipe: pipe, pipe: pipe,
config: config, config: config,
stateDirFD: stateDirFD,
}, nil }, nil
case initStandard: case initStandard:
return &linuxStandardInit{ return &linuxStandardInit{

View File

@ -51,7 +51,6 @@ type setnsProcess struct {
fds []string fds []string
process *Process process *Process
bootstrapData io.Reader bootstrapData io.Reader
rootDir *os.File
} }
func (p *setnsProcess) startTime() (string, error) { func (p *setnsProcess) startTime() (string, error) {
@ -70,7 +69,6 @@ func (p *setnsProcess) start() (err error) {
defer p.parentPipe.Close() defer p.parentPipe.Close()
err = p.cmd.Start() err = p.cmd.Start()
p.childPipe.Close() p.childPipe.Close()
p.rootDir.Close()
if err != nil { if err != nil {
return newSystemErrorWithCause(err, "starting setns process") return newSystemErrorWithCause(err, "starting setns process")
} }

View File

@ -5,7 +5,6 @@ package libcontainer
import ( import (
"fmt" "fmt"
"os" "os"
"syscall"
"github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/opencontainers/runc/libcontainer/keys" "github.com/opencontainers/runc/libcontainer/keys"
@ -19,7 +18,6 @@ import (
type linuxSetnsInit struct { type linuxSetnsInit struct {
pipe *os.File pipe *os.File
config *initConfig config *initConfig
stateDirFD int
} }
func (l *linuxSetnsInit) getSessionRingName() string { func (l *linuxSetnsInit) getSessionRingName() string {
@ -60,8 +58,5 @@ func (l *linuxSetnsInit) Init() error {
if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil { if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil {
return err return err
} }
// close the statedir fd before exec because the kernel resets dumpable in the wrong order
// https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1290-L1318
syscall.Close(l.stateDirFD)
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ()) return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
} }