gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/syscalls/linux/sys_mount.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package linux
    16  
    17  import (
    18  	"gvisor.dev/gvisor/pkg/abi/linux"
    19  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    20  	"gvisor.dev/gvisor/pkg/hostarch"
    21  	"gvisor.dev/gvisor/pkg/sentry/arch"
    22  	"gvisor.dev/gvisor/pkg/sentry/kernel"
    23  	"gvisor.dev/gvisor/pkg/sentry/vfs"
    24  )
    25  
    26  // Mount implements Linux syscall mount(2).
    27  func Mount(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    28  	sourceAddr := args[0].Pointer()
    29  	targetAddr := args[1].Pointer()
    30  	typeAddr := args[2].Pointer()
    31  	flags := args[3].Uint64()
    32  	dataAddr := args[4].Pointer()
    33  
    34  	// Must have CAP_SYS_ADMIN in the current mount namespace's associated user
    35  	// namespace.
    36  	creds := t.Credentials()
    37  	if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespace().Owner) {
    38  		return 0, nil, linuxerr.EPERM
    39  	}
    40  
    41  	// Ignore magic value that was required before Linux 2.4.
    42  	if flags&linux.MS_MGC_MSK == linux.MS_MGC_VAL {
    43  		flags = flags &^ linux.MS_MGC_MSK
    44  	}
    45  
    46  	// Silently allow MS_NOSUID, since we don't implement set-id bits anyway.
    47  	const unsupported = linux.MS_UNBINDABLE | linux.MS_MOVE | linux.MS_NODIRATIME
    48  
    49  	// Linux just allows passing any flags to mount(2) - it won't fail when
    50  	// unknown or unsupported flags are passed. Since we don't implement
    51  	// everything, we fail explicitly on flags that are unimplemented.
    52  	if flags&(unsupported) != 0 {
    53  		return 0, nil, linuxerr.EINVAL
    54  	}
    55  
    56  	// For null-terminated strings related to mount(2), Linux copies in at most
    57  	// a page worth of data. See fs/namespace.c:copy_mount_string().
    58  	targetPath, err := copyInPath(t, targetAddr)
    59  	if err != nil {
    60  		return 0, nil, err
    61  	}
    62  	target, err := getTaskPathOperation(t, linux.AT_FDCWD, targetPath, disallowEmptyPath, followFinalSymlink)
    63  	if err != nil {
    64  		return 0, nil, err
    65  	}
    66  	defer target.Release(t)
    67  	var opts vfs.MountOptions
    68  	if flags&(linux.MS_NOATIME|linux.MS_STRICTATIME) == linux.MS_NOATIME {
    69  		opts.Flags.NoATime = true
    70  	}
    71  	if flags&linux.MS_NOEXEC == linux.MS_NOEXEC {
    72  		opts.Flags.NoExec = true
    73  	}
    74  	if flags&linux.MS_NODEV == linux.MS_NODEV {
    75  		opts.Flags.NoDev = true
    76  	}
    77  	if flags&linux.MS_NOSUID == linux.MS_NOSUID {
    78  		opts.Flags.NoSUID = true
    79  	}
    80  	if flags&linux.MS_RDONLY == linux.MS_RDONLY {
    81  		opts.ReadOnly = true
    82  	}
    83  	data := ""
    84  	if dataAddr != 0 {
    85  		// In Linux, a full page is always copied in regardless of null
    86  		// character placement, and the address is passed to each file system.
    87  		// Most file systems always treat this data as a string, though, and so
    88  		// do all of the ones we implement.
    89  		data, err = t.CopyInString(dataAddr, hostarch.PageSize)
    90  		if err != nil {
    91  			return 0, nil, err
    92  		}
    93  	}
    94  	opts.GetFilesystemOptions.Data = data
    95  	switch {
    96  	case flags&linux.MS_REMOUNT != 0:
    97  		// When MS_REMOUNT is specified, the flags and data should match the values used in the original mount() call,
    98  		// except for those parameters that are being changed.
    99  		//
   100  		// The src and filesystem type are ignored for MS_REMOUNT.
   101  		return 0, nil, t.Kernel().VFS().RemountAt(t, creds, &target.pop, &opts)
   102  	case flags&linux.MS_BIND != 0:
   103  		sourcePath, err := copyInPath(t, sourceAddr)
   104  		if err != nil {
   105  			return 0, nil, err
   106  		}
   107  		var sourceTpop taskPathOperation
   108  		sourceTpop, err = getTaskPathOperation(t, linux.AT_FDCWD, sourcePath, disallowEmptyPath, followFinalSymlink)
   109  		if err != nil {
   110  			return 0, nil, err
   111  		}
   112  		defer sourceTpop.Release(t)
   113  		return 0, nil, t.Kernel().VFS().BindAt(t, creds, &sourceTpop.pop, &target.pop, flags&linux.MS_REC != 0)
   114  	case flags&(linux.MS_SHARED|linux.MS_PRIVATE|linux.MS_SLAVE|linux.MS_UNBINDABLE) != 0:
   115  		return 0, nil, t.Kernel().VFS().SetMountPropagationAt(t, creds, &target.pop, uint32(flags))
   116  	}
   117  
   118  	// Only copy in source, fstype, and data if we are doing a normal mount.
   119  	source, err := t.CopyInString(sourceAddr, hostarch.PageSize)
   120  	if err != nil {
   121  		return 0, nil, err
   122  	}
   123  	fsType, err := t.CopyInString(typeAddr, hostarch.PageSize)
   124  	if err != nil {
   125  		return 0, nil, err
   126  	}
   127  	_, err = t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts)
   128  	return 0, nil, err
   129  }
   130  
   131  // Umount2 implements Linux syscall umount2(2).
   132  func Umount2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   133  	addr := args[0].Pointer()
   134  	flags := args[1].Int()
   135  
   136  	// Must have CAP_SYS_ADMIN in the mount namespace's associated user
   137  	// namespace.
   138  	//
   139  	// Currently, this is always the init task's user namespace.
   140  	creds := t.Credentials()
   141  	if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespace().Owner) {
   142  		return 0, nil, linuxerr.EPERM
   143  	}
   144  
   145  	const unsupported = linux.MNT_FORCE | linux.MNT_EXPIRE
   146  	if flags&unsupported != 0 {
   147  		return 0, nil, linuxerr.EINVAL
   148  	}
   149  
   150  	path, err := copyInPath(t, addr)
   151  	if err != nil {
   152  		return 0, nil, err
   153  	}
   154  	tpop, err := getTaskPathOperation(t, linux.AT_FDCWD, path, disallowEmptyPath, shouldFollowFinalSymlink(flags&linux.UMOUNT_NOFOLLOW == 0))
   155  	if err != nil {
   156  		return 0, nil, err
   157  	}
   158  	defer tpop.Release(t)
   159  
   160  	opts := vfs.UmountOptions{
   161  		Flags: uint32(flags &^ linux.UMOUNT_NOFOLLOW),
   162  	}
   163  
   164  	return 0, nil, t.Kernel().VFS().UmountAt(t, creds, &tpop.pop, &opts)
   165  }