Skip to content

Commit db25439

Browse files
authored
Merge pull request #4417 from kolyshkin/fix-mount-leak
runc run: fix mount leak
2 parents c611a21 + 13a6f56 commit db25439

File tree

1 file changed

+22
-46
lines changed

1 file changed

+22
-46
lines changed

libcontainer/rootfs_linux.go

Lines changed: 22 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -982,54 +982,33 @@ func mknodDevice(dest string, node *devices.Device) error {
982982
return os.Chown(dest, int(node.Uid), int(node.Gid))
983983
}
984984

985-
// Get the parent mount point of directory passed in as argument. Also return
986-
// optional fields.
987-
func getParentMount(rootfs string) (string, string, error) {
988-
mi, err := mountinfo.GetMounts(mountinfo.ParentsFilter(rootfs))
989-
if err != nil {
990-
return "", "", err
991-
}
992-
if len(mi) < 1 {
993-
return "", "", fmt.Errorf("could not find parent mount of %s", rootfs)
994-
}
995-
996-
// find the longest mount point
997-
var idx, maxlen int
998-
for i := range mi {
999-
if len(mi[i].Mountpoint) > maxlen {
1000-
maxlen = len(mi[i].Mountpoint)
1001-
idx = i
985+
// rootfsParentMountPrivate ensures rootfs parent mount is private.
986+
// This is needed for two reasons:
987+
// - pivot_root() will fail if parent mount is shared;
988+
// - when we bind mount rootfs, if its parent is not private, the new mount
989+
// will propagate (leak!) to parent namespace and we don't want that.
990+
func rootfsParentMountPrivate(path string) error {
991+
var err error
992+
// Assuming path is absolute and clean (this is checked in
993+
// libcontainer/validate). Any error other than EINVAL means we failed,
994+
// and EINVAL means this is not a mount point, so traverse up until we
995+
// find one.
996+
for {
997+
err = unix.Mount("", path, "", unix.MS_PRIVATE, "")
998+
if err == nil {
999+
return nil
10021000
}
1003-
}
1004-
return mi[idx].Mountpoint, mi[idx].Optional, nil
1005-
}
1006-
1007-
// Make parent mount private if it was shared
1008-
func rootfsParentMountPrivate(rootfs string) error {
1009-
sharedMount := false
1010-
1011-
parentMount, optionalOpts, err := getParentMount(rootfs)
1012-
if err != nil {
1013-
return err
1014-
}
1015-
1016-
optsSplit := strings.Split(optionalOpts, " ")
1017-
for _, opt := range optsSplit {
1018-
if strings.HasPrefix(opt, "shared:") {
1019-
sharedMount = true
1001+
if err != unix.EINVAL || path == "/" { //nolint:errorlint // unix errors are bare
10201002
break
10211003
}
1004+
path = filepath.Dir(path)
10221005
}
1023-
1024-
// Make parent mount PRIVATE if it was shared. It is needed for two
1025-
// reasons. First of all pivot_root() will fail if parent mount is
1026-
// shared. Secondly when we bind mount rootfs it will propagate to
1027-
// parent namespace and we don't want that to happen.
1028-
if sharedMount {
1029-
return mount("", parentMount, "", unix.MS_PRIVATE, "")
1006+
return &mountError{
1007+
op: "remount-private",
1008+
target: path,
1009+
flags: unix.MS_PRIVATE,
1010+
err: err,
10301011
}
1031-
1032-
return nil
10331012
}
10341013

10351014
func prepareRoot(config *configs.Config) error {
@@ -1041,9 +1020,6 @@ func prepareRoot(config *configs.Config) error {
10411020
return err
10421021
}
10431022

1044-
// Make parent mount private to make sure following bind mount does
1045-
// not propagate in other namespaces. Also it will help with kernel
1046-
// check pass in pivot_root. (IS_SHARED(new_mnt->mnt_parent))
10471023
if err := rootfsParentMountPrivate(config.Rootfs); err != nil {
10481024
return err
10491025
}

0 commit comments

Comments
 (0)