Skip to content

Commit 0dd88a9

Browse files
authored
Merge branch 'main' into fix-poststart
2 parents b7f2dab + ad5b481 commit 0dd88a9

File tree

6 files changed

+107
-106
lines changed

6 files changed

+107
-106
lines changed

.github/workflows/validate.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
sudo apt -qy install libseccomp-dev
4141
- uses: golangci/golangci-lint-action@v6
4242
with:
43-
version: v1.57
43+
version: v1.59
4444
# Extra linters, only checking new code from a pull request.
4545
- name: lint-extra
4646
if: github.event_name == 'pull_request'

.golangci.yml

+5
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,8 @@ linters:
1111
- errorlint
1212
- unconvert
1313
- unparam
14+
15+
linters-settings:
16+
govet:
17+
enable:
18+
- nilness

libcontainer/container_linux.go

+7-14
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (c *Container) Status() (Status, error) {
9999
func (c *Container) State() (*State, error) {
100100
c.m.Lock()
101101
defer c.m.Unlock()
102-
return c.currentState()
102+
return c.currentState(), nil
103103
}
104104

105105
// OCIState returns the current container's state information.
@@ -531,7 +531,7 @@ func (c *Container) newParentProcess(p *Process) (parentProcess, error) {
531531
logrus.Debug("runc-dmz: using runc-dmz") // used for tests
532532
} else if errors.Is(err, dmz.ErrNoDmzBinary) {
533533
logrus.Debug("runc-dmz binary not embedded in runc binary, falling back to /proc/self/exe clone")
534-
} else if err != nil {
534+
} else {
535535
return nil, fmt.Errorf("failed to create runc-dmz binary clone: %w", err)
536536
}
537537
} else {
@@ -666,10 +666,7 @@ func (c *Container) newInitProcess(p *Process, cmd *exec.Cmd, comm *processComm)
666666

667667
func (c *Container) newSetnsProcess(p *Process, cmd *exec.Cmd, comm *processComm) (*setnsProcess, error) {
668668
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns))
669-
state, err := c.currentState()
670-
if err != nil {
671-
return nil, fmt.Errorf("unable to get container state: %w", err)
672-
}
669+
state := c.currentState()
673670
// for setns process, we don't have to set cloneflags as the process namespaces
674671
// will only be set via setns syscall
675672
data, err := c.bootstrapData(0, state.NamespacePaths)
@@ -847,12 +844,8 @@ func (c *Container) updateState(process parentProcess) (*State, error) {
847844
if process != nil {
848845
c.initProcess = process
849846
}
850-
state, err := c.currentState()
851-
if err != nil {
852-
return nil, err
853-
}
854-
err = c.saveState(state)
855-
if err != nil {
847+
state := c.currentState()
848+
if err := c.saveState(state); err != nil {
856849
return nil, err
857850
}
858851
return state, nil
@@ -938,7 +931,7 @@ func (c *Container) isPaused() (bool, error) {
938931
return state == configs.Frozen, nil
939932
}
940933

941-
func (c *Container) currentState() (*State, error) {
934+
func (c *Container) currentState() *State {
942935
var (
943936
startTime uint64
944937
externalDescriptors []string
@@ -982,7 +975,7 @@ func (c *Container) currentState() (*State, error) {
982975
}
983976
}
984977
}
985-
return state, nil
978+
return state
986979
}
987980

988981
func (c *Container) currentOCIState() (*specs.State, error) {

libcontainer/criu_linux.go

+7-28
Original file line numberDiff line numberDiff line change
@@ -523,17 +523,7 @@ func (c *Container) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts) {
523523
// restore using CRIU. This function is inspired from the code in
524524
// rootfs_linux.go.
525525
func (c *Container) makeCriuRestoreMountpoints(m *configs.Mount) error {
526-
me := mountEntry{Mount: m}
527-
dest, err := securejoin.SecureJoin(c.config.Rootfs, m.Destination)
528-
if err != nil {
529-
return err
530-
}
531-
// TODO: pass srcFD? Not sure if criu is impacted by issue #2484.
532-
if err := checkProcMount(c.config.Rootfs, dest, me); err != nil {
533-
return err
534-
}
535-
switch m.Device {
536-
case "cgroup":
526+
if m.Device == "cgroup" {
537527
// No mount point(s) need to be created:
538528
//
539529
// * for v1, mount points are saved by CRIU because
@@ -542,23 +532,12 @@ func (c *Container) makeCriuRestoreMountpoints(m *configs.Mount) error {
542532
// * for v2, /sys/fs/cgroup is a real mount, but
543533
// the mountpoint appears as soon as /sys is mounted
544534
return nil
545-
case "bind":
546-
// For bind-mounts (unlike other filesystem types), we need to check if
547-
// the source exists.
548-
fi, _, err := me.srcStat()
549-
if err != nil {
550-
// error out if the source of a bind mount does not exist as we
551-
// will be unable to bind anything to it.
552-
return err
553-
}
554-
if err := createIfNotExists(dest, fi.IsDir()); err != nil {
555-
return err
556-
}
557-
default:
558-
// for all other filesystems just create the mountpoints
559-
if err := os.MkdirAll(dest, 0o755); err != nil {
560-
return err
561-
}
535+
}
536+
// TODO: pass srcFD? Not sure if criu is impacted by issue #2484.
537+
me := mountEntry{Mount: m}
538+
// For all other filesystems, just make the target.
539+
if _, err := createMountpoint(c.config.Rootfs, me); err != nil {
540+
return fmt.Errorf("create criu restore mountpoint for %s mount: %w", me.Destination, err)
562541
}
563542
return nil
564543
}

libcontainer/rootfs_linux.go

+72-63
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,11 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
308308

309309
for _, b := range binds {
310310
if c.cgroupns {
311+
// We just created the tmpfs, and so we can just use filepath.Join
312+
// here (not to mention we want to make sure we create the path
313+
// inside the tmpfs, so we don't want to resolve symlinks).
311314
subsystemPath := filepath.Join(c.root, b.Destination)
315+
subsystemName := filepath.Base(b.Destination)
312316
if err := os.MkdirAll(subsystemPath, 0o755); err != nil {
313317
return err
314318
}
@@ -319,7 +323,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
319323
}
320324
var (
321325
source = "cgroup"
322-
data = filepath.Base(subsystemPath)
326+
data = subsystemName
323327
)
324328
if data == "systemd" {
325329
data = cgroups.CgroupNamePrefix + data
@@ -349,14 +353,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
349353
}
350354

351355
func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
352-
dest, err := securejoin.SecureJoin(c.root, m.Destination)
353-
if err != nil {
354-
return err
355-
}
356-
if err := os.MkdirAll(dest, 0o755); err != nil {
357-
return err
358-
}
359-
err = utils.WithProcfd(c.root, m.Destination, func(dstFd string) error {
356+
err := utils.WithProcfd(c.root, m.Destination, func(dstFd string) error {
360357
return mountViaFds(m.Source, nil, m.Destination, dstFd, "cgroup2", uintptr(m.Flags), m.Data)
361358
})
362359
if err == nil || !(errors.Is(err, unix.EPERM) || errors.Is(err, unix.EBUSY)) {
@@ -482,6 +479,65 @@ func statfsToMountFlags(st unix.Statfs_t) int {
482479
return flags
483480
}
484481

482+
var errRootfsToFile = errors.New("config tries to change rootfs to file")
483+
484+
func createMountpoint(rootfs string, m mountEntry) (string, error) {
485+
dest, err := securejoin.SecureJoin(rootfs, m.Destination)
486+
if err != nil {
487+
return "", err
488+
}
489+
if err := checkProcMount(rootfs, dest, m); err != nil {
490+
return "", fmt.Errorf("check proc-safety of %s mount: %w", m.Destination, err)
491+
}
492+
493+
switch m.Device {
494+
case "bind":
495+
fi, _, err := m.srcStat()
496+
if err != nil {
497+
// Error out if the source of a bind mount does not exist as we
498+
// will be unable to bind anything to it.
499+
return "", err
500+
}
501+
// If the original source is not a directory, make the target a file.
502+
if !fi.IsDir() {
503+
// Make sure we aren't tricked into trying to make the root a file.
504+
if rootfs == dest {
505+
return "", fmt.Errorf("%w: file bind mount over rootfs", errRootfsToFile)
506+
}
507+
// Make the parent directory.
508+
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
509+
return "", fmt.Errorf("make parent dir of file bind-mount: %w", err)
510+
}
511+
// Make the target file.
512+
f, err := os.OpenFile(dest, os.O_CREATE, 0o755)
513+
if err != nil {
514+
return "", fmt.Errorf("create target of file bind-mount: %w", err)
515+
}
516+
_ = f.Close()
517+
// Nothing left to do.
518+
return dest, nil
519+
}
520+
521+
case "tmpfs":
522+
// If the original target exists, copy the mode for the tmpfs mount.
523+
if stat, err := os.Stat(dest); err == nil {
524+
dt := fmt.Sprintf("mode=%04o", syscallMode(stat.Mode()))
525+
if m.Data != "" {
526+
dt = dt + "," + m.Data
527+
}
528+
m.Data = dt
529+
530+
// Nothing left to do.
531+
return dest, nil
532+
}
533+
}
534+
535+
if err := os.MkdirAll(dest, 0o755); err != nil {
536+
return "", err
537+
}
538+
return dest, nil
539+
}
540+
485541
func mountToRootfs(c *mountConfig, m mountEntry) error {
486542
rootfs := c.root
487543

@@ -495,7 +551,7 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
495551
// TODO: This won't be necessary once we switch to libpathrs and we can
496552
// stop all of these symlink-exchange attacks.
497553
dest := filepath.Clean(m.Destination)
498-
if !strings.HasPrefix(dest, rootfs) {
554+
if !utils.IsLexicallyInRoot(rootfs, dest) {
499555
// Do not use securejoin as it resolves symlinks.
500556
dest = filepath.Join(rootfs, dest)
501557
}
@@ -516,37 +572,19 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
516572
return mountPropagate(m, rootfs, "")
517573
}
518574

519-
mountLabel := c.label
520-
dest, err := securejoin.SecureJoin(rootfs, m.Destination)
575+
dest, err := createMountpoint(rootfs, m)
521576
if err != nil {
522-
return err
523-
}
524-
if err := checkProcMount(rootfs, dest, m); err != nil {
525-
return err
577+
return fmt.Errorf("create mountpoint for %s mount: %w", m.Destination, err)
526578
}
579+
mountLabel := c.label
527580

528581
switch m.Device {
529582
case "mqueue":
530-
if err := os.MkdirAll(dest, 0o755); err != nil {
531-
return err
532-
}
533583
if err := mountPropagate(m, rootfs, ""); err != nil {
534584
return err
535585
}
536586
return label.SetFileLabel(dest, mountLabel)
537587
case "tmpfs":
538-
if stat, err := os.Stat(dest); err != nil {
539-
if err := os.MkdirAll(dest, 0o755); err != nil {
540-
return err
541-
}
542-
} else {
543-
dt := fmt.Sprintf("mode=%04o", syscallMode(stat.Mode()))
544-
if m.Data != "" {
545-
dt = dt + "," + m.Data
546-
}
547-
m.Data = dt
548-
}
549-
550588
if m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP {
551589
err = doTmpfsCopyUp(m, rootfs, mountLabel)
552590
} else {
@@ -555,15 +593,6 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
555593

556594
return err
557595
case "bind":
558-
fi, _, err := m.srcStat()
559-
if err != nil {
560-
// error out if the source of a bind mount does not exist as we will be
561-
// unable to bind anything to it.
562-
return err
563-
}
564-
if err := createIfNotExists(dest, fi.IsDir()); err != nil {
565-
return err
566-
}
567596
// open_tree()-related shenanigans are all handled in mountViaFds.
568597
if err := mountPropagate(m, rootfs, mountLabel); err != nil {
569598
return err
@@ -679,9 +708,6 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
679708
}
680709
return mountCgroupV1(m.Mount, c)
681710
default:
682-
if err := os.MkdirAll(dest, 0o755); err != nil {
683-
return err
684-
}
685711
return mountPropagate(m, rootfs, mountLabel)
686712
}
687713
}
@@ -899,6 +925,9 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
899925
if err != nil {
900926
return err
901927
}
928+
if dest == rootfs {
929+
return fmt.Errorf("%w: mknod over rootfs", errRootfsToFile)
930+
}
902931
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
903932
return err
904933
}
@@ -1169,26 +1198,6 @@ func chroot() error {
11691198
return nil
11701199
}
11711200

1172-
// createIfNotExists creates a file or a directory only if it does not already exist.
1173-
func createIfNotExists(path string, isDir bool) error {
1174-
if _, err := os.Stat(path); err != nil {
1175-
if os.IsNotExist(err) {
1176-
if isDir {
1177-
return os.MkdirAll(path, 0o755)
1178-
}
1179-
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
1180-
return err
1181-
}
1182-
f, err := os.OpenFile(path, os.O_CREATE, 0o755)
1183-
if err != nil {
1184-
return err
1185-
}
1186-
_ = f.Close()
1187-
}
1188-
}
1189-
return nil
1190-
}
1191-
11921201
// readonlyPath will make a path read only.
11931202
func readonlyPath(path string) error {
11941203
if err := mount(path, path, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {

libcontainer/utils/utils_unix.go

+15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"path/filepath"
1010
"runtime"
1111
"strconv"
12+
"strings"
1213
"sync"
1314
_ "unsafe" // for go:linkname
1415

@@ -260,3 +261,17 @@ func ProcThreadSelf(subpath string) (string, ProcThreadSelfCloser) {
260261
func ProcThreadSelfFd(fd uintptr) (string, ProcThreadSelfCloser) {
261262
return ProcThreadSelf("fd/" + strconv.FormatUint(uint64(fd), 10))
262263
}
264+
265+
// IsLexicallyInRoot is shorthand for strings.HasPrefix(path+"/", root+"/"),
266+
// but properly handling the case where path or root are "/".
267+
//
268+
// NOTE: The return value only make sense if the path doesn't contain "..".
269+
func IsLexicallyInRoot(root, path string) bool {
270+
if root != "/" {
271+
root += "/"
272+
}
273+
if path != "/" {
274+
path += "/"
275+
}
276+
return strings.HasPrefix(path, root)
277+
}

0 commit comments

Comments
 (0)