@@ -224,36 +224,6 @@ func mountCmd(cmd configs.Command) error {
224
224
return nil
225
225
}
226
226
227
- func prepareBindMount (m * configs.Mount , rootfs string , mountFd * int ) error {
228
- source := m .Source
229
- if mountFd != nil {
230
- source = "/proc/self/fd/" + strconv .Itoa (* mountFd )
231
- }
232
-
233
- stat , err := os .Stat (source )
234
- if err != nil {
235
- // error out if the source of a bind mount does not exist as we will be
236
- // unable to bind anything to it.
237
- return err
238
- }
239
- // ensure that the destination of the bind mount is resolved of symlinks at mount time because
240
- // any previous mounts can invalidate the next mount's destination.
241
- // this can happen when a user specifies mounts within other mounts to cause breakouts or other
242
- // evil stuff to try to escape the container's rootfs.
243
- var dest string
244
- if dest , err = securejoin .SecureJoin (rootfs , m .Destination ); err != nil {
245
- return err
246
- }
247
- if err := checkProcMount (rootfs , dest , m , source ); err != nil {
248
- return err
249
- }
250
- if err := createIfNotExists (dest , stat .IsDir ()); err != nil {
251
- return err
252
- }
253
-
254
- return nil
255
- }
256
-
257
227
func mountCgroupV1 (m * configs.Mount , c * mountConfig ) error {
258
228
binds , err := getCgroupMounts (m )
259
229
if err != nil {
@@ -282,7 +252,8 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
282
252
for _ , b := range binds {
283
253
if c .cgroupns {
284
254
subsystemPath := filepath .Join (c .root , b .Destination )
285
- if err := os .MkdirAll (subsystemPath , 0o755 ); err != nil {
255
+ subsystemName := filepath .Base (b .Destination )
256
+ if err := utils .MkdirAllInRoot (c .root , subsystemPath , 0o755 ); err != nil {
286
257
return err
287
258
}
288
259
if err := utils .WithProcfd (c .root , b .Destination , func (procfd string ) error {
@@ -292,7 +263,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
292
263
}
293
264
var (
294
265
source = "cgroup"
295
- data = filepath . Base ( subsystemPath )
266
+ data = subsystemName
296
267
)
297
268
if data == "systemd" {
298
269
data = cgroups .CgroupNamePrefix + data
@@ -322,14 +293,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
322
293
}
323
294
324
295
func mountCgroupV2 (m * configs.Mount , c * mountConfig ) error {
325
- dest , err := securejoin .SecureJoin (c .root , m .Destination )
326
- if err != nil {
327
- return err
328
- }
329
- if err := os .MkdirAll (dest , 0o755 ); err != nil {
330
- return err
331
- }
332
- err = utils .WithProcfd (c .root , m .Destination , func (procfd string ) error {
296
+ err := utils .WithProcfd (c .root , m .Destination , func (procfd string ) error {
333
297
return mount (m .Source , m .Destination , procfd , "cgroup2" , uintptr (m .Flags ), m .Data )
334
298
})
335
299
if err == nil || ! (errors .Is (err , unix .EPERM ) || errors .Is (err , unix .EBUSY )) {
@@ -411,6 +375,81 @@ func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) {
411
375
})
412
376
}
413
377
378
+ var errRootfsToFile = errors .New ("config tries to change rootfs to file" )
379
+
380
+ func createMountpoint (rootfs string , m * configs.Mount , mountFd * int , source string ) (string , error ) {
381
+ dest , err := securejoin .SecureJoin (rootfs , m .Destination )
382
+ if err != nil {
383
+ return "" , err
384
+ }
385
+ if err := checkProcMount (rootfs , dest , m , source ); err != nil {
386
+ return "" , fmt .Errorf ("check proc-safety of %s mount: %w" , m .Destination , err )
387
+ }
388
+
389
+ switch m .Device {
390
+ case "bind" :
391
+ source := m .Source
392
+ if mountFd != nil {
393
+ source = "/proc/self/fd/" + strconv .Itoa (* mountFd )
394
+ }
395
+
396
+ fi , err := os .Stat (source )
397
+ if err != nil {
398
+ // Error out if the source of a bind mount does not exist as we
399
+ // will be unable to bind anything to it.
400
+ return "" , fmt .Errorf ("bind mount source stat: %w" , err )
401
+ }
402
+ // If the original source is not a directory, make the target a file.
403
+ if ! fi .IsDir () {
404
+ // Make sure we aren't tricked into trying to make the root a file.
405
+ if rootfs == dest {
406
+ return "" , fmt .Errorf ("%w: file bind mount over rootfs" , errRootfsToFile )
407
+ }
408
+ // Make the parent directory.
409
+ destDir , destBase := filepath .Split (dest )
410
+ destDirFd , err := utils .MkdirAllInRootOpen (rootfs , destDir , 0o755 )
411
+ if err != nil {
412
+ return "" , fmt .Errorf ("make parent dir of file bind-mount: %w" , err )
413
+ }
414
+ defer destDirFd .Close ()
415
+ // Make the target file. We want to avoid opening any file that is
416
+ // already there because it could be a "bad" file like an invalid
417
+ // device or hung tty that might cause a DoS, so we use mknodat.
418
+ // destBase does not contain any "/" components, and mknodat does
419
+ // not follow trailing symlinks, so we can safely just call mknodat
420
+ // here.
421
+ if err := unix .Mknodat (int (destDirFd .Fd ()), destBase , unix .S_IFREG | 0o644 , 0 ); err != nil {
422
+ // If we get EEXIST, there was already an inode there and
423
+ // we can consider that a success.
424
+ if ! errors .Is (err , unix .EEXIST ) {
425
+ err = & os.PathError {Op : "mknod regular file" , Path : dest , Err : err }
426
+ return "" , fmt .Errorf ("create target of file bind-mount: %w" , err )
427
+ }
428
+ }
429
+ // Nothing left to do.
430
+ return dest , nil
431
+ }
432
+
433
+ case "tmpfs" :
434
+ // If the original target exists, copy the mode for the tmpfs mount.
435
+ if stat , err := os .Stat (dest ); err == nil {
436
+ dt := fmt .Sprintf ("mode=%04o" , syscallMode (stat .Mode ()))
437
+ if m .Data != "" {
438
+ dt = dt + "," + m .Data
439
+ }
440
+ m .Data = dt
441
+
442
+ // Nothing left to do.
443
+ return dest , nil
444
+ }
445
+ }
446
+
447
+ if err := utils .MkdirAllInRoot (rootfs , dest , 0o755 ); err != nil {
448
+ return "" , err
449
+ }
450
+ return dest , nil
451
+ }
452
+
414
453
func mountToRootfs (m * configs.Mount , c * mountConfig ) error {
415
454
rootfs := c .root
416
455
@@ -435,53 +474,34 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error {
435
474
} else if ! fi .IsDir () {
436
475
return fmt .Errorf ("filesystem %q must be mounted on ordinary directory" , m .Device )
437
476
}
438
- if err := os . MkdirAll ( dest , 0o755 ); err != nil {
477
+ if err := utils . MkdirAllInRoot ( rootfs , dest , 0o755 ); err != nil {
439
478
return err
440
479
}
441
480
// Selinux kernels do not support labeling of /proc or /sys.
442
481
return mountPropagate (m , rootfs , "" , nil )
443
482
}
444
483
445
- mountLabel := c .label
446
484
mountFd := c .fd
447
- dest , err := securejoin . SecureJoin (rootfs , m . Destination )
485
+ dest , err := createMountpoint (rootfs , m , mountFd , m . Source )
448
486
if err != nil {
449
- return err
487
+ return fmt . Errorf ( "create mount destination for %s mount: %w" , m . Destination , err )
450
488
}
489
+ mountLabel := c .label
451
490
452
491
switch m .Device {
453
492
case "mqueue" :
454
- if err := os .MkdirAll (dest , 0o755 ); err != nil {
455
- return err
456
- }
457
493
if err := mountPropagate (m , rootfs , "" , nil ); err != nil {
458
494
return err
459
495
}
460
496
return label .SetFileLabel (dest , mountLabel )
461
497
case "tmpfs" :
462
- if stat , err := os .Stat (dest ); err != nil {
463
- if err := os .MkdirAll (dest , 0o755 ); err != nil {
464
- return err
465
- }
466
- } else {
467
- dt := fmt .Sprintf ("mode=%04o" , syscallMode (stat .Mode ()))
468
- if m .Data != "" {
469
- dt = dt + "," + m .Data
470
- }
471
- m .Data = dt
472
- }
473
-
474
498
if m .Extensions & configs .EXT_COPYUP == configs .EXT_COPYUP {
475
499
err = doTmpfsCopyUp (m , rootfs , mountLabel )
476
500
} else {
477
501
err = mountPropagate (m , rootfs , mountLabel , nil )
478
502
}
479
-
480
503
return err
481
504
case "bind" :
482
- if err := prepareBindMount (m , rootfs , mountFd ); err != nil {
483
- return err
484
- }
485
505
if err := mountPropagate (m , rootfs , mountLabel , mountFd ); err != nil {
486
506
return err
487
507
}
@@ -509,12 +529,6 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error {
509
529
}
510
530
return mountCgroupV1 (m , c )
511
531
default :
512
- if err := checkProcMount (rootfs , dest , m , m .Source ); err != nil {
513
- return err
514
- }
515
- if err := os .MkdirAll (dest , 0o755 ); err != nil {
516
- return err
517
- }
518
532
return mountPropagate (m , rootfs , mountLabel , mountFd )
519
533
}
520
534
if err := setRecAttr (m , rootfs ); err != nil {
@@ -745,7 +759,10 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
745
759
if err != nil {
746
760
return err
747
761
}
748
- if err := os .MkdirAll (filepath .Dir (dest ), 0o755 ); err != nil {
762
+ if dest == rootfs {
763
+ return fmt .Errorf ("%w: mknod over rootfs" , errRootfsToFile )
764
+ }
765
+ if err := utils .MkdirAllInRoot (rootfs , filepath .Dir (dest ), 0o755 ); err != nil {
749
766
return err
750
767
}
751
768
if bind {
@@ -1011,26 +1028,6 @@ func chroot() error {
1011
1028
return nil
1012
1029
}
1013
1030
1014
- // createIfNotExists creates a file or a directory only if it does not already exist.
1015
- func createIfNotExists (path string , isDir bool ) error {
1016
- if _ , err := os .Stat (path ); err != nil {
1017
- if os .IsNotExist (err ) {
1018
- if isDir {
1019
- return os .MkdirAll (path , 0o755 )
1020
- }
1021
- if err := os .MkdirAll (filepath .Dir (path ), 0o755 ); err != nil {
1022
- return err
1023
- }
1024
- f , err := os .OpenFile (path , os .O_CREATE , 0o755 )
1025
- if err != nil {
1026
- return err
1027
- }
1028
- _ = f .Close ()
1029
- }
1030
- }
1031
- return nil
1032
- }
1033
-
1034
1031
// readonlyPath will make a path read only.
1035
1032
func readonlyPath (path string ) error {
1036
1033
if err := mount (path , path , "" , "" , unix .MS_BIND | unix .MS_REC , "" ); err != nil {
0 commit comments