Commit b1b11d00 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'cleanup-kernel_read_write' of git://git.infradead.org/users/hch/misc

Pull in-kernel read and write op cleanups from Christoph Hellwig:
 "Cleanup in-kernel read and write operations

  Reshuffle the (__)kernel_read and (__)kernel_write helpers, and ensure
  all users of in-kernel file I/O use them if they don't use iov_iter
  based methods already.

  The new WARN_ONs in combination with syzcaller already found a missing
  input validation in 9p. The fix should be on your way through the
  maintainer ASAP".

[ This is prep-work for the real changes coming 5.9 ]

* tag 'cleanup-kernel_read_write' of git://git.infradead.org/users/hch/misc:
  fs: remove __vfs_read
  fs: implement kernel_read using __kernel_read
  integrity/ima: switch to using __kernel_read
  fs: add a __kernel_read helper
  fs: remove __vfs_write
  fs: implement kernel_write using __kernel_write
  fs: check FMODE_WRITE in __kernel_write
  fs: unexport __kernel_write
  bpfilter: switch to kernel_write
  autofs: switch to kernel_write
  cachefiles: switch to kernel_write
parents 1bfde037 775802c0
...@@ -53,7 +53,7 @@ static int autofs_write(struct autofs_sb_info *sbi, ...@@ -53,7 +53,7 @@ static int autofs_write(struct autofs_sb_info *sbi,
mutex_lock(&sbi->pipe_mutex); mutex_lock(&sbi->pipe_mutex);
while (bytes) { while (bytes) {
wr = __kernel_write(file, data, bytes, &file->f_pos); wr = kernel_write(file, data, bytes, &file->f_pos);
if (wr <= 0) if (wr <= 0)
break; break;
data += wr; data += wr;
......
...@@ -937,7 +937,7 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page) ...@@ -937,7 +937,7 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page)
} }
data = kmap(page); data = kmap(page);
ret = __kernel_write(file, data, len, &pos); ret = kernel_write(file, data, len, &pos);
kunmap(page); kunmap(page);
fput(file); fput(file);
if (ret != len) if (ret != len)
......
...@@ -419,28 +419,42 @@ static ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, lo ...@@ -419,28 +419,42 @@ static ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, lo
return ret; return ret;
} }
ssize_t __vfs_read(struct file *file, char __user *buf, size_t count, ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
loff_t *pos)
{ {
mm_segment_t old_fs = get_fs();
ssize_t ret;
if (WARN_ON_ONCE(!(file->f_mode & FMODE_READ)))
return -EINVAL;
if (!(file->f_mode & FMODE_CAN_READ))
return -EINVAL;
if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
set_fs(KERNEL_DS);
if (file->f_op->read) if (file->f_op->read)
return file->f_op->read(file, buf, count, pos); ret = file->f_op->read(file, (void __user *)buf, count, pos);
else if (file->f_op->read_iter) else if (file->f_op->read_iter)
return new_sync_read(file, buf, count, pos); ret = new_sync_read(file, (void __user *)buf, count, pos);
else else
return -EINVAL; ret = -EINVAL;
set_fs(old_fs);
if (ret > 0) {
fsnotify_access(file);
add_rchar(current, ret);
}
inc_syscr(current);
return ret;
} }
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos) ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
{ {
mm_segment_t old_fs; ssize_t ret;
ssize_t result;
old_fs = get_fs(); ret = rw_verify_area(READ, file, pos, count);
set_fs(KERNEL_DS); if (ret)
/* The cast to a user pointer is valid due to the set_fs() */ return ret;
result = vfs_read(file, (void __user *)buf, count, pos); return __kernel_read(file, buf, count, pos);
set_fs(old_fs);
return result;
} }
EXPORT_SYMBOL(kernel_read); EXPORT_SYMBOL(kernel_read);
...@@ -456,17 +470,22 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) ...@@ -456,17 +470,22 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
return -EFAULT; return -EFAULT;
ret = rw_verify_area(READ, file, pos, count); ret = rw_verify_area(READ, file, pos, count);
if (!ret) { if (ret)
if (count > MAX_RW_COUNT) return ret;
count = MAX_RW_COUNT; if (count > MAX_RW_COUNT)
ret = __vfs_read(file, buf, count, pos); count = MAX_RW_COUNT;
if (ret > 0) {
fsnotify_access(file);
add_rchar(current, ret);
}
inc_syscr(current);
}
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
else if (file->f_op->read_iter)
ret = new_sync_read(file, buf, count, pos);
else
ret = -EINVAL;
if (ret > 0) {
fsnotify_access(file);
add_rchar(current, ret);
}
inc_syscr(current);
return ret; return ret;
} }
...@@ -488,23 +507,15 @@ static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t ...@@ -488,23 +507,15 @@ static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t
return ret; return ret;
} }
static ssize_t __vfs_write(struct file *file, const char __user *p, /* caller is responsible for file_start_write/file_end_write */
size_t count, loff_t *pos)
{
if (file->f_op->write)
return file->f_op->write(file, p, count, pos);
else if (file->f_op->write_iter)
return new_sync_write(file, p, count, pos);
else
return -EINVAL;
}
ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos) ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos)
{ {
mm_segment_t old_fs; mm_segment_t old_fs;
const char __user *p; const char __user *p;
ssize_t ret; ssize_t ret;
if (WARN_ON_ONCE(!(file->f_mode & FMODE_WRITE)))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_WRITE)) if (!(file->f_mode & FMODE_CAN_WRITE))
return -EINVAL; return -EINVAL;
...@@ -513,7 +524,12 @@ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t ...@@ -513,7 +524,12 @@ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t
p = (__force const char __user *)buf; p = (__force const char __user *)buf;
if (count > MAX_RW_COUNT) if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT; count = MAX_RW_COUNT;
ret = __vfs_write(file, p, count, pos); if (file->f_op->write)
ret = file->f_op->write(file, p, count, pos);
else if (file->f_op->write_iter)
ret = new_sync_write(file, p, count, pos);
else
ret = -EINVAL;
set_fs(old_fs); set_fs(old_fs);
if (ret > 0) { if (ret > 0) {
fsnotify_modify(file); fsnotify_modify(file);
...@@ -522,21 +538,20 @@ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t ...@@ -522,21 +538,20 @@ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t
inc_syscw(current); inc_syscw(current);
return ret; return ret;
} }
EXPORT_SYMBOL(__kernel_write);
ssize_t kernel_write(struct file *file, const void *buf, size_t count, ssize_t kernel_write(struct file *file, const void *buf, size_t count,
loff_t *pos) loff_t *pos)
{ {
mm_segment_t old_fs; ssize_t ret;
ssize_t res;
old_fs = get_fs(); ret = rw_verify_area(WRITE, file, pos, count);
set_fs(KERNEL_DS); if (ret)
/* The cast to a user pointer is valid due to the set_fs() */ return ret;
res = vfs_write(file, (__force const char __user *)buf, count, pos);
set_fs(old_fs);
return res; file_start_write(file);
ret = __kernel_write(file, buf, count, pos);
file_end_write(file);
return ret;
} }
EXPORT_SYMBOL(kernel_write); EXPORT_SYMBOL(kernel_write);
...@@ -552,19 +567,23 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ ...@@ -552,19 +567,23 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
return -EFAULT; return -EFAULT;
ret = rw_verify_area(WRITE, file, pos, count); ret = rw_verify_area(WRITE, file, pos, count);
if (!ret) { if (ret)
if (count > MAX_RW_COUNT) return ret;
count = MAX_RW_COUNT; if (count > MAX_RW_COUNT)
file_start_write(file); count = MAX_RW_COUNT;
ret = __vfs_write(file, buf, count, pos); file_start_write(file);
if (ret > 0) { if (file->f_op->write)
fsnotify_modify(file); ret = file->f_op->write(file, buf, count, pos);
add_wchar(current, ret); else if (file->f_op->write_iter)
} ret = new_sync_write(file, buf, count, pos);
inc_syscw(current); else
file_end_write(file); ret = -EINVAL;
if (ret > 0) {
fsnotify_modify(file);
add_wchar(current, ret);
} }
inc_syscw(current);
file_end_write(file);
return ret; return ret;
} }
......
...@@ -1918,7 +1918,6 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, ...@@ -1918,7 +1918,6 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
struct iovec *fast_pointer, struct iovec *fast_pointer,
struct iovec **ret_pointer); struct iovec **ret_pointer);
extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *);
extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *); 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_write(struct file *, const char __user *, size_t, loff_t *);
extern ssize_t vfs_readv(struct file *, const struct iovec __user *, extern ssize_t vfs_readv(struct file *, const struct iovec __user *,
...@@ -3034,6 +3033,7 @@ extern int kernel_read_file_from_path_initns(const char *, void **, loff_t *, lo ...@@ -3034,6 +3033,7 @@ extern int kernel_read_file_from_path_initns(const char *, void **, loff_t *, lo
extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t, extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t,
enum kernel_read_file_id); enum kernel_read_file_id);
extern ssize_t kernel_read(struct file *, void *, size_t, loff_t *); extern ssize_t kernel_read(struct file *, void *, size_t, loff_t *);
ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);
extern ssize_t kernel_write(struct file *, const void *, size_t, loff_t *); extern ssize_t kernel_write(struct file *, const void *, size_t, loff_t *);
extern ssize_t __kernel_write(struct file *, const void *, size_t, loff_t *); extern ssize_t __kernel_write(struct file *, const void *, size_t, loff_t *);
extern struct file * open_exec(const char *); extern struct file * open_exec(const char *);
......
...@@ -50,7 +50,7 @@ static int __bpfilter_process_sockopt(struct sock *sk, int optname, ...@@ -50,7 +50,7 @@ static int __bpfilter_process_sockopt(struct sock *sk, int optname,
req.len = optlen; req.len = optlen;
if (!bpfilter_ops.info.pid) if (!bpfilter_ops.info.pid)
goto out; goto out;
n = __kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req), n = kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req),
&pos); &pos);
if (n != sizeof(req)) { if (n != sizeof(req)) {
pr_err("write fail %zd\n", n); pr_err("write fail %zd\n", n);
......
...@@ -188,19 +188,7 @@ DEFINE_LSM(integrity) = { ...@@ -188,19 +188,7 @@ DEFINE_LSM(integrity) = {
int integrity_kernel_read(struct file *file, loff_t offset, int integrity_kernel_read(struct file *file, loff_t offset,
void *addr, unsigned long count) void *addr, unsigned long count)
{ {
mm_segment_t old_fs; return __kernel_read(file, addr, count, &offset);
char __user *buf = (char __user *)addr;
ssize_t ret;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = __vfs_read(file, buf, count, &offset);
set_fs(old_fs);
return ret;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment