Skip to content

Commit b1b11d0

Browse files
committed
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
2 parents 1bfde03 + 775802c commit b1b11d0

File tree

6 files changed

+80
-73
lines changed

6 files changed

+80
-73
lines changed

fs/autofs/waitq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ static int autofs_write(struct autofs_sb_info *sbi,
5353

5454
mutex_lock(&sbi->pipe_mutex);
5555
while (bytes) {
56-
wr = __kernel_write(file, data, bytes, &file->f_pos);
56+
wr = kernel_write(file, data, bytes, &file->f_pos);
5757
if (wr <= 0)
5858
break;
5959
data += wr;

fs/cachefiles/rdwr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page)
937937
}
938938

939939
data = kmap(page);
940-
ret = __kernel_write(file, data, len, &pos);
940+
ret = kernel_write(file, data, len, &pos);
941941
kunmap(page);
942942
fput(file);
943943
if (ret != len)

fs/read_write.c

Lines changed: 75 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -419,28 +419,42 @@ static ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, lo
419419
return ret;
420420
}
421421

422-
ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,
423-
loff_t *pos)
422+
ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
424423
{
424+
mm_segment_t old_fs = get_fs();
425+
ssize_t ret;
426+
427+
if (WARN_ON_ONCE(!(file->f_mode & FMODE_READ)))
428+
return -EINVAL;
429+
if (!(file->f_mode & FMODE_CAN_READ))
430+
return -EINVAL;
431+
432+
if (count > MAX_RW_COUNT)
433+
count = MAX_RW_COUNT;
434+
set_fs(KERNEL_DS);
425435
if (file->f_op->read)
426-
return file->f_op->read(file, buf, count, pos);
436+
ret = file->f_op->read(file, (void __user *)buf, count, pos);
427437
else if (file->f_op->read_iter)
428-
return new_sync_read(file, buf, count, pos);
438+
ret = new_sync_read(file, (void __user *)buf, count, pos);
429439
else
430-
return -EINVAL;
440+
ret = -EINVAL;
441+
set_fs(old_fs);
442+
if (ret > 0) {
443+
fsnotify_access(file);
444+
add_rchar(current, ret);
445+
}
446+
inc_syscr(current);
447+
return ret;
431448
}
432449

433450
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
434451
{
435-
mm_segment_t old_fs;
436-
ssize_t result;
452+
ssize_t ret;
437453

438-
old_fs = get_fs();
439-
set_fs(KERNEL_DS);
440-
/* The cast to a user pointer is valid due to the set_fs() */
441-
result = vfs_read(file, (void __user *)buf, count, pos);
442-
set_fs(old_fs);
443-
return result;
454+
ret = rw_verify_area(READ, file, pos, count);
455+
if (ret)
456+
return ret;
457+
return __kernel_read(file, buf, count, pos);
444458
}
445459
EXPORT_SYMBOL(kernel_read);
446460

@@ -456,17 +470,22 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
456470
return -EFAULT;
457471

458472
ret = rw_verify_area(READ, file, pos, count);
459-
if (!ret) {
460-
if (count > MAX_RW_COUNT)
461-
count = MAX_RW_COUNT;
462-
ret = __vfs_read(file, buf, count, pos);
463-
if (ret > 0) {
464-
fsnotify_access(file);
465-
add_rchar(current, ret);
466-
}
467-
inc_syscr(current);
468-
}
473+
if (ret)
474+
return ret;
475+
if (count > MAX_RW_COUNT)
476+
count = MAX_RW_COUNT;
469477

478+
if (file->f_op->read)
479+
ret = file->f_op->read(file, buf, count, pos);
480+
else if (file->f_op->read_iter)
481+
ret = new_sync_read(file, buf, count, pos);
482+
else
483+
ret = -EINVAL;
484+
if (ret > 0) {
485+
fsnotify_access(file);
486+
add_rchar(current, ret);
487+
}
488+
inc_syscr(current);
470489
return ret;
471490
}
472491

@@ -488,23 +507,15 @@ static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t
488507
return ret;
489508
}
490509

491-
static ssize_t __vfs_write(struct file *file, const char __user *p,
492-
size_t count, loff_t *pos)
493-
{
494-
if (file->f_op->write)
495-
return file->f_op->write(file, p, count, pos);
496-
else if (file->f_op->write_iter)
497-
return new_sync_write(file, p, count, pos);
498-
else
499-
return -EINVAL;
500-
}
501-
510+
/* caller is responsible for file_start_write/file_end_write */
502511
ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos)
503512
{
504513
mm_segment_t old_fs;
505514
const char __user *p;
506515
ssize_t ret;
507516

517+
if (WARN_ON_ONCE(!(file->f_mode & FMODE_WRITE)))
518+
return -EBADF;
508519
if (!(file->f_mode & FMODE_CAN_WRITE))
509520
return -EINVAL;
510521

@@ -513,7 +524,12 @@ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t
513524
p = (__force const char __user *)buf;
514525
if (count > MAX_RW_COUNT)
515526
count = MAX_RW_COUNT;
516-
ret = __vfs_write(file, p, count, pos);
527+
if (file->f_op->write)
528+
ret = file->f_op->write(file, p, count, pos);
529+
else if (file->f_op->write_iter)
530+
ret = new_sync_write(file, p, count, pos);
531+
else
532+
ret = -EINVAL;
517533
set_fs(old_fs);
518534
if (ret > 0) {
519535
fsnotify_modify(file);
@@ -522,21 +538,20 @@ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t
522538
inc_syscw(current);
523539
return ret;
524540
}
525-
EXPORT_SYMBOL(__kernel_write);
526541

527542
ssize_t kernel_write(struct file *file, const void *buf, size_t count,
528543
loff_t *pos)
529544
{
530-
mm_segment_t old_fs;
531-
ssize_t res;
545+
ssize_t ret;
532546

533-
old_fs = get_fs();
534-
set_fs(KERNEL_DS);
535-
/* The cast to a user pointer is valid due to the set_fs() */
536-
res = vfs_write(file, (__force const char __user *)buf, count, pos);
537-
set_fs(old_fs);
547+
ret = rw_verify_area(WRITE, file, pos, count);
548+
if (ret)
549+
return ret;
538550

539-
return res;
551+
file_start_write(file);
552+
ret = __kernel_write(file, buf, count, pos);
553+
file_end_write(file);
554+
return ret;
540555
}
541556
EXPORT_SYMBOL(kernel_write);
542557

@@ -552,19 +567,23 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
552567
return -EFAULT;
553568

554569
ret = rw_verify_area(WRITE, file, pos, count);
555-
if (!ret) {
556-
if (count > MAX_RW_COUNT)
557-
count = MAX_RW_COUNT;
558-
file_start_write(file);
559-
ret = __vfs_write(file, buf, count, pos);
560-
if (ret > 0) {
561-
fsnotify_modify(file);
562-
add_wchar(current, ret);
563-
}
564-
inc_syscw(current);
565-
file_end_write(file);
570+
if (ret)
571+
return ret;
572+
if (count > MAX_RW_COUNT)
573+
count = MAX_RW_COUNT;
574+
file_start_write(file);
575+
if (file->f_op->write)
576+
ret = file->f_op->write(file, buf, count, pos);
577+
else if (file->f_op->write_iter)
578+
ret = new_sync_write(file, buf, count, pos);
579+
else
580+
ret = -EINVAL;
581+
if (ret > 0) {
582+
fsnotify_modify(file);
583+
add_wchar(current, ret);
566584
}
567-
585+
inc_syscw(current);
586+
file_end_write(file);
568587
return ret;
569588
}
570589

include/linux/fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1918,7 +1918,6 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
19181918
struct iovec *fast_pointer,
19191919
struct iovec **ret_pointer);
19201920

1921-
extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *);
19221921
extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);
19231922
extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
19241923
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
30343033
extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t,
30353034
enum kernel_read_file_id);
30363035
extern ssize_t kernel_read(struct file *, void *, size_t, loff_t *);
3036+
ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);
30373037
extern ssize_t kernel_write(struct file *, const void *, size_t, loff_t *);
30383038
extern ssize_t __kernel_write(struct file *, const void *, size_t, loff_t *);
30393039
extern struct file * open_exec(const char *);

net/bpfilter/bpfilter_kern.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ static int __bpfilter_process_sockopt(struct sock *sk, int optname,
5050
req.len = optlen;
5151
if (!bpfilter_ops.info.pid)
5252
goto out;
53-
n = __kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req),
53+
n = kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req),
5454
&pos);
5555
if (n != sizeof(req)) {
5656
pr_err("write fail %zd\n", n);

security/integrity/iint.c

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -188,19 +188,7 @@ DEFINE_LSM(integrity) = {
188188
int integrity_kernel_read(struct file *file, loff_t offset,
189189
void *addr, unsigned long count)
190190
{
191-
mm_segment_t old_fs;
192-
char __user *buf = (char __user *)addr;
193-
ssize_t ret;
194-
195-
if (!(file->f_mode & FMODE_READ))
196-
return -EBADF;
197-
198-
old_fs = get_fs();
199-
set_fs(KERNEL_DS);
200-
ret = __vfs_read(file, buf, count, &offset);
201-
set_fs(old_fs);
202-
203-
return ret;
191+
return __kernel_read(file, addr, count, &offset);
204192
}
205193

206194
/*

0 commit comments

Comments
 (0)