@@ -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
351355func 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+
485541func 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.
11931202func readonlyPath (path string ) error {
11941203 if err := mount (path , path , "" , unix .MS_BIND | unix .MS_REC , "" ); err != nil {
0 commit comments