]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
loadpin: Implement custom proc_handler for enforce
authorJoel Granados <joel.granados@kernel.org>
Fri, 5 Dec 2025 08:43:11 +0000 (09:43 +0100)
committerJoel Granados <joel.granados@kernel.org>
Mon, 5 Jan 2026 12:45:19 +0000 (13:45 +0100)
Add a new static variable (loadpin_root_writable) to keep the
write-ability state of enforce. Remove set_sysctl and const qualify
loadpin_sysctl_table (moves into .rodata) as there is no longer need to
change the value of extra1. The new proc_handler_loadpin returns -EINVAL
when loadpin_root_writable is false and the kernel var (enforce) is
being written. The old way of modifying the write-ability of enforce
stays in loadpin_check and is still set by calling sb_is_writable.

Signed-off-by: Joel Granados <joel.granados@kernel.org>
security/loadpin/loadpin.c

index 273ffbd6defe1324d6688dec5f9fe6c9401283ed..8aeec0c4327e69c6c24ae28b791b6ead11c94f23 100644 (file)
@@ -52,32 +52,29 @@ static DEFINE_SPINLOCK(pinned_root_spinlock);
 static bool deny_reading_verity_digests;
 #endif
 
+// initialized to false
+static bool loadpin_root_writable;
 #ifdef CONFIG_SYSCTL
-static struct ctl_table loadpin_sysctl_table[] = {
+
+static int proc_handler_loadpin(const struct ctl_table *table, int dir,
+                               void *buffer, size_t *lenp, loff_t *ppos)
+{
+       if (!loadpin_root_writable && SYSCTL_USER_TO_KERN(dir))
+               return -EINVAL;
+       return proc_dointvec_minmax(table, dir, buffer, lenp, ppos);
+}
+
+static const struct ctl_table loadpin_sysctl_table[] = {
        {
                .procname       = "enforce",
                .data           = &enforce,
                .maxlen         = sizeof(int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ONE,
+               .proc_handler   = proc_handler_loadpin,
+               .extra1         = SYSCTL_ZERO,
                .extra2         = SYSCTL_ONE,
        },
 };
-
-static void set_sysctl(bool is_writable)
-{
-       /*
-        * If load pinning is not enforced via a read-only block
-        * device, allow sysctl to change modes for testing.
-        */
-       if (is_writable)
-               loadpin_sysctl_table[0].extra1 = SYSCTL_ZERO;
-       else
-               loadpin_sysctl_table[0].extra1 = SYSCTL_ONE;
-}
-#else
-static inline void set_sysctl(bool is_writable) { }
 #endif
 
 static void report_writable(struct super_block *mnt_sb, bool writable)
@@ -131,7 +128,6 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
        struct super_block *load_root;
        const char *origin = kernel_read_file_id_str(id);
        bool first_root_pin = false;
-       bool load_root_writable;
 
        /* If the file id is excluded, ignore the pinning. */
        if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) &&
@@ -152,7 +148,6 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
        }
 
        load_root = file->f_path.mnt->mnt_sb;
-       load_root_writable = sb_is_writable(load_root);
 
        /* First loaded module/firmware defines the root for all others. */
        spin_lock(&pinned_root_spinlock);
@@ -168,8 +163,8 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
        spin_unlock(&pinned_root_spinlock);
 
        if (first_root_pin) {
-               report_writable(pinned_root, load_root_writable);
-               set_sysctl(load_root_writable);
+               loadpin_root_writable = sb_is_writable(pinned_root);
+               report_writable(pinned_root, loadpin_root_writable);
                report_load(origin, file, "pinned");
        }