Commit 4d1b97f9 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: kill the XFS_IOC_{ALLOC,FREE}SP* ioctls



According to the glibc compat header for Irix 4, these ioctls originated
in April 1991 as a (somewhat clunky) way to preallocate space at the end
of a file on an EFS filesystem.  XFS, which was released in Irix 5.3 in
December 1993, picked up these ioctls to maintain compatibility and they
were ported to Linux in the early 2000s.

Recently it was pointed out to me they still lurk in the kernel, even
though the Linux fallocate syscall supplanted the functionality a long
time ago.  fstests doesn't seem to include any real functional or stress
tests for these ioctls, which means that the code quality is ... very
questionable.  Most notably, it was a stale disk block exposure vector
for 21 years and nobody noticed or complained.  As mature programmers
say, "If you're not testing it, it's broken."

Given all that, let's withdraw these ioctls from the XFS userspace API.
Normally we'd set a long deprecation process, but I estimate that there
aren't any real users, so let's trigger a warning in dmesg and return
-ENOTTY.

See: CVE-2021-4155

Augments: 983d8e60 ("xfs: map unwritten blocks in XFS_IOC_{ALLOC,FREE}SP just like fallocate")
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarEric Sandeen <sandeen@redhat.com>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent 9dec0368
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -771,8 +771,7 @@ int
xfs_alloc_file_space(
	struct xfs_inode	*ip,
	xfs_off_t		offset,
	xfs_off_t		len,
	int			alloc_type)
	xfs_off_t		len)
{
	xfs_mount_t		*mp = ip->i_mount;
	xfs_off_t		count;
@@ -865,7 +864,7 @@ xfs_alloc_file_space(
			goto error;

		error = xfs_bmapi_write(tp, ip, startoffset_fsb,
					allocatesize_fsb, alloc_type, 0, imapp,
				allocatesize_fsb, XFS_BMAPI_PREALLOC, 0, imapp,
				&nimaps);
		if (error)
			goto error;
+1 −1
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ int xfs_bmap_last_extent(struct xfs_trans *tp, struct xfs_inode *ip,

/* preallocation and hole punch interface */
int	xfs_alloc_file_space(struct xfs_inode *ip, xfs_off_t offset,
			     xfs_off_t len, int alloc_type);
			     xfs_off_t len);
int	xfs_free_file_space(struct xfs_inode *ip, xfs_off_t offset,
			    xfs_off_t len);
int	xfs_collapse_file_space(struct xfs_inode *, xfs_off_t offset,
+1 −2
Original line number Diff line number Diff line
@@ -1052,8 +1052,7 @@ xfs_file_fallocate(
		}

		if (!xfs_is_always_cow_inode(ip)) {
			error = xfs_alloc_file_space(ip, offset, len,
						     XFS_BMAPI_PREALLOC);
			error = xfs_alloc_file_space(ip, offset, len);
			if (error)
				goto out_unlock;
		}
+5 −87
Original line number Diff line number Diff line
@@ -627,86 +627,6 @@ xfs_attrmulti_by_handle(
	return error;
}

int
xfs_ioc_space(
	struct file		*filp,
	xfs_flock64_t		*bf)
{
	struct inode		*inode = file_inode(filp);
	struct xfs_inode	*ip = XFS_I(inode);
	struct iattr		iattr;
	enum xfs_prealloc_flags	flags = XFS_PREALLOC_CLEAR;
	uint			iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
	int			error;

	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
		return -EPERM;

	if (!(filp->f_mode & FMODE_WRITE))
		return -EBADF;

	if (!S_ISREG(inode->i_mode))
		return -EINVAL;

	if (xfs_is_always_cow_inode(ip))
		return -EOPNOTSUPP;

	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
	if (filp->f_mode & FMODE_NOCMTIME)
		flags |= XFS_PREALLOC_INVISIBLE;

	error = mnt_want_write_file(filp);
	if (error)
		return error;

	xfs_ilock(ip, iolock);
	error = xfs_break_layouts(inode, &iolock, BREAK_UNMAP);
	if (error)
		goto out_unlock;
	inode_dio_wait(inode);

	switch (bf->l_whence) {
	case 0: /*SEEK_SET*/
		break;
	case 1: /*SEEK_CUR*/
		bf->l_start += filp->f_pos;
		break;
	case 2: /*SEEK_END*/
		bf->l_start += XFS_ISIZE(ip);
		break;
	default:
		error = -EINVAL;
		goto out_unlock;
	}

	if (bf->l_start < 0 || bf->l_start > inode->i_sb->s_maxbytes) {
		error = -EINVAL;
		goto out_unlock;
	}

	if (bf->l_start > XFS_ISIZE(ip)) {
		error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
				bf->l_start - XFS_ISIZE(ip), 0);
		if (error)
			goto out_unlock;
	}

	iattr.ia_valid = ATTR_SIZE;
	iattr.ia_size = bf->l_start;
	error = xfs_vn_setattr_size(file_mnt_user_ns(filp), file_dentry(filp),
				    &iattr);
	if (error)
		goto out_unlock;

	error = xfs_update_prealloc_flags(ip, flags);

out_unlock:
	xfs_iunlock(ip, iolock);
	mnt_drop_write_file(filp);
	return error;
}

/* Return 0 on success or positive error */
int
xfs_fsbulkstat_one_fmt(
@@ -1964,13 +1884,11 @@ xfs_file_ioctl(
	case XFS_IOC_ALLOCSP:
	case XFS_IOC_FREESP:
	case XFS_IOC_ALLOCSP64:
	case XFS_IOC_FREESP64: {
		xfs_flock64_t		bf;

		if (copy_from_user(&bf, arg, sizeof(bf)))
			return -EFAULT;
		return xfs_ioc_space(filp, &bf);
	}
	case XFS_IOC_FREESP64:
		xfs_warn_once(mp,
	"%s should use fallocate; XFS_IOC_{ALLOC,FREE}SP ioctl unsupported",
				current->comm);
		return -ENOTTY;
	case XFS_IOC_DIOINFO: {
		struct xfs_buftarg	*target = xfs_inode_buftarg(ip);
		struct dioattr		da;
+0 −6
Original line number Diff line number Diff line
@@ -10,12 +10,6 @@ struct xfs_bstat;
struct xfs_ibulk;
struct xfs_inogrp;


extern int
xfs_ioc_space(
	struct file		*filp,
	xfs_flock64_t		*bf);

int
xfs_ioc_swapext(
	xfs_swapext_t	*sxp);
Loading