mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add permission checks to vfs2 truncate.
- Check write permission on truncate(2). Unlike ftruncate(2), truncate(2) fails if the user does not have write permissions on the file. - For gofers under InteropModeShared, check file type before making a truncate request. We should fail early and avoid making an rpc when possible. Furthermore, depending on the remote host's failure may give us unexpected behavior--if the host converts the truncate request to an ftruncate syscall on an open fd, we will get EINVAL instead of EISDIR. Updates #2923. PiperOrigin-RevId: 322913569
This commit is contained in:
@@ -1334,7 +1334,7 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
|
||||
fs.renameMuRUnlockAndCheckCaching(&ds)
|
||||
return err
|
||||
}
|
||||
if err := d.setStat(ctx, rp.Credentials(), &opts.Stat, rp.Mount()); err != nil {
|
||||
if err := d.setStat(ctx, rp.Credentials(), &opts, rp.Mount()); err != nil {
|
||||
fs.renameMuRUnlockAndCheckCaching(&ds)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -888,7 +888,8 @@ func (d *dentry) statTo(stat *linux.Statx) {
|
||||
stat.DevMinor = d.fs.devMinor
|
||||
}
|
||||
|
||||
func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, stat *linux.Statx, mnt *vfs.Mount) error {
|
||||
func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs.SetStatOptions, mnt *vfs.Mount) error {
|
||||
stat := &opts.Stat
|
||||
if stat.Mask == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -896,7 +897,7 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, stat *lin
|
||||
return syserror.EPERM
|
||||
}
|
||||
mode := linux.FileMode(atomic.LoadUint32(&d.mode))
|
||||
if err := vfs.CheckSetStat(ctx, creds, stat, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
|
||||
if err := vfs.CheckSetStat(ctx, creds, opts, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mnt.CheckBeginWrite(); err != nil {
|
||||
@@ -937,6 +938,17 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, stat *lin
|
||||
}
|
||||
if !d.isSynthetic() {
|
||||
if stat.Mask != 0 {
|
||||
if stat.Mask&linux.STATX_SIZE != 0 {
|
||||
// Check whether to allow a truncate request to be made.
|
||||
switch d.mode & linux.S_IFMT {
|
||||
case linux.S_IFREG:
|
||||
// Allow.
|
||||
case linux.S_IFDIR:
|
||||
return syserror.EISDIR
|
||||
default:
|
||||
return syserror.EINVAL
|
||||
}
|
||||
}
|
||||
if err := d.file.setAttr(ctx, p9.SetAttrMask{
|
||||
Permissions: stat.Mask&linux.STATX_MODE != 0,
|
||||
UID: stat.Mask&linux.STATX_UID != 0,
|
||||
@@ -1498,7 +1510,7 @@ func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linu
|
||||
|
||||
// SetStat implements vfs.FileDescriptionImpl.SetStat.
|
||||
func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
if err := fd.dentry().setStat(ctx, auth.CredentialsFromContext(ctx), &opts.Stat, fd.vfsfd.Mount()); err != nil {
|
||||
if err := fd.dentry().setStat(ctx, auth.CredentialsFromContext(ctx), &opts, fd.vfsfd.Mount()); err != nil {
|
||||
return err
|
||||
}
|
||||
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
|
||||
|
||||
@@ -373,7 +373,7 @@ func (i *inode) fstat(fs *filesystem) (linux.Statx, error) {
|
||||
|
||||
// SetStat implements kernfs.Inode.
|
||||
func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
|
||||
s := opts.Stat
|
||||
s := &opts.Stat
|
||||
|
||||
m := s.Mask
|
||||
if m == 0 {
|
||||
@@ -386,7 +386,7 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
|
||||
if err := syscall.Fstat(i.hostFD, &hostStat); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := vfs.CheckSetStat(ctx, creds, &s, linux.FileMode(hostStat.Mode&linux.PermissionsMask), auth.KUID(hostStat.Uid), auth.KGID(hostStat.Gid)); err != nil {
|
||||
if err := vfs.CheckSetStat(ctx, creds, &opts, linux.FileMode(hostStat.Mode), auth.KUID(hostStat.Uid), auth.KGID(hostStat.Gid)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -396,6 +396,9 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
|
||||
}
|
||||
}
|
||||
if m&linux.STATX_SIZE != 0 {
|
||||
if hostStat.Mode&linux.S_IFMT != linux.S_IFREG {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
if err := syscall.Ftruncate(i.hostFD, int64(s.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ func (a *InodeAttrs) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *aut
|
||||
if opts.Stat.Mask&^(linux.STATX_MODE|linux.STATX_UID|linux.STATX_GID) != 0 {
|
||||
return syserror.EPERM
|
||||
}
|
||||
if err := vfs.CheckSetStat(ctx, creds, &opts.Stat, a.Mode(), auth.KUID(atomic.LoadUint32(&a.uid)), auth.KGID(atomic.LoadUint32(&a.gid))); err != nil {
|
||||
if err := vfs.CheckSetStat(ctx, creds, &opts, a.Mode(), auth.KUID(atomic.LoadUint32(&a.uid)), auth.KGID(atomic.LoadUint32(&a.gid))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1104,7 +1104,7 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
|
||||
}
|
||||
|
||||
mode := linux.FileMode(atomic.LoadUint32(&d.mode))
|
||||
if err := vfs.CheckSetStat(ctx, rp.Credentials(), &opts.Stat, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
|
||||
if err := vfs.CheckSetStat(ctx, rp.Credentials(), &opts, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
|
||||
return err
|
||||
}
|
||||
mnt := rp.Mount()
|
||||
|
||||
@@ -151,7 +151,7 @@ func (fd *nonDirectoryFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux
|
||||
func (fd *nonDirectoryFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
d := fd.dentry()
|
||||
mode := linux.FileMode(atomic.LoadUint32(&d.mode))
|
||||
if err := vfs.CheckSetStat(ctx, auth.CredentialsFromContext(ctx), &opts.Stat, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
|
||||
if err := vfs.CheckSetStat(ctx, auth.CredentialsFromContext(ctx), &opts, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
|
||||
return err
|
||||
}
|
||||
mnt := fd.vfsfd.Mount()
|
||||
|
||||
@@ -649,7 +649,7 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
|
||||
fs.mu.RUnlock()
|
||||
return err
|
||||
}
|
||||
if err := d.inode.setStat(ctx, rp.Credentials(), &opts.Stat); err != nil {
|
||||
if err := d.inode.setStat(ctx, rp.Credentials(), &opts); err != nil {
|
||||
fs.mu.RUnlock()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -452,7 +452,8 @@ func (i *inode) statTo(stat *linux.Statx) {
|
||||
}
|
||||
}
|
||||
|
||||
func (i *inode) setStat(ctx context.Context, creds *auth.Credentials, stat *linux.Statx) error {
|
||||
func (i *inode) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs.SetStatOptions) error {
|
||||
stat := &opts.Stat
|
||||
if stat.Mask == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -460,7 +461,7 @@ func (i *inode) setStat(ctx context.Context, creds *auth.Credentials, stat *linu
|
||||
return syserror.EPERM
|
||||
}
|
||||
mode := linux.FileMode(atomic.LoadUint32(&i.mode))
|
||||
if err := vfs.CheckSetStat(ctx, creds, stat, mode, auth.KUID(atomic.LoadUint32(&i.uid)), auth.KGID(atomic.LoadUint32(&i.gid))); err != nil {
|
||||
if err := vfs.CheckSetStat(ctx, creds, opts, mode, auth.KUID(atomic.LoadUint32(&i.uid)), auth.KGID(atomic.LoadUint32(&i.gid))); err != nil {
|
||||
return err
|
||||
}
|
||||
i.mu.Lock()
|
||||
@@ -695,7 +696,7 @@ func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linu
|
||||
func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
d := fd.dentry()
|
||||
if err := d.inode.setStat(ctx, creds, &opts.Stat); err != nil {
|
||||
if err := d.inode.setStat(ctx, creds, &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
Mask: linux.STATX_SIZE,
|
||||
Size: uint64(length),
|
||||
},
|
||||
NeedWritePerm: true,
|
||||
})
|
||||
return 0, nil, handleSetSizeError(t, err)
|
||||
}
|
||||
@@ -197,6 +198,10 @@ func Ftruncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
|
||||
}
|
||||
defer file.DecRef()
|
||||
|
||||
if !file.IsWritable() {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
err := file.SetStat(t, vfs.SetStatOptions{
|
||||
Stat: linux.Statx{
|
||||
Mask: linux.STATX_SIZE,
|
||||
|
||||
@@ -164,6 +164,12 @@ type SetStatOptions struct {
|
||||
// == UTIME_OMIT (VFS users must unset the corresponding bit in Stat.Mask
|
||||
// instead).
|
||||
Stat linux.Statx
|
||||
|
||||
// NeedWritePerm indicates that write permission on the file is needed for
|
||||
// this operation. This is needed for truncate(2) (note that ftruncate(2)
|
||||
// does not require the same check--instead, it checks that the fd is
|
||||
// writable).
|
||||
NeedWritePerm bool
|
||||
}
|
||||
|
||||
// BoundEndpointOptions contains options to VirtualFilesystem.BoundEndpointAt()
|
||||
|
||||
@@ -183,7 +183,8 @@ func MayWriteFileWithOpenFlags(flags uint32) bool {
|
||||
// CheckSetStat checks that creds has permission to change the metadata of a
|
||||
// file with the given permissions, UID, and GID as specified by stat, subject
|
||||
// to the rules of Linux's fs/attr.c:setattr_prepare().
|
||||
func CheckSetStat(ctx context.Context, creds *auth.Credentials, stat *linux.Statx, mode linux.FileMode, kuid auth.KUID, kgid auth.KGID) error {
|
||||
func CheckSetStat(ctx context.Context, creds *auth.Credentials, opts *SetStatOptions, mode linux.FileMode, kuid auth.KUID, kgid auth.KGID) error {
|
||||
stat := &opts.Stat
|
||||
if stat.Mask&linux.STATX_SIZE != 0 {
|
||||
limit, err := CheckLimit(ctx, 0, int64(stat.Size))
|
||||
if err != nil {
|
||||
@@ -215,6 +216,11 @@ func CheckSetStat(ctx context.Context, creds *auth.Credentials, stat *linux.Stat
|
||||
return syserror.EPERM
|
||||
}
|
||||
}
|
||||
if opts.NeedWritePerm && !creds.HasCapability(linux.CAP_DAC_OVERRIDE) {
|
||||
if err := GenericCheckPermissions(creds, MayWrite, mode, kuid, kgid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if stat.Mask&(linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_CTIME) != 0 {
|
||||
if !CanActAsOwner(creds, kuid) {
|
||||
if (stat.Mask&linux.STATX_ATIME != 0 && stat.Atime.Nsec != linux.UTIME_NOW) ||
|
||||
|
||||
Reference in New Issue
Block a user