Skip to content

Commit

Permalink
iov_iter: refactor rw_copy_check_uvector and import_iovec
Browse files Browse the repository at this point in the history
Split rw_copy_check_uvector into two new helpers with more sensible
calling conventions:

 - iovec_from_user copies a iovec from userspace either into the provided
   stack buffer if it fits, or allocates a new buffer for it.  Returns
   the actually used iovec.  It also verifies that iov_len does fit a
   signed type, and handles compat iovecs if the compat flag is set.
 - __import_iovec consolidates the native and compat versions of
   import_iovec. It calls iovec_from_user, then validates each iovec
   actually points to user addresses, and ensures the total length
   doesn't overflow.

This has two major implications:

 - the access_process_vm case loses the total lenght checking, which
   wasn't required anyway, given that each call receives two iovecs
   for the local and remote side of the operation, and it verifies
   the total length on the local side already.
 - instead of a single loop there now are two loops over the iovecs.
   Given that the iovecs are cache hot this doesn't make a major
   difference

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Christoph Hellwig authored and Al Viro committed Oct 3, 2020
1 parent fb041b5 commit bfdc597
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 232 deletions.
16 changes: 5 additions & 11 deletions include/linux/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
#endif /* COMPAT_SYSCALL_DEFINEx */

struct compat_iovec {
compat_uptr_t iov_base;
compat_size_t iov_len;
};

#ifdef CONFIG_COMPAT

#ifndef compat_user_stack_pointer
Expand Down Expand Up @@ -248,11 +253,6 @@ typedef struct compat_siginfo {
} _sifields;
} compat_siginfo_t;

struct compat_iovec {
compat_uptr_t iov_base;
compat_size_t iov_len;
};

struct compat_rlimit {
compat_ulong_t rlim_cur;
compat_ulong_t rlim_max;
Expand Down Expand Up @@ -451,12 +451,6 @@ extern long compat_arch_ptrace(struct task_struct *child, compat_long_t request,

struct epoll_event; /* fortunately, this one is fixed-layout */

extern ssize_t compat_rw_copy_check_uvector(int type,
const struct compat_iovec __user *uvector,
unsigned long nr_segs,
unsigned long fast_segs, struct iovec *fast_pointer,
struct iovec **ret_pointer);

extern void __user *compat_alloc_user_space(unsigned long len);

int compat_restore_altstack(const compat_stack_t __user *uss);
Expand Down
13 changes: 0 additions & 13 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,6 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
/* File supports async buffered reads */
#define FMODE_BUF_RASYNC ((__force fmode_t)0x40000000)

/*
* Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
* that indicates that they should check the contents of the iovec are
* valid, but not check the memory that the iovec elements
* points too.
*/
#define CHECK_IOVEC_ONLY -1

/*
* Attribute flags. These should be or-ed together to figure out what
* has been changed!
Expand Down Expand Up @@ -1887,11 +1879,6 @@ static inline int call_mmap(struct file *file, struct vm_area_struct *vma)
return file->f_op->mmap(file, vma);
}

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);

extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);
extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
extern ssize_t vfs_readv(struct file *, const struct iovec __user *,
Expand Down
12 changes: 9 additions & 3 deletions include/linux/uio.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,15 @@ bool csum_and_copy_from_iter_full(void *addr, size_t bytes, __wsum *csum, struct
size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp,
struct iov_iter *i);

ssize_t import_iovec(int type, const struct iovec __user * uvector,
unsigned nr_segs, unsigned fast_segs,
struct iovec **iov, struct iov_iter *i);
struct iovec *iovec_from_user(const struct iovec __user *uvector,
unsigned long nr_segs, unsigned long fast_segs,
struct iovec *fast_iov, bool compat);
ssize_t import_iovec(int type, const struct iovec __user *uvec,
unsigned nr_segs, unsigned fast_segs, struct iovec **iovp,
struct iov_iter *i);
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);

#ifdef CONFIG_COMPAT
struct compat_iovec;
Expand Down
Loading

0 comments on commit bfdc597

Please sign in to comment.