Linux 6.9-rc4
[sfrench/cifs-2.6.git] / kernel / params.c
index cf448785d0586d72b34366de83362342de4d9838..2e447f8ae183e7ec6d1815c862e0cec6572099d4 100644 (file)
@@ -1,17 +1,20 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
-/* Helpers for initial module or kernel cmdline parsing
-   Copyright (C) 2001 Rusty Russell.
-
-*/
-#include <linux/kernel.h>
-#include <linux/string.h>
+/*
+ * Helpers for initial module or kernel cmdline parsing
+ * Copyright (C) 2001 Rusty Russell.
+ */
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kstrtox.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/err.h>
+#include <linux/overflow.h>
+#include <linux/security.h>
 #include <linux/slab.h>
-#include <linux/ctype.h>
+#include <linux/string.h>
 
 #ifdef CONFIG_SYSFS
 /* Protects all built-in parameters, modules use their own param_lock */
@@ -46,7 +49,7 @@ static void *kmalloc_parameter(unsigned int size)
 {
        struct kmalloced_param *p;
 
-       p = kmalloc(sizeof(*p) + size, GFP_KERNEL);
+       p = kmalloc(size_add(sizeof(*p), size), GFP_KERNEL);
        if (!p)
                return NULL;
 
@@ -96,13 +99,19 @@ bool parameq(const char *a, const char *b)
        return parameqn(a, b, strlen(a)+1);
 }
 
-static void param_check_unsafe(const struct kernel_param *kp)
+static bool param_check_unsafe(const struct kernel_param *kp)
 {
+       if (kp->flags & KERNEL_PARAM_FL_HWPARAM &&
+           security_locked_down(LOCKDOWN_MODULE_PARAMETERS))
+               return false;
+
        if (kp->flags & KERNEL_PARAM_FL_UNSAFE) {
                pr_notice("Setting dangerous option %s - tainting kernel\n",
                          kp->name);
                add_taint(TAINT_USER, LOCKDEP_STILL_OK);
        }
+
+       return true;
 }
 
 static int parse_one(char *param,
@@ -112,9 +121,7 @@ static int parse_one(char *param,
                     unsigned num_params,
                     s16 min_level,
                     s16 max_level,
-                    void *arg,
-                    int (*handle_unknown)(char *param, char *val,
-                                    const char *doing, void *arg))
+                    void *arg, parse_unknown_fn handle_unknown)
 {
        unsigned int i;
        int err;
@@ -132,8 +139,10 @@ static int parse_one(char *param,
                        pr_debug("handling %s with %p\n", param,
                                params[i].ops->set);
                        kernel_param_lock(params[i].mod);
-                       param_check_unsafe(&params[i]);
-                       err = params[i].ops->set(val, &params[i]);
+                       if (param_check_unsafe(&params[i]))
+                               err = params[i].ops->set(val, &params[i]);
+                       else
+                               err = -EPERM;
                        kernel_param_unlock(params[i].mod);
                        return err;
                }
@@ -155,9 +164,7 @@ char *parse_args(const char *doing,
                 unsigned num,
                 s16 min_level,
                 s16 max_level,
-                void *arg,
-                int (*unknown)(char *param, char *val,
-                               const char *doing, void *arg))
+                void *arg, parse_unknown_fn unknown)
 {
        char *param, *val, *err = NULL;
 
@@ -224,28 +231,52 @@ char *parse_args(const char *doing,
        EXPORT_SYMBOL(param_ops_##name)
 
 
-STANDARD_PARAM_DEF(byte,       unsigned char,          "%hhu", kstrtou8);
-STANDARD_PARAM_DEF(short,      short,                  "%hi",  kstrtos16);
-STANDARD_PARAM_DEF(ushort,     unsigned short,         "%hu",  kstrtou16);
-STANDARD_PARAM_DEF(int,                int,                    "%i",   kstrtoint);
-STANDARD_PARAM_DEF(uint,       unsigned int,           "%u",   kstrtouint);
-STANDARD_PARAM_DEF(long,       long,                   "%li",  kstrtol);
-STANDARD_PARAM_DEF(ulong,      unsigned long,          "%lu",  kstrtoul);
-STANDARD_PARAM_DEF(ullong,     unsigned long long,     "%llu", kstrtoull);
+STANDARD_PARAM_DEF(byte,       unsigned char,          "%hhu",         kstrtou8);
+STANDARD_PARAM_DEF(short,      short,                  "%hi",          kstrtos16);
+STANDARD_PARAM_DEF(ushort,     unsigned short,         "%hu",          kstrtou16);
+STANDARD_PARAM_DEF(int,                int,                    "%i",           kstrtoint);
+STANDARD_PARAM_DEF(uint,       unsigned int,           "%u",           kstrtouint);
+STANDARD_PARAM_DEF(long,       long,                   "%li",          kstrtol);
+STANDARD_PARAM_DEF(ulong,      unsigned long,          "%lu",          kstrtoul);
+STANDARD_PARAM_DEF(ullong,     unsigned long long,     "%llu",         kstrtoull);
+STANDARD_PARAM_DEF(hexint,     unsigned int,           "%#08x",        kstrtouint);
+
+int param_set_uint_minmax(const char *val, const struct kernel_param *kp,
+               unsigned int min, unsigned int max)
+{
+       unsigned int num;
+       int ret;
+
+       if (!val)
+               return -EINVAL;
+       ret = kstrtouint(val, 0, &num);
+       if (ret)
+               return ret;
+       if (num < min || num > max)
+               return -EINVAL;
+       *((unsigned int *)kp->arg) = num;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(param_set_uint_minmax);
 
 int param_set_charp(const char *val, const struct kernel_param *kp)
 {
-       if (strlen(val) > 1024) {
+       size_t len, maxlen = 1024;
+
+       len = strnlen(val, maxlen + 1);
+       if (len == maxlen + 1) {
                pr_err("%s: string parameter too long\n", kp->name);
                return -ENOSPC;
        }
 
        maybe_kfree_parameter(*(char **)kp->arg);
 
-       /* This is a hack.  We can't kmalloc in early boot, and we
-        * don't need to; this mangled commandline is preserved. */
+       /*
+        * This is a hack. We can't kmalloc() in early boot, and we
+        * don't need to; this mangled commandline is preserved.
+        */
        if (slab_is_available()) {
-               *(char **)kp->arg = kmalloc_parameter(strlen(val)+1);
+               *(char **)kp->arg = kmalloc_parameter(len + 1);
                if (!*(char **)kp->arg)
                        return -ENOMEM;
                strcpy(*(char **)kp->arg, val);
@@ -282,7 +313,7 @@ int param_set_bool(const char *val, const struct kernel_param *kp)
        if (!val) val = "1";
 
        /* One of =[yYnN01] */
-       return strtobool(val, kp->arg);
+       return kstrtobool(val, kp->arg);
 }
 EXPORT_SYMBOL(param_set_bool);
 
@@ -302,7 +333,7 @@ EXPORT_SYMBOL(param_ops_bool);
 
 int param_set_bool_enable_only(const char *val, const struct kernel_param *kp)
 {
-       int err = 0;
+       int err;
        bool new_value;
        bool orig_value = *(bool *)kp->arg;
        struct kernel_param dummy_kp = *kp;
@@ -483,7 +514,7 @@ int param_set_copystring(const char *val, const struct kernel_param *kp)
 {
        const struct kparam_string *kps = kp->str;
 
-       if (strlen(val)+1 > kps->maxlen) {
+       if (strnlen(val, kps->maxlen) == kps->maxlen) {
                pr_err("%s: string doesn't fit in %u chars.\n",
                       kp->name, kps->maxlen-1);
                return -ENOSPC;
@@ -520,7 +551,7 @@ struct module_param_attrs
 {
        unsigned int num;
        struct attribute_group grp;
-       struct param_attribute attrs[0];
+       struct param_attribute attrs[];
 };
 
 #ifdef CONFIG_SYSFS
@@ -553,8 +584,10 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
                return -EPERM;
 
        kernel_param_lock(mk->mod);
-       param_check_unsafe(attribute->param);
-       err = attribute->param->ops->set(buf, attribute->param);
+       if (param_check_unsafe(attribute->param))
+               err = attribute->param->ops->set(buf, attribute->param);
+       else
+               err = -EPERM;
        kernel_param_unlock(mk->mod);
        if (!err)
                return len;
@@ -712,8 +745,10 @@ void module_param_sysfs_remove(struct module *mod)
 {
        if (mod->mkobj.mp) {
                sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
-               /* We are positive that no one is using any param
-                * attrs at this point.  Deallocate immediately. */
+               /*
+                * We are positive that no one is using any param
+                * attrs at this point. Deallocate immediately.
+                */
                free_module_param_attrs(&mod->mkobj);
        }
 }
@@ -816,7 +851,7 @@ static void __init param_sysfs_builtin(void)
                        name_len = 0;
                } else {
                        name_len = dot - kp->name + 1;
-                       strlcpy(modname, kp->name, name_len);
+                       strscpy(modname, kp->name, name_len);
                }
                kernel_add_sysfs_param(modname, kp, name_len);
        }
@@ -831,18 +866,16 @@ ssize_t __modver_version_show(struct module_attribute *mattr,
        return scnprintf(buf, PAGE_SIZE, "%s\n", vattr->version);
 }
 
-extern const struct module_version_attribute *__start___modver[];
-extern const struct module_version_attribute *__stop___modver[];
+extern const struct module_version_attribute __start___modver[];
+extern const struct module_version_attribute __stop___modver[];
 
 static void __init version_sysfs_builtin(void)
 {
-       const struct module_version_attribute **p;
+       const struct module_version_attribute *vattr;
        struct module_kobject *mk;
        int err;
 
-       for (p = __start___modver; p < __stop___modver; p++) {
-               const struct module_version_attribute *vattr = *p;
-
+       for (vattr = __start___modver; vattr < __stop___modver; vattr++) {
                mk = locate_module_kobject(vattr->module_name);
                if (mk) {
                        err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr);
@@ -898,9 +931,9 @@ static const struct sysfs_ops module_sysfs_ops = {
        .store = module_attr_store,
 };
 
-static int uevent_filter(struct kset *kset, struct kobject *kobj)
+static int uevent_filter(const struct kobject *kobj)
 {
-       struct kobj_type *ktype = get_ktype(kobj);
+       const struct kobj_type *ktype = get_ktype(kobj);
 
        if (ktype == &module_ktype)
                return 1;
@@ -912,7 +945,6 @@ static const struct kset_uevent_ops module_uevent_ops = {
 };
 
 struct kset *module_kset;
-int module_sysfs_initialized;
 
 static void module_kobj_release(struct kobject *kobj)
 {
@@ -920,13 +952,17 @@ static void module_kobj_release(struct kobject *kobj)
        complete(mk->kobj_completion);
 }
 
-struct kobj_type module_ktype = {
+const struct kobj_type module_ktype = {
        .release   =    module_kobj_release,
        .sysfs_ops =    &module_sysfs_ops,
 };
 
 /*
- * param_sysfs_init - wrapper for built-in params support
+ * param_sysfs_init - create "module" kset
+ *
+ * This must be done before the initramfs is unpacked and
+ * request_module() thus becomes possible, because otherwise the
+ * module load would fail in mod_sysfs_init.
  */
 static int __init param_sysfs_init(void)
 {
@@ -936,13 +972,25 @@ static int __init param_sysfs_init(void)
                        __FILE__, __LINE__);
                return -ENOMEM;
        }
-       module_sysfs_initialized = 1;
+
+       return 0;
+}
+subsys_initcall(param_sysfs_init);
+
+/*
+ * param_sysfs_builtin_init - add sysfs version and parameter
+ * attributes for built-in modules
+ */
+static int __init param_sysfs_builtin_init(void)
+{
+       if (!module_kset)
+               return -ENOMEM;
 
        version_sysfs_builtin();
        param_sysfs_builtin();
 
        return 0;
 }
-subsys_initcall(param_sysfs_init);
+late_initcall(param_sysfs_builtin_init);
 
 #endif /* CONFIG_SYSFS */