@@ -308,7 +308,11 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
308
308
309
309
for _ , b := range binds {
310
310
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).
311
314
subsystemPath := filepath .Join (c .root , b .Destination )
315
+ subsystemName := filepath .Base (b .Destination )
312
316
if err := os .MkdirAll (subsystemPath , 0o755 ); err != nil {
313
317
return err
314
318
}
@@ -319,7 +323,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
319
323
}
320
324
var (
321
325
source = "cgroup"
322
- data = filepath . Base ( subsystemPath )
326
+ data = subsystemName
323
327
)
324
328
if data == "systemd" {
325
329
data = cgroups .CgroupNamePrefix + data
@@ -349,14 +353,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
349
353
}
350
354
351
355
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 {
360
357
return mountViaFds (m .Source , nil , m .Destination , dstFd , "cgroup2" , uintptr (m .Flags ), m .Data )
361
358
})
362
359
if err == nil || ! (errors .Is (err , unix .EPERM ) || errors .Is (err , unix .EBUSY )) {
@@ -482,6 +479,65 @@ func statfsToMountFlags(st unix.Statfs_t) int {
482
479
return flags
483
480
}
484
481
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
+
485
541
func mountToRootfs (c * mountConfig , m mountEntry ) error {
486
542
rootfs := c .root
487
543
@@ -495,7 +551,7 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
495
551
// TODO: This won't be necessary once we switch to libpathrs and we can
496
552
// stop all of these symlink-exchange attacks.
497
553
dest := filepath .Clean (m .Destination )
498
- if ! strings . HasPrefix ( dest , rootfs ) {
554
+ if ! utils . IsLexicallyInRoot ( rootfs , dest ) {
499
555
// Do not use securejoin as it resolves symlinks.
500
556
dest = filepath .Join (rootfs , dest )
501
557
}
@@ -516,37 +572,19 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
516
572
return mountPropagate (m , rootfs , "" )
517
573
}
518
574
519
- mountLabel := c .label
520
- dest , err := securejoin .SecureJoin (rootfs , m .Destination )
575
+ dest , err := createMountpoint (rootfs , m )
521
576
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 )
526
578
}
579
+ mountLabel := c .label
527
580
528
581
switch m .Device {
529
582
case "mqueue" :
530
- if err := os .MkdirAll (dest , 0o755 ); err != nil {
531
- return err
532
- }
533
583
if err := mountPropagate (m , rootfs , "" ); err != nil {
534
584
return err
535
585
}
536
586
return label .SetFileLabel (dest , mountLabel )
537
587
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
-
550
588
if m .Extensions & configs .EXT_COPYUP == configs .EXT_COPYUP {
551
589
err = doTmpfsCopyUp (m , rootfs , mountLabel )
552
590
} else {
@@ -555,15 +593,6 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
555
593
556
594
return err
557
595
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
- }
567
596
// open_tree()-related shenanigans are all handled in mountViaFds.
568
597
if err := mountPropagate (m , rootfs , mountLabel ); err != nil {
569
598
return err
@@ -679,9 +708,6 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
679
708
}
680
709
return mountCgroupV1 (m .Mount , c )
681
710
default :
682
- if err := os .MkdirAll (dest , 0o755 ); err != nil {
683
- return err
684
- }
685
711
return mountPropagate (m , rootfs , mountLabel )
686
712
}
687
713
}
@@ -899,6 +925,9 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
899
925
if err != nil {
900
926
return err
901
927
}
928
+ if dest == rootfs {
929
+ return fmt .Errorf ("%w: mknod over rootfs" , errRootfsToFile )
930
+ }
902
931
if err := os .MkdirAll (filepath .Dir (dest ), 0o755 ); err != nil {
903
932
return err
904
933
}
@@ -1169,26 +1198,6 @@ func chroot() error {
1169
1198
return nil
1170
1199
}
1171
1200
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
-
1192
1201
// readonlyPath will make a path read only.
1193
1202
func readonlyPath (path string ) error {
1194
1203
if err := mount (path , path , "" , unix .MS_BIND | unix .MS_REC , "" ); err != nil {
0 commit comments