Skip to content

Commit eeb7144

Browse files
committed
make exec.fifo can be safety read on readonly tmpfs
Signed-off-by: ningmingxiao <[email protected]>
1 parent 721d066 commit eeb7144

File tree

2 files changed

+40
-69
lines changed

2 files changed

+40
-69
lines changed

libcontainer/container_linux.go

Lines changed: 10 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"golang.org/x/sys/unix"
2121

2222
"github.com/opencontainers/cgroups"
23+
"github.com/opencontainers/runc/internal/linux"
2324
"github.com/opencontainers/runc/libcontainer/configs"
2425
"github.com/opencontainers/runc/libcontainer/exeseal"
2526
"github.com/opencontainers/runc/libcontainer/intelrdt"
@@ -231,74 +232,19 @@ func (c *Container) Exec() error {
231232
}
232233

233234
func (c *Container) exec() error {
234-
path := filepath.Join(c.stateDir, execFifoFilename)
235-
pid := c.initProcess.pid()
236-
blockingFifoOpenCh := awaitFifoOpen(path)
237-
for {
238-
select {
239-
case result := <-blockingFifoOpenCh:
240-
return handleFifoResult(result)
241-
242-
case <-time.After(time.Millisecond * 100):
243-
stat, err := system.Stat(pid)
244-
if err != nil || stat.State == system.Zombie {
245-
// could be because process started, ran, and completed between our 100ms timeout and our system.Stat() check.
246-
// see if the fifo exists and has data (with a non-blocking open, which will succeed if the writing process is complete).
247-
if err := handleFifoResult(fifoOpen(path, false)); err != nil {
248-
return errors.New("container process is already dead")
249-
}
250-
return nil
251-
}
252-
}
253-
}
254-
}
255-
256-
func readFromExecFifo(execFifo io.Reader) error {
257-
data, err := io.ReadAll(execFifo)
235+
fifoPath := filepath.Join(c.stateDir, execFifoFilename)
236+
fd, err := linux.Open(fifoPath, unix.O_WRONLY|unix.O_CLOEXEC, 0)
258237
if err != nil {
259238
return err
260239
}
261-
if len(data) <= 0 {
262-
return errors.New("cannot start an already running container")
240+
defer unix.Close(fd)
241+
if _, err := unix.Write(fd, []byte("0")); err != nil {
242+
return &os.PathError{Op: "write exec fifo", Path: fifoPath, Err: err}
263243
}
264-
return nil
265-
}
266-
267-
func awaitFifoOpen(path string) <-chan openResult {
268-
fifoOpened := make(chan openResult)
269-
go func() {
270-
result := fifoOpen(path, true)
271-
fifoOpened <- result
272-
}()
273-
return fifoOpened
274-
}
275-
276-
func fifoOpen(path string, block bool) openResult {
277-
flags := os.O_RDONLY
278-
if !block {
279-
flags |= unix.O_NONBLOCK
280-
}
281-
f, err := os.OpenFile(path, flags, 0)
282-
if err != nil {
283-
return openResult{err: fmt.Errorf("exec fifo: %w", err)}
284-
}
285-
return openResult{file: f}
286-
}
287-
288-
func handleFifoResult(result openResult) error {
289-
if result.err != nil {
290-
return result.err
291-
}
292-
f := result.file
293-
defer f.Close()
294-
if err := readFromExecFifo(f); err != nil {
244+
if err := os.Remove(fifoPath); os.IsNotExist(err) {
295245
return err
296246
}
297-
err := os.Remove(f.Name())
298-
if err == nil || os.IsNotExist(err) {
299-
return nil
300-
}
301-
return err
247+
return nil
302248
}
303249

304250
type openResult struct {
@@ -450,7 +396,7 @@ func (c *Container) createExecFifo() (retErr error) {
450396
}
451397

452398
fifoName := filepath.Join(c.stateDir, execFifoFilename)
453-
if err := unix.Mkfifo(fifoName, 0o622); err != nil {
399+
if err := unix.Mkfifo(fifoName, 0o644); err != nil {
454400
return &os.PathError{Op: "mkfifo", Path: fifoName, Err: err}
455401
}
456402
defer func() {
@@ -459,7 +405,7 @@ func (c *Container) createExecFifo() (retErr error) {
459405
}
460406
}()
461407
// Ensure permission bits (can be different because of umask).
462-
if err := os.Chmod(fifoName, 0o622); err != nil {
408+
if err := os.Chmod(fifoName, 0o644); err != nil {
463409
return err
464410
}
465411
return os.Chown(fifoName, rootuid, rootgid)

libcontainer/standard_init_linux.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package libcontainer
33
import (
44
"errors"
55
"fmt"
6+
"io"
67
"os"
78
"os/exec"
89

@@ -266,14 +267,15 @@ func (l *linuxStandardInit) Init() error {
266267
// user process. We open it through /proc/self/fd/$fd, because the fd that
267268
// was given to us was an O_PATH fd to the fifo itself. Linux allows us to
268269
// re-open an O_PATH fd through /proc.
269-
fd, err := linux.Open(fifoPath, unix.O_WRONLY|unix.O_CLOEXEC, 0)
270-
if err != nil {
270+
271+
result := fifoOpen(fifoPath, true)
272+
if result.err != nil {
271273
return err
272274
}
273-
if _, err := unix.Write(fd, []byte("0")); err != nil {
274-
return &os.PathError{Op: "write exec fifo", Path: fifoPath, Err: err}
275+
if err := readFromExecFifo(result.file); err != nil {
276+
return err
275277
}
276-
278+
result.file.Close()
277279
// Close the O_PATH fifofd fd before exec because the kernel resets
278280
// dumpable in the wrong order. This has been fixed in newer kernels, but
279281
// we keep this to ensure CVE-2016-9962 doesn't re-emerge on older kernels.
@@ -305,3 +307,26 @@ func (l *linuxStandardInit) Init() error {
305307
}
306308
return linux.Exec(name, l.config.Args, l.config.Env)
307309
}
310+
311+
func fifoOpen(path string, block bool) openResult {
312+
flags := os.O_RDONLY
313+
if !block {
314+
flags |= unix.O_NONBLOCK
315+
}
316+
f, err := os.OpenFile(path, flags, 0)
317+
if err != nil {
318+
return openResult{err: fmt.Errorf("exec fifo: %w", err)}
319+
}
320+
return openResult{file: f}
321+
}
322+
323+
func readFromExecFifo(execFifo io.Reader) error {
324+
data, err := io.ReadAll(execFifo)
325+
if err != nil {
326+
return err
327+
}
328+
if len(data) <= 0 {
329+
return errors.New("cannot start an already running container")
330+
}
331+
return nil
332+
}

0 commit comments

Comments
 (0)