mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 04:33:26 +02:00
Merge branch 'work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull compat iovec cleanups from Al Viro: "Christoph's series around import_iovec() and compat variant thereof" * 'work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: security/keys: remove compat_keyctl_instantiate_key_iov mm: remove compat_process_vm_{readv,writev} fs: remove compat_sys_vmsplice fs: remove the compat readv/writev syscalls fs: remove various compat readv/writev helpers iov_iter: transparently handle compat iovecs in import_iovec iov_iter: refactor rw_copy_check_uvector and import_iovec iov_iter: move rw_copy_check_uvector() into lib/iov_iter.c compat.h: fix a spelling error in <linux/compat.h>
This commit is contained in:
178
lib/iov_iter.c
178
lib/iov_iter.c
@@ -7,6 +7,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/splice.h>
|
||||
#include <linux/compat.h>
|
||||
#include <net/checksum.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/instrumented.h>
|
||||
@@ -1645,16 +1646,145 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
|
||||
}
|
||||
EXPORT_SYMBOL(dup_iter);
|
||||
|
||||
static int copy_compat_iovec_from_user(struct iovec *iov,
|
||||
const struct iovec __user *uvec, unsigned long nr_segs)
|
||||
{
|
||||
const struct compat_iovec __user *uiov =
|
||||
(const struct compat_iovec __user *)uvec;
|
||||
int ret = -EFAULT, i;
|
||||
|
||||
if (!user_access_begin(uvec, nr_segs * sizeof(*uvec)))
|
||||
return -EFAULT;
|
||||
|
||||
for (i = 0; i < nr_segs; i++) {
|
||||
compat_uptr_t buf;
|
||||
compat_ssize_t len;
|
||||
|
||||
unsafe_get_user(len, &uiov[i].iov_len, uaccess_end);
|
||||
unsafe_get_user(buf, &uiov[i].iov_base, uaccess_end);
|
||||
|
||||
/* check for compat_size_t not fitting in compat_ssize_t .. */
|
||||
if (len < 0) {
|
||||
ret = -EINVAL;
|
||||
goto uaccess_end;
|
||||
}
|
||||
iov[i].iov_base = compat_ptr(buf);
|
||||
iov[i].iov_len = len;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
uaccess_end:
|
||||
user_access_end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int copy_iovec_from_user(struct iovec *iov,
|
||||
const struct iovec __user *uvec, unsigned long nr_segs)
|
||||
{
|
||||
unsigned long seg;
|
||||
|
||||
if (copy_from_user(iov, uvec, nr_segs * sizeof(*uvec)))
|
||||
return -EFAULT;
|
||||
for (seg = 0; seg < nr_segs; seg++) {
|
||||
if ((ssize_t)iov[seg].iov_len < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iovec *iovec_from_user(const struct iovec __user *uvec,
|
||||
unsigned long nr_segs, unsigned long fast_segs,
|
||||
struct iovec *fast_iov, bool compat)
|
||||
{
|
||||
struct iovec *iov = fast_iov;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
return iov;
|
||||
if (nr_segs > UIO_MAXIOV)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (nr_segs > fast_segs) {
|
||||
iov = kmalloc_array(nr_segs, sizeof(struct iovec), GFP_KERNEL);
|
||||
if (!iov)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (compat)
|
||||
ret = copy_compat_iovec_from_user(iov, uvec, nr_segs);
|
||||
else
|
||||
ret = copy_iovec_from_user(iov, uvec, nr_segs);
|
||||
if (ret) {
|
||||
if (iov != fast_iov)
|
||||
kfree(iov);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return iov;
|
||||
}
|
||||
|
||||
ssize_t __import_iovec(int type, const struct iovec __user *uvec,
|
||||
unsigned nr_segs, unsigned fast_segs, struct iovec **iovp,
|
||||
struct iov_iter *i, bool compat)
|
||||
{
|
||||
ssize_t total_len = 0;
|
||||
unsigned long seg;
|
||||
struct iovec *iov;
|
||||
|
||||
iov = iovec_from_user(uvec, nr_segs, fast_segs, *iovp, compat);
|
||||
if (IS_ERR(iov)) {
|
||||
*iovp = NULL;
|
||||
return PTR_ERR(iov);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
for (seg = 0; seg < nr_segs; seg++) {
|
||||
ssize_t len = (ssize_t)iov[seg].iov_len;
|
||||
|
||||
if (!access_ok(iov[seg].iov_base, len)) {
|
||||
if (iov != *iovp)
|
||||
kfree(iov);
|
||||
*iovp = NULL;
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (len > MAX_RW_COUNT - total_len) {
|
||||
len = MAX_RW_COUNT - total_len;
|
||||
iov[seg].iov_len = len;
|
||||
}
|
||||
total_len += len;
|
||||
}
|
||||
|
||||
iov_iter_init(i, type, iov, nr_segs, total_len);
|
||||
if (iov == *iovp)
|
||||
*iovp = NULL;
|
||||
else
|
||||
*iovp = iov;
|
||||
return total_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* import_iovec() - Copy an array of &struct iovec from userspace
|
||||
* into the kernel, check that it is valid, and initialize a new
|
||||
* &struct iov_iter iterator to access it.
|
||||
*
|
||||
* @type: One of %READ or %WRITE.
|
||||
* @uvector: Pointer to the userspace array.
|
||||
* @uvec: Pointer to the userspace array.
|
||||
* @nr_segs: Number of elements in userspace array.
|
||||
* @fast_segs: Number of elements in @iov.
|
||||
* @iov: (input and output parameter) Pointer to pointer to (usually small
|
||||
* @iovp: (input and output parameter) Pointer to pointer to (usually small
|
||||
* on-stack) kernel array.
|
||||
* @i: Pointer to iterator that will be initialized on success.
|
||||
*
|
||||
@@ -1667,51 +1797,15 @@ EXPORT_SYMBOL(dup_iter);
|
||||
*
|
||||
* Return: Negative error code on error, bytes imported on success
|
||||
*/
|
||||
ssize_t import_iovec(int type, const struct iovec __user * uvector,
|
||||
ssize_t import_iovec(int type, const struct iovec __user *uvec,
|
||||
unsigned nr_segs, unsigned fast_segs,
|
||||
struct iovec **iov, struct iov_iter *i)
|
||||
struct iovec **iovp, struct iov_iter *i)
|
||||
{
|
||||
ssize_t n;
|
||||
struct iovec *p;
|
||||
n = rw_copy_check_uvector(type, uvector, nr_segs, fast_segs,
|
||||
*iov, &p);
|
||||
if (n < 0) {
|
||||
if (p != *iov)
|
||||
kfree(p);
|
||||
*iov = NULL;
|
||||
return n;
|
||||
}
|
||||
iov_iter_init(i, type, p, nr_segs, n);
|
||||
*iov = p == *iov ? NULL : p;
|
||||
return n;
|
||||
return __import_iovec(type, uvec, nr_segs, fast_segs, iovp, i,
|
||||
in_compat_syscall());
|
||||
}
|
||||
EXPORT_SYMBOL(import_iovec);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#include <linux/compat.h>
|
||||
|
||||
ssize_t compat_import_iovec(int type,
|
||||
const struct compat_iovec __user * uvector,
|
||||
unsigned nr_segs, unsigned fast_segs,
|
||||
struct iovec **iov, struct iov_iter *i)
|
||||
{
|
||||
ssize_t n;
|
||||
struct iovec *p;
|
||||
n = compat_rw_copy_check_uvector(type, uvector, nr_segs, fast_segs,
|
||||
*iov, &p);
|
||||
if (n < 0) {
|
||||
if (p != *iov)
|
||||
kfree(p);
|
||||
*iov = NULL;
|
||||
return n;
|
||||
}
|
||||
iov_iter_init(i, type, p, nr_segs, n);
|
||||
*iov = p == *iov ? NULL : p;
|
||||
return n;
|
||||
}
|
||||
EXPORT_SYMBOL(compat_import_iovec);
|
||||
#endif
|
||||
|
||||
int import_single_range(int rw, void __user *buf, size_t len,
|
||||
struct iovec *iov, struct iov_iter *i)
|
||||
{
|
||||
|
Reference in New Issue
Block a user