Hi Christian,
On Wed, 4 Nov 2026 at 20:32, Christian Brauner <[email protected]> wrote:
> proc_reg_write() pins the entry and calls ->proc_write() which copies
> from userspace itself. A page fault in that copy can take as long as
> the writer wants, just like the copy to userspace of a read, and
> remove_proc_entry() waits for it.
>
> So do what kernfs does for writes as well. An entry that implements
> ->proc_write_buf() gets a NUL-terminated kernel copy of what was
> written. procfs makes that copy before it pins the entry. The copy is
> capped at ->proc_write_size bytes, or at PAGE_SIZE - 1 like
> proc_simple_write() if the entry doesn't care. Larger writes fail with
> -EINVAL. The proc_ops go away with the entry, so procfs remembers the
> limit in the entry when it is registered, like the size of the seq_file
> state, instead of pinning the entry just to look up a number.
>
> Entries that take binary data in pieces, like the PCI config space,
> ALSA's data entries or a firmware image, can ask for
> PROC_ENTRY_SHORT_WRITES instead. procfs then copies in the first
> ->proc_write_size bytes and the write comes up short.
>
> ->proc_write_buf() gets the position like ->proc_write() so entries
> that write at an offset keep working.
>
> Note that procfs copies in the whole write before the entry looks at any
> of it. So a write that is too large or whose buffer can't be read fails
> before the entry's own checks run, permission checks included, even
> where the entry only ever looked at the first few bytes.
>
> An entry that implements ->proc_write_buf() and still has a
> ->proc_write() fails to register.
>
> No users yet.
>
> Signed-off-by: Christian Brauner (Amutable) <[email protected]>
Thanks for your patch!
> --- a/include/linux/proc_fs.h
> +++ b/include/linux/proc_fs.h
> @@ -67,6 +74,16 @@ struct proc_ops {
> const struct seq_operations *proc_seq_ops;
> int (*proc_show)(struct seq_file *, void *);
> unsigned int proc_state_size;
> + /*
> + * procfs copies what is written into a kernel buffer and calls
> + * ->proc_write_buf() with it. The buffer is NUL-terminated and holds
> + * at most ->proc_write_size bytes, PAGE_SIZE - 1 if that's 0. Larger
> + * writes fail with -EINVAL unless the entry asks for
> + * PROC_ENTRY_SHORT_WRITES. The position is NULL for a stream, like
> + * for ->write().
> + */
> + ssize_t (*proc_write_buf)(struct file *, char *, size_t, loff_t *);
"const char *"?
Seems like you have to drop several const attributes during the
conversion from "const char __user *buf" to "char *buf".
While at it, I think it would be good to use this conversion as an
opportunity to replace "char *" by "void *", which will probably let
you drop several casts from users.
> + unsigned int proc_write_size;
> } __randomize_layout;
>
> /* definitions for hide_pid field */
>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds