Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4fbfe5a

Browse files
committed
Only allow proc mount if it is procfs
Fixes #2128 This allows proc to be bind mounted for host and rootless namespace usecases but it removes the ability to mount over the top of proc with a directory. ```bash > sudo docker run --rm apparmor docker: Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"/var/lib/docker/volumes/aae28ea068c33d60e64d1a75916cf3ec2dc3634f97571854c9ed30c8401460c1/_data\\\" to rootfs \\\"/var/lib/docker/overlay2/a6be5ae911bf19f8eecb23a295dec85be9a8ee8da66e9fb55b47c841d1e381b7/merged\\\" at \\\"/proc\\\" caused \\\"\\\\\\\"/var/lib/docker/overlay2/a6be5ae911bf19f8eecb23a295dec85be9a8ee8da66e9fb55b47c841d1e381b7/merged/proc\\\\\\\" cannot be mounted because it is not of type proc\\\"\"": unknown. > sudo docker run --rm -v /proc:/proc apparmor docker-default (enforce) root 18989 0.9 0.0 1288 4 ? Ss 16:47 0:00 sleep 20 ``` Signed-off-by: Michael Crosby <[email protected]>
1 parent 7507c64 commit 4fbfe5a

File tree

3 files changed

+42
-21
lines changed

3 files changed

+42
-21
lines changed

libcontainer/container_linux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
"syscall" // only for SysProcAttr and Signal
2020
"time"
2121

22-
"github.com/cyphar/filepath-securejoin"
22+
securejoin "github.com/cyphar/filepath-securejoin"
2323
"github.com/opencontainers/runc/libcontainer/cgroups"
2424
"github.com/opencontainers/runc/libcontainer/configs"
2525
"github.com/opencontainers/runc/libcontainer/intelrdt"
@@ -1176,7 +1176,7 @@ func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error {
11761176
if err != nil {
11771177
return err
11781178
}
1179-
if err := checkMountDestination(c.config.Rootfs, dest); err != nil {
1179+
if err := checkProcMount(c.config.Rootfs, dest, ""); err != nil {
11801180
return err
11811181
}
11821182
m.Destination = dest

libcontainer/rootfs_linux.go

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"strings"
1414
"time"
1515

16-
"github.com/cyphar/filepath-securejoin"
16+
securejoin "github.com/cyphar/filepath-securejoin"
1717
"github.com/mrunalp/fileutils"
1818
"github.com/opencontainers/runc/libcontainer/cgroups"
1919
"github.com/opencontainers/runc/libcontainer/configs"
@@ -197,7 +197,7 @@ func prepareBindMount(m *configs.Mount, rootfs string) error {
197197
if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil {
198198
return err
199199
}
200-
if err := checkMountDestination(rootfs, dest); err != nil {
200+
if err := checkProcMount(rootfs, dest, m.Source); err != nil {
201201
return err
202202
}
203203
// update the mount with the correct dest after symlinks are resolved.
@@ -414,7 +414,7 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
414414
if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil {
415415
return err
416416
}
417-
if err := checkMountDestination(rootfs, dest); err != nil {
417+
if err := checkProcMount(rootfs, dest, m.Source); err != nil {
418418
return err
419419
}
420420
// update the mount with the correct dest after symlinks are resolved.
@@ -461,12 +461,11 @@ func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) {
461461
return binds, nil
462462
}
463463

464-
// checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
464+
// checkProcMount checks to ensure that the mount destination is not over the top of /proc.
465465
// dest is required to be an abs path and have any symlinks resolved before calling this function.
466-
func checkMountDestination(rootfs, dest string) error {
467-
invalidDestinations := []string{
468-
"/proc",
469-
}
466+
//
467+
// if source is nil, don't stat the filesystem. This is used for restore of a checkpoint.
468+
func checkProcMount(rootfs, dest, source string) error {
470469
// White list, it should be sub directories of invalid destinations
471470
validDestinations := []string{
472471
// These entries can be bind mounted by files emulated by fuse,
@@ -489,18 +488,40 @@ func checkMountDestination(rootfs, dest string) error {
489488
return nil
490489
}
491490
}
492-
for _, invalid := range invalidDestinations {
493-
path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest)
494-
if err != nil {
495-
return err
496-
}
497-
if path != "." && !strings.HasPrefix(path, "..") {
498-
return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid)
491+
const procPath = "/proc"
492+
path, err := filepath.Rel(filepath.Join(rootfs, procPath), dest)
493+
if err != nil {
494+
return err
495+
}
496+
if !strings.HasPrefix(path, "..") {
497+
if path == "." {
498+
// an empty source is pasted on restore
499+
if source == "" {
500+
return nil
501+
}
502+
// only allow a mount on-top of proc if it's source is "proc"
503+
isproc, err := isProc(source)
504+
if err != nil {
505+
return err
506+
}
507+
if !isproc {
508+
return fmt.Errorf("%q cannot be mounted because it is not of type proc", dest)
509+
}
510+
return nil
499511
}
512+
return fmt.Errorf("%q cannot be mounted because it is inside /proc", dest)
500513
}
501514
return nil
502515
}
503516

517+
func isProc(path string) (bool, error) {
518+
var s unix.Statfs_t
519+
if err := unix.Statfs(path, &s); err != nil {
520+
return false, err
521+
}
522+
return s.Type == unix.PROC_SUPER_MAGIC, nil
523+
}
524+
504525
func setupDevSymlinks(rootfs string) error {
505526
var links = [][2]string{
506527
{"/proc/self/fd", "/dev/fd"},

libcontainer/rootfs_linux_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,31 @@ import (
1010

1111
func TestCheckMountDestOnProc(t *testing.T) {
1212
dest := "/rootfs/proc/sys"
13-
err := checkMountDestination("/rootfs", dest)
13+
err := checkProcMount("/rootfs", dest, "")
1414
if err == nil {
1515
t.Fatal("destination inside proc should return an error")
1616
}
1717
}
1818

1919
func TestCheckMountDestOnProcChroot(t *testing.T) {
2020
dest := "/rootfs/proc/"
21-
err := checkMountDestination("/rootfs", dest)
21+
err := checkProcMount("/rootfs", dest, "/proc")
2222
if err != nil {
2323
t.Fatal("destination inside proc when using chroot should not return an error")
2424
}
2525
}
2626

2727
func TestCheckMountDestInSys(t *testing.T) {
2828
dest := "/rootfs//sys/fs/cgroup"
29-
err := checkMountDestination("/rootfs", dest)
29+
err := checkProcMount("/rootfs", dest, "")
3030
if err != nil {
3131
t.Fatal("destination inside /sys should not return an error")
3232
}
3333
}
3434

3535
func TestCheckMountDestFalsePositive(t *testing.T) {
3636
dest := "/rootfs/sysfiles/fs/cgroup"
37-
err := checkMountDestination("/rootfs", dest)
37+
err := checkProcMount("/rootfs", dest, "")
3838
if err != nil {
3939
t.Fatal(err)
4040
}

0 commit comments

Comments
 (0)