@@ -308,7 +308,11 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
308308
309309for _ ,b := range binds {
310310if 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).
311314subsystemPath := filepath .Join (c .root ,b .Destination )
315+ subsystemName := filepath .Base (b .Destination )
312316if err := os .MkdirAll (subsystemPath ,0o755 );err != nil {
313317return err
314318}
@@ -319,7 +323,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
319323}
320324var (
321325source = "cgroup"
322- data = filepath . Base ( subsystemPath )
326+ data = subsystemName
323327)
324328if data == "systemd" {
325329data = 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 {
360357return mountViaFds (m .Source ,nil ,m .Destination ,dstFd ,"cgroup2" ,uintptr (m .Flags ),m .Data )
361358})
362359if err == nil || ! (errors .Is (err ,unix .EPERM )|| errors .Is (err ,unix .EBUSY )) {
@@ -482,6 +479,65 @@ func statfsToMountFlags(st unix.Statfs_t) int {
482479return 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 {
486542rootfs := 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.
497553dest := 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.
500556dest = filepath .Join (rootfs ,dest )
501557}
@@ -516,37 +572,19 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
516572return mountPropagate (m ,rootfs ,"" )
517573}
518574
519- mountLabel := c .label
520- dest ,err := securejoin .SecureJoin (rootfs ,m .Destination )
575+ dest ,err := createMountpoint (rootfs ,m )
521576if 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
528581switch m .Device {
529582case "mqueue" :
530- if err := os .MkdirAll (dest ,0o755 );err != nil {
531- return err
532- }
533583if err := mountPropagate (m ,rootfs ,"" );err != nil {
534584return err
535585}
536586return label .SetFileLabel (dest ,mountLabel )
537587case "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-
550588if m .Extensions & configs .EXT_COPYUP == configs .EXT_COPYUP {
551589err = doTmpfsCopyUp (m ,rootfs ,mountLabel )
552590}else {
@@ -555,15 +593,6 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
555593
556594return err
557595case "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.
568597if err := mountPropagate (m ,rootfs ,mountLabel );err != nil {
569598return err
@@ -679,9 +708,6 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
679708}
680709return mountCgroupV1 (m .Mount ,c )
681710default :
682- if err := os .MkdirAll (dest ,0o755 );err != nil {
683- return err
684- }
685711return mountPropagate (m ,rootfs ,mountLabel )
686712}
687713}
@@ -899,6 +925,9 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
899925if err != nil {
900926return err
901927}
928+ if dest == rootfs {
929+ return fmt .Errorf ("%w: mknod over rootfs" ,errRootfsToFile )
930+ }
902931if err := os .MkdirAll (filepath .Dir (dest ),0o755 );err != nil {
903932return err
904933}
@@ -1169,26 +1198,6 @@ func chroot() error {
11691198return 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 {
11941203if err := mount (path ,path ,"" ,unix .MS_BIND | unix .MS_REC ,"" );err != nil {