proc_simple_write() already hands ->write() a kernel copy of what was written but makes that copy with the entry pinned. Let procfs make it before it pins the entry instead. That covers everything created with proc_create_net_data_write() and proc_create_net_single_write(), the afs files and the rxrpc stats.
So a write to a proc_create_net_single() file without ->write() can fail with -EINVAL or -EFAULT now before proc_simple_write() gets to refuse it with -EACCES. Signed-off-by: Christian Brauner (Amutable) <[email protected]> --- fs/proc/generic.c | 15 ++++----------- fs/proc/internal.h | 2 +- fs/proc/proc_net.c | 4 ++-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 7dc7df101286..83d273778a60 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -904,26 +904,19 @@ void proc_remove(struct proc_dir_entry *de) EXPORT_SYMBOL(proc_remove); /* - * Pull a user buffer into memory and pass it to the file's write handler if - * one is supplied. The ->write() method is permitted to modify the - * kernel-side buffer. + * Pass what was written to the file's write handler if one is supplied. The + * ->write() method is permitted to modify the buffer. */ -ssize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size, - loff_t *_pos) +ssize_t proc_simple_write(struct file *f, char *buf, size_t size, loff_t *_pos) { struct proc_dir_entry *pde = PDE(file_inode(f)); - char *buf; int ret; if (!pde->write) return -EACCES; - if (size == 0 || size > PAGE_SIZE - 1) + if (size == 0) return -EINVAL; - buf = memdup_user_nul(ubuf, size); - if (IS_ERR(buf)) - return PTR_ERR(buf); ret = pde->write(f, buf, size); - kfree(buf); return ret == 0 ? size : ret; } diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 80b346a3299d..ed22c6dfb0bf 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -317,7 +317,7 @@ static inline bool is_empty_pde(const struct proc_dir_entry *pde) { return S_ISDIR(pde->mode) && !pde->proc_iops; } -extern ssize_t proc_simple_write(struct file *, const char __user *, size_t, loff_t *); +extern ssize_t proc_simple_write(struct file *, char *, size_t, loff_t *); /* * inode.c diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index fa485581d05e..cdeb2e025d2e 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -77,7 +77,7 @@ static int seq_release_net(struct inode *ino, struct file *f) static const struct proc_ops proc_net_seq_ops = { .proc_open = seq_open_net, - .proc_write = proc_simple_write, + .proc_write_buf = proc_simple_write, .proc_release = seq_release_net, }; @@ -186,7 +186,7 @@ static int single_release_net(struct inode *ino, struct file *f) static const struct proc_ops proc_net_single_ops = { .proc_open = single_open_net, - .proc_write = proc_simple_write, + .proc_write_buf = proc_simple_write, .proc_release = single_release_net, }; -- 2.53.0
