Skip to content

Commit 4e33fae

Browse files
Merge pull request opencontainers#1570 from cyphar/close-statedirfd-hole
init: switch away from stateDirFd entirely
2 parents ae29480 + 7d66aab commit 4e33fae

File tree

5 files changed

+42
-29
lines changed

5 files changed

+42
-29
lines changed

libcontainer/container_linux.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,23 @@ func (c *linuxContainer) deleteExecFifo() {
350350
os.Remove(fifoName)
351351
}
352352

353+
// includeExecFifo opens the container's execfifo as a pathfd, so that the
354+
// container cannot access the statedir (and the FIFO itself remains
355+
// un-opened). It then adds the FifoFd to the given exec.Cmd as an inherited
356+
// fd, with _LIBCONTAINER_FIFOFD set to its fd number.
357+
func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error {
358+
fifoName := filepath.Join(c.root, execFifoFilename)
359+
fifoFd, err := unix.Open(fifoName, unix.O_PATH|unix.O_CLOEXEC, 0)
360+
if err != nil {
361+
return err
362+
}
363+
364+
cmd.ExtraFiles = append(cmd.ExtraFiles, os.NewFile(uintptr(fifoFd), fifoName))
365+
cmd.Env = append(cmd.Env,
366+
fmt.Sprintf("_LIBCONTAINER_FIFOFD=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
367+
return nil
368+
}
369+
353370
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
354371
parentPipe, childPipe, err := utils.NewSockPair("init")
355372
if err != nil {
@@ -363,18 +380,15 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
363380
return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
364381
}
365382

366-
// We only set up rootDir if we're not doing a `runc exec`. The reason for
367-
// this is to avoid cases where a racing, unprivileged process inside the
368-
// container can get access to the statedir file descriptor (which would
369-
// allow for container rootfs escape).
370-
rootDir, err := os.Open(c.root)
371-
if err != nil {
372-
return nil, err
383+
// We only set up fifoFd if we're not doing a `runc exec`. The historic
384+
// reason for this is that previously we would pass a dirfd that allowed
385+
// for container rootfs escape (and not doing it in `runc exec` avoided
386+
// that problem), but we no longer do that. However, there's no need to do
387+
// this for `runc exec` so we just keep it this way to be safe.
388+
if err := c.includeExecFifo(cmd); err != nil {
389+
return nil, newSystemErrorWithCause(err, "including execfifo in cmd.Exec setup")
373390
}
374-
cmd.ExtraFiles = append(cmd.ExtraFiles, rootDir)
375-
cmd.Env = append(cmd.Env,
376-
fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
377-
return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir)
391+
return c.newInitProcess(p, cmd, parentPipe, childPipe)
378392
}
379393

380394
func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) {
@@ -406,7 +420,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
406420
return cmd, nil
407421
}
408422

409-
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*initProcess, error) {
423+
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) {
410424
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
411425
nsMaps := make(map[configs.NamespaceType]string)
412426
for _, ns := range c.config.Namespaces {
@@ -429,7 +443,6 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
429443
process: p,
430444
bootstrapData: data,
431445
sharePidns: sharePidns,
432-
rootDir: rootDir,
433446
}, nil
434447
}
435448

libcontainer/factory_linux.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,10 @@ func (l *LinuxFactory) Type() string {
233233
// This is a low level implementation detail of the reexec and should not be consumed externally
234234
func (l *LinuxFactory) StartInitialization() (err error) {
235235
var (
236-
pipefd, rootfd int
236+
pipefd, fifofd int
237237
consoleSocket *os.File
238238
envInitPipe = os.Getenv("_LIBCONTAINER_INITPIPE")
239-
envStateDir = os.Getenv("_LIBCONTAINER_STATEDIR")
239+
envFifoFd = os.Getenv("_LIBCONTAINER_FIFOFD")
240240
envConsole = os.Getenv("_LIBCONTAINER_CONSOLE")
241241
)
242242

@@ -252,11 +252,11 @@ func (l *LinuxFactory) StartInitialization() (err error) {
252252
)
253253
defer pipe.Close()
254254

255-
// Only init processes have STATEDIR.
256-
rootfd = -1
255+
// Only init processes have FIFOFD.
256+
fifofd = -1
257257
if it == initStandard {
258-
if rootfd, err = strconv.Atoi(envStateDir); err != nil {
259-
return fmt.Errorf("unable to convert _LIBCONTAINER_STATEDIR=%s to int: %s", envStateDir, err)
258+
if fifofd, err = strconv.Atoi(envFifoFd); err != nil {
259+
return fmt.Errorf("unable to convert _LIBCONTAINER_FIFOFD=%s to int: %s", envFifoFd, err)
260260
}
261261
}
262262

@@ -291,7 +291,7 @@ func (l *LinuxFactory) StartInitialization() (err error) {
291291
}
292292
}()
293293

294-
i, err := newContainerInit(it, pipe, consoleSocket, rootfd)
294+
i, err := newContainerInit(it, pipe, consoleSocket, fifofd)
295295
if err != nil {
296296
return err
297297
}

libcontainer/init_linux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type initer interface {
6868
Init() error
6969
}
7070

71-
func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDirFD int) (initer, error) {
71+
func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd int) (initer, error) {
7272
var config *initConfig
7373
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
7474
return nil, err
@@ -89,7 +89,7 @@ func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDi
8989
consoleSocket: consoleSocket,
9090
parentPid: unix.Getppid(),
9191
config: config,
92-
stateDirFD: stateDirFD,
92+
fifoFd: fifoFd,
9393
}, nil
9494
}
9595
return nil, fmt.Errorf("unknown init type %q", t)

libcontainer/process_linux.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ type initProcess struct {
203203
process *Process
204204
bootstrapData io.Reader
205205
sharePidns bool
206-
rootDir *os.File
207206
}
208207

209208
func (p *initProcess) pid() int {
@@ -258,7 +257,6 @@ func (p *initProcess) start() error {
258257
err := p.cmd.Start()
259258
p.process.ops = p
260259
p.childPipe.Close()
261-
p.rootDir.Close()
262260
if err != nil {
263261
p.process.ops = nil
264262
return newSystemErrorWithCause(err, "starting init process command")

libcontainer/standard_init_linux.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type linuxStandardInit struct {
2222
pipe *os.File
2323
consoleSocket *os.File
2424
parentPid int
25-
stateDirFD int
25+
fifoFd int
2626
config *initConfig
2727
}
2828

@@ -164,9 +164,11 @@ func (l *linuxStandardInit) Init() error {
164164
}
165165
// close the pipe to signal that we have completed our init.
166166
l.pipe.Close()
167-
// wait for the fifo to be opened on the other side before
168-
// exec'ing the users process.
169-
fd, err := unix.Openat(l.stateDirFD, execFifoFilename, os.O_WRONLY|unix.O_CLOEXEC, 0)
167+
// Wait for the FIFO to be opened on the other side before exec-ing the
168+
// user process. We open it through /proc/self/fd/$fd, because the fd that
169+
// was given to us was an O_PATH fd to the fifo itself. Linux allows us to
170+
// re-open an O_PATH fd through /proc.
171+
fd, err := unix.Open(fmt.Sprintf("/proc/self/fd/%d", l.fifoFd), unix.O_WRONLY|unix.O_CLOEXEC, 0)
170172
if err != nil {
171173
return newSystemErrorWithCause(err, "openat exec fifo")
172174
}
@@ -180,7 +182,7 @@ func (l *linuxStandardInit) Init() error {
180182
}
181183
// close the statedir fd before exec because the kernel resets dumpable in the wrong order
182184
// https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1290-L1318
183-
unix.Close(l.stateDirFD)
185+
unix.Close(l.fifoFd)
184186
if err := syscall.Exec(name, l.config.Args[0:], os.Environ()); err != nil {
185187
return newSystemErrorWithCause(err, "exec user process")
186188
}

0 commit comments

Comments
 (0)