Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 435f49a5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

readv/writev: do the same MAX_RW_COUNT truncation that read/write does



We used to protect against overflow, but rather than return an error, do
what read/write does, namely to limit the total size to MAX_RW_COUNT.
This is not only more consistent, but it also means that any broken
low-level read/write routine that still keeps counts in 'int' can't
break.

Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f56f4400
Loading
Loading
Loading
Loading
+6 −6
Original line number Original line Diff line number Diff line
@@ -606,14 +606,14 @@ ssize_t compat_rw_copy_check_uvector(int type,
	/*
	/*
	 * Single unix specification:
	 * Single unix specification:
	 * We should -EINVAL if an element length is not >= 0 and fitting an
	 * We should -EINVAL if an element length is not >= 0 and fitting an
	 * ssize_t.  The total length is fitting an ssize_t
	 * ssize_t.
	 *
	 *
	 * Be careful here because iov_len is a size_t not an ssize_t
	 * In Linux, the total length is limited to MAX_RW_COUNT, there is
	 * no overflow possibility.
	 */
	 */
	tot_len = 0;
	tot_len = 0;
	ret = -EINVAL;
	ret = -EINVAL;
	for (seg = 0; seg < nr_segs; seg++) {
	for (seg = 0; seg < nr_segs; seg++) {
		compat_ssize_t tmp = tot_len;
		compat_uptr_t buf;
		compat_uptr_t buf;
		compat_ssize_t len;
		compat_ssize_t len;


@@ -624,13 +624,13 @@ ssize_t compat_rw_copy_check_uvector(int type,
		}
		}
		if (len < 0)	/* size_t not fitting in compat_ssize_t .. */
		if (len < 0)	/* size_t not fitting in compat_ssize_t .. */
			goto out;
			goto out;
		tot_len += len;
		if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
			goto out;
		if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
		if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
			ret = -EFAULT;
			ret = -EFAULT;
			goto out;
			goto out;
		}
		}
		if (len > MAX_RW_COUNT - tot_len)
			len = MAX_RW_COUNT - tot_len;
		tot_len += len;
		iov->iov_base = compat_ptr(buf);
		iov->iov_base = compat_ptr(buf);
		iov->iov_len = (compat_size_t) len;
		iov->iov_len = (compat_size_t) len;
		uvector++;
		uvector++;
+33 −29
Original line number Original line Diff line number Diff line
@@ -243,8 +243,6 @@ SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
 * them to something that fits in "int" so that others
 * them to something that fits in "int" so that others
 * won't have to do range checks all the time.
 * won't have to do range checks all the time.
 */
 */
#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)

int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
{
{
	struct inode *inode;
	struct inode *inode;
@@ -624,6 +622,9 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
	 * if an element length is < 0 when cast to ssize_t or if the
	 * if an element length is < 0 when cast to ssize_t or if the
	 * total length would overflow the ssize_t return value of the
	 * total length would overflow the ssize_t return value of the
	 * system call.
	 * system call.
	 *
	 * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
	 * overflow case.
	 */
	 */
	ret = 0;
	ret = 0;
	for (seg = 0; seg < nr_segs; seg++) {
	for (seg = 0; seg < nr_segs; seg++) {
@@ -632,7 +633,7 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,


		/* see if we we're about to use an invalid len or if
		/* see if we we're about to use an invalid len or if
		 * it's about to overflow ssize_t */
		 * it's about to overflow ssize_t */
		if (len < 0 || (ret + len < ret)) {
		if (len < 0) {
			ret = -EINVAL;
			ret = -EINVAL;
			goto out;
			goto out;
		}
		}
@@ -640,7 +641,10 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
			ret = -EFAULT;
			ret = -EFAULT;
			goto out;
			goto out;
		}
		}

		if (len > MAX_RW_COUNT - ret) {
			len = MAX_RW_COUNT - ret;
			iov[seg].iov_len = len;
		}
		ret += len;
		ret += len;
	}
	}
out:
out:
+1 −0
Original line number Original line Diff line number Diff line
@@ -1867,6 +1867,7 @@ extern int current_umask(void);
/* /sys/fs */
/* /sys/fs */
extern struct kobject *fs_kobj;
extern struct kobject *fs_kobj;


#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
extern int rw_verify_area(int, struct file *, loff_t *, size_t);
extern int rw_verify_area(int, struct file *, loff_t *, size_t);


#define FLOCK_VERIFY_READ  1
#define FLOCK_VERIFY_READ  1