[PATCH RFC 3/7] sysctl: add proc_handler_new to struct ctl_table

From: Thomas Weißschuh
Date: Sat Nov 25 2023 - 07:53:14 EST


The existing handler function take the struct ctl_table as a mutable
parameter. This prevents the table definitions from being put into
.rodata where they would be protected from accidental or malicious
modification.

As many parts of the kernel define proc_handlers provide a gradual
transition mechanism through the introduction of a new field which takes
the table as a read-only parameter.

Signed-off-by: Thomas Weißschuh <linux@xxxxxxxxxxxxxx>
---
fs/proc/proc_sysctl.c | 6 ++++--
include/linux/sysctl.h | 17 +++++++++++++----
2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 1bb0aa2ff501..810ecdd3b84c 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -573,7 +573,7 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,

/* if that can happen at all, it should be -EINVAL, not -EISDIR */
error = -EINVAL;
- if (!table->proc_handler)
+ if (!table->proc_handler && !table->proc_handler_new)
goto out;

/* don't even try if the size is too large */
@@ -655,7 +655,7 @@ static __poll_t proc_sys_poll(struct file *filp, poll_table *wait)
if (IS_ERR(head))
return EPOLLERR | EPOLLHUP;

- if (!table->proc_handler)
+ if (!table->proc_handler && !table->proc_handler_new)
goto out;

if (!table->poll)
@@ -1333,6 +1333,8 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
*
* proc_handler - the text handler routine (described below)
*
+ * proc_handler_new - const variant of the text handler routine (described below)
+ *
* extra1, extra2 - extra pointers usable by the proc handler routines
* XXX: we should eventually modify these to use long min / max [0]
* [0] https://lkml.kernel.org/87zgpte9o4.fsf@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 604aaaa1fce2..de1a5a714070 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -63,6 +63,8 @@ extern const unsigned long sysctl_long_vals[];

typedef int proc_handler(struct ctl_table *ctl, int write, void *buffer,
size_t *lenp, loff_t *ppos);
+typedef int proc_handler_new(const struct ctl_table *ctl, int write,
+ void *buffer, size_t *lenp, loff_t *ppos);

int proc_dostring(struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_dobool(struct ctl_table *table, int write, void *buffer,
@@ -107,10 +109,10 @@ int proc_do_static_key(struct ctl_table *table, int write, void *buffer,
* struct enable minimal validation of the values being written to be
* performed, and the mode field allows minimal authentication.
*
- * There must be a proc_handler routine for any terminal nodes
- * mirrored under /proc/sys (non-terminals are handled by a built-in
- * directory handler). Several default handlers are available to
- * cover common cases.
+ * There must be one proc_handler/proc_handler_new routine for any terminal
+ * nodes mirrored under /proc/sys (non-terminals are handled by a built-in
+ * directory handler).
+ * Several default handlers are available to cover common cases.
*/

/* Support for userspace poll() to watch for changes */
@@ -149,6 +151,7 @@ struct ctl_table {
SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY
} type;
proc_handler *proc_handler; /* Callback for text formatting */
+ proc_handler_new *proc_handler_new; /* Callback for text formatting */
struct ctl_table_poll *poll;
void *extra1;
void *extra2;
@@ -301,6 +304,12 @@ int sysctl_max_threads(struct ctl_table *table, int write, void *buffer,
static inline int sysctl_run_handler(struct ctl_table *ctl, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
+ if (ctl->proc_handler_new && ctl->proc_handler)
+ pr_warn_ratelimited("sysctl table %s has both proc_handler and proc_handler_new, this is a but\n",
+ ctl->procname);
+
+ if (ctl->proc_handler_new)
+ return ctl->proc_handler_new(ctl, write, buffer, lenp, ppos);
return ctl->proc_handler(ctl, write, buffer, lenp, ppos);
}


--
2.43.0