|
@@ -243,8 +243,6 @@ bad:
|
|
|
* them to something that fits in "int" so that others
|
|
|
* 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)
|
|
|
{
|
|
|
struct inode *inode;
|
|
@@ -584,65 +582,71 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
|
|
|
unsigned long nr_segs, unsigned long fast_segs,
|
|
|
struct iovec *fast_pointer,
|
|
|
struct iovec **ret_pointer)
|
|
|
- {
|
|
|
+{
|
|
|
unsigned long seg;
|
|
|
- ssize_t ret;
|
|
|
+ ssize_t ret;
|
|
|
struct iovec *iov = fast_pointer;
|
|
|
|
|
|
- /*
|
|
|
- * SuS says "The readv() function *may* fail if the iovcnt argument
|
|
|
- * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
|
|
|
- * traditionally returned zero for zero segments, so...
|
|
|
- */
|
|
|
+ /*
|
|
|
+ * SuS says "The readv() function *may* fail if the iovcnt argument
|
|
|
+ * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
|
|
|
+ * traditionally returned zero for zero segments, so...
|
|
|
+ */
|
|
|
if (nr_segs == 0) {
|
|
|
ret = 0;
|
|
|
- goto out;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * First get the "struct iovec" from user memory and
|
|
|
- * verify all the pointers
|
|
|
- */
|
|
|
+ /*
|
|
|
+ * First get the "struct iovec" from user memory and
|
|
|
+ * verify all the pointers
|
|
|
+ */
|
|
|
if (nr_segs > UIO_MAXIOV) {
|
|
|
ret = -EINVAL;
|
|
|
- goto out;
|
|
|
+ goto out;
|
|
|
}
|
|
|
if (nr_segs > fast_segs) {
|
|
|
- iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
|
|
|
+ iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
|
|
|
if (iov == NULL) {
|
|
|
ret = -ENOMEM;
|
|
|
- goto out;
|
|
|
+ goto out;
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
|
|
|
ret = -EFAULT;
|
|
|
- goto out;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
+ /*
|
|
|
* According to the Single Unix Specification we should return EINVAL
|
|
|
* 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
|
|
|
* system call.
|
|
|
- */
|
|
|
+ *
|
|
|
+ * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
|
|
|
+ * overflow case.
|
|
|
+ */
|
|
|
ret = 0;
|
|
|
- for (seg = 0; seg < nr_segs; seg++) {
|
|
|
- void __user *buf = iov[seg].iov_base;
|
|
|
- ssize_t len = (ssize_t)iov[seg].iov_len;
|
|
|
+ for (seg = 0; seg < nr_segs; seg++) {
|
|
|
+ void __user *buf = iov[seg].iov_base;
|
|
|
+ ssize_t len = (ssize_t)iov[seg].iov_len;
|
|
|
|
|
|
/* see if we we're about to use an invalid len or if
|
|
|
* it's about to overflow ssize_t */
|
|
|
- if (len < 0 || (ret + len < ret)) {
|
|
|
+ if (len < 0) {
|
|
|
ret = -EINVAL;
|
|
|
- goto out;
|
|
|
+ goto out;
|
|
|
}
|
|
|
if (unlikely(!access_ok(vrfy_dir(type), buf, len))) {
|
|
|
ret = -EFAULT;
|
|
|
- goto out;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (len > MAX_RW_COUNT - ret) {
|
|
|
+ len = MAX_RW_COUNT - ret;
|
|
|
+ iov[seg].iov_len = len;
|
|
|
}
|
|
|
-
|
|
|
ret += len;
|
|
|
- }
|
|
|
+ }
|
|
|
out:
|
|
|
*ret_pointer = iov;
|
|
|
return ret;
|