[PATCH RFC 2/4] sysctl: Apply sysctl context when flag is active

From: Joel Granados

Date: Thu Sep 24 2026 - 10:35:28 EST


Whenever CTL_TABLE_F_CTX_DATA is set on the entry, overlay the ->data
entry contained in inst on the entry to be passed to proc_handlers. No
table sets the flags yet, so there is no functional change.

Signed-off-by: Joel Granados <joel.granados@xxxxxxxxxx>
---
fs/proc/proc_sysctl.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index fe32337892f9badc897a29e9176e66790003fb05..1ed9f4e1a17e28c5db1d07356390cbee968cb91f 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -550,12 +550,31 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
return err;
}

+/* Returns @entry itself when it is not flagged, else a resolved copy in @buf. */
+static const struct ctl_table *sysctl_apply_ctx(struct ctl_table_header *head,
+ const struct ctl_table *entry,
+ struct ctl_table *buf)
+{
+ ptrdiff_t off;
+
+ if (!(entry->flags & CTL_TABLE_F_CTX_DATA))
+ return entry;
+
+ off = (const char *)entry->data - (const char *)head->ctx.tmpl;
+
+ *buf = *entry;
+ buf->data = (char *)head->ctx.inst + off;
+
+ return buf;
+}
+
static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
int write)
{
struct inode *inode = file_inode(iocb->ki_filp);
struct ctl_table_header *head = grab_header(inode);
const struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+ struct ctl_table ctx_entry;
size_t count = iov_iter_count(iter);
char *kbuf;
ssize_t error;
@@ -567,6 +586,8 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
* At this point we know that the sysctl was not unregistered
* and won't be until we finish.
*/
+ table = sysctl_apply_ctx(head, table, &ctx_entry);
+
error = -EPERM;
if (sysctl_perm(head, table, write ? MAY_WRITE : MAY_READ))
goto out;
@@ -797,6 +818,7 @@ static int proc_sys_permission(struct mnt_idmap *idmap,
*/
struct ctl_table_header *head;
const struct ctl_table *table;
+ struct ctl_table ctx_entry;
int error;

/* Executable files are not allowed under /proc/sys/ */
@@ -810,8 +832,11 @@ static int proc_sys_permission(struct mnt_idmap *idmap,
table = PROC_I(inode)->sysctl_entry;
if (!table) /* global root - r-xr-xr-x */
error = mask & MAY_WRITE ? -EACCES : 0;
- else /* Use the permissions on the sysctl table entry */
+ else {
+ /* ->permissions may inspect the entry, so resolve it first. */
+ table = sysctl_apply_ctx(head, table, &ctx_entry);
error = sysctl_perm(head, table, mask & ~MAY_NOT_BLOCK);
+ }

sysctl_head_finish(head);
return error;

--
2.50.1