From: Chuck Lever Add Badari's function to check access for all the segments in a passed-in iov. We can use the total byte count later. Signed-off-by: Chuck Lever Cc: Trond Myklebust Signed-off-by: Andrew Morton --- fs/nfs/direct.c | 65 +++++++++++++++++++++++++++++----------------- 1 files changed, 41 insertions(+), 24 deletions(-) diff -puN fs/nfs/direct.c~nfs-check-all-iov-segments-for-correct-memory-access-rights fs/nfs/direct.c --- 25/fs/nfs/direct.c~nfs-check-all-iov-segments-for-correct-memory-access-rights Fri May 19 11:25:07 2006 +++ 25-akpm/fs/nfs/direct.c Fri May 19 11:25:07 2006 @@ -374,7 +374,6 @@ static ssize_t nfs_direct_read(struct ki if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count); rpc_clnt_sigmask(clnt, &oldset); if (nfs_direct_read_schedule(dreq, user_addr, count, pos)) result = nfs_direct_wait(dreq); @@ -709,8 +708,6 @@ static ssize_t nfs_direct_write(struct k if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - nfs_add_stats(inode, NFSIOS_DIRECTWRITTENBYTES, count); - nfs_begin_data_update(inode); rpc_clnt_sigmask(clnt, &oldset); @@ -721,6 +718,36 @@ static ssize_t nfs_direct_write(struct k return result; } +/* + * Check: + * 1. All bytes in the user buffers are properly accessible + * 2. The resulting number of bytes won't overflow ssize_t + */ +static ssize_t check_access_ok(int type, const struct iovec *iov, unsigned long nr_segs) +{ + ssize_t count = 0; + ssize_t retval = -EINVAL; + unsigned long seg; + + for (seg = 0; seg < nr_segs; seg++) { + void __user *buf = iov[seg].iov_base; + ssize_t len = (ssize_t) iov[seg].iov_len; + + if (len < 0) /* size_t not fitting an ssize_t .. */ + goto out; + if (unlikely(!access_ok(type, buf, len))) { + retval = -EFAULT; + goto out; + } + count += len; + if (count < 0) /* math overflow on the ssize_t */ + goto out; + } + retval = count; +out: + return retval; +} + /** * nfs_file_direct_read - file direct read operation for NFS files * @iocb: target I/O control block @@ -745,13 +772,18 @@ static ssize_t nfs_direct_write(struct k ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { - ssize_t retval = -EINVAL; + ssize_t retval; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; /* XXX: temporary */ const char __user *buf = iov[0].iov_base; size_t count = iov[0].iov_len; + retval = check_access_ok(VERIFY_WRITE, iov, nr_segs); + if (retval <= 0) + goto out; + nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, retval); + dprintk("nfs: direct read(%s/%s, %lu@%Ld)\n", file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name, @@ -760,15 +792,6 @@ ssize_t nfs_file_direct_read(struct kioc if (nr_segs != 1) return -EINVAL; - if (count < 0) - goto out; - retval = -EFAULT; - if (!access_ok(VERIFY_WRITE, buf, count)) - goto out; - retval = 0; - if (!count) - goto out; - retval = nfs_sync_mapping(mapping); if (retval) goto out; @@ -816,6 +839,11 @@ ssize_t nfs_file_direct_write(struct kio const char __user *buf = iov[0].iov_base; size_t count = iov[0].iov_len; + retval = check_access_ok(VERIFY_READ, iov, nr_segs); + if (retval <= 0) + goto out; + nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, retval); + dfprintk(VFS, "nfs: direct write(%s/%s, %lu@%Ld)\n", file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name, @@ -828,17 +856,6 @@ ssize_t nfs_file_direct_write(struct kio if (retval) goto out; - retval = -EINVAL; - if ((ssize_t) count < 0) - goto out; - retval = 0; - if (!count) - goto out; - - retval = -EFAULT; - if (!access_ok(VERIFY_READ, buf, count)) - goto out; - retval = nfs_sync_mapping(mapping); if (retval) goto out; _