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) <brauner@kernel.org>
---
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.cdiff --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