openvswitch: Fix GSO with multiple MPLS label.
[cascardo/linux.git] / kernel / params.c
index 34f5270..0af9b2c 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/slab.h>
@@ -83,6 +84,15 @@ 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)
+{
+       if (kp->flags & KERNEL_PARAM_FL_UNSAFE) {
+               pr_warn("Setting dangerous option %s - tainting kernel\n",
+                       kp->name);
+               add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+       }
+}
+
 static int parse_one(char *param,
                     char *val,
                     const char *doing,
@@ -104,11 +114,12 @@ static int parse_one(char *param,
                                return 0;
                        /* No one handled NULL, so do it here. */
                        if (!val &&
-                           !(params[i].ops->flags & KERNEL_PARAM_FL_NOARG))
+                           !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
                                return -EINVAL;
                        pr_debug("handling %s with %p\n", param,
                                params[i].ops->set);
                        mutex_lock(&param_lock);
+                       param_check_unsafe(&params[i]);
                        err = params[i].ops->set(val, &params[i]);
                        mutex_unlock(&param_lock);
                        return err;
@@ -318,7 +329,7 @@ int param_get_bool(char *buffer, const struct kernel_param *kp)
 EXPORT_SYMBOL(param_get_bool);
 
 struct kernel_param_ops param_ops_bool = {
-       .flags = KERNEL_PARAM_FL_NOARG,
+       .flags = KERNEL_PARAM_OPS_FL_NOARG,
        .set = param_set_bool,
        .get = param_get_bool,
 };
@@ -369,7 +380,7 @@ int param_set_bint(const char *val, const struct kernel_param *kp)
 EXPORT_SYMBOL(param_set_bint);
 
 struct kernel_param_ops param_ops_bint = {
-       .flags = KERNEL_PARAM_FL_NOARG,
+       .flags = KERNEL_PARAM_OPS_FL_NOARG,
        .set = param_set_bint,
        .get = param_get_int,
 };
@@ -503,8 +514,6 @@ EXPORT_SYMBOL(param_ops_string);
 #define to_module_attr(n) container_of(n, struct module_attribute, attr)
 #define to_module_kobject(n) container_of(n, struct module_kobject, kobj)
 
-extern struct kernel_param __start___param[], __stop___param[];
-
 struct param_attribute
 {
        struct module_attribute mattr;
@@ -552,6 +561,7 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
                return -EPERM;
 
        mutex_lock(&param_lock);
+       param_check_unsafe(attribute->param);
        err = attribute->param->ops->set(buf, attribute->param);
        mutex_unlock(&param_lock);
        if (!err)
@@ -593,74 +603,67 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,
                                     const struct kernel_param *kp,
                                     const char *name)
 {
-       struct module_param_attrs *new;
-       struct attribute **attrs;
-       int err, num;
+       struct module_param_attrs *new_mp;
+       struct attribute **new_attrs;
+       unsigned int i;
 
        /* We don't bother calling this with invisible parameters. */
        BUG_ON(!kp->perm);
 
        if (!mk->mp) {
-               num = 0;
-               attrs = NULL;
-       } else {
-               num = mk->mp->num;
-               attrs = mk->mp->grp.attrs;
+               /* First allocation. */
+               mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL);
+               if (!mk->mp)
+                       return -ENOMEM;
+               mk->mp->grp.name = "parameters";
+               /* NULL-terminated attribute array. */
+               mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]),
+                                           GFP_KERNEL);
+               /* Caller will cleanup via free_module_param_attrs */
+               if (!mk->mp->grp.attrs)
+                       return -ENOMEM;
        }
 
-       /* Enlarge. */
-       new = krealloc(mk->mp,
-                      sizeof(*mk->mp) + sizeof(mk->mp->attrs[0]) * (num+1),
-                      GFP_KERNEL);
-       if (!new) {
-               kfree(attrs);
-               err = -ENOMEM;
-               goto fail;
-       }
-       /* Despite looking like the typical realloc() bug, this is safe.
-        * We *want* the old 'attrs' to be freed either way, and we'll store
-        * the new one in the success case. */
-       attrs = krealloc(attrs, sizeof(new->grp.attrs[0])*(num+2), GFP_KERNEL);
-       if (!attrs) {
-               err = -ENOMEM;
-               goto fail_free_new;
-       }
+       /* Enlarge allocations. */
+       new_mp = krealloc(mk->mp,
+                         sizeof(*mk->mp) +
+                         sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1),
+                         GFP_KERNEL);
+       if (!new_mp)
+               return -ENOMEM;
+       mk->mp = new_mp;
 
-       /* Sysfs wants everything zeroed. */
-       memset(new, 0, sizeof(*new));
-       memset(&new->attrs[num], 0, sizeof(new->attrs[num]));
-       memset(&attrs[num], 0, sizeof(attrs[num]));
-       new->grp.name = "parameters";
-       new->grp.attrs = attrs;
+       /* Extra pointer for NULL terminator */
+       new_attrs = krealloc(mk->mp->grp.attrs,
+                            sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2),
+                            GFP_KERNEL);
+       if (!new_attrs)
+               return -ENOMEM;
+       mk->mp->grp.attrs = new_attrs;
 
        /* Tack new one on the end. */
-       sysfs_attr_init(&new->attrs[num].mattr.attr);
-       new->attrs[num].param = kp;
-       new->attrs[num].mattr.show = param_attr_show;
-       new->attrs[num].mattr.store = param_attr_store;
-       new->attrs[num].mattr.attr.name = (char *)name;
-       new->attrs[num].mattr.attr.mode = kp->perm;
-       new->num = num+1;
+       sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr);
+       mk->mp->attrs[mk->mp->num].param = kp;
+       mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show;
+       /* Do not allow runtime DAC changes to make param writable. */
+       if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
+               mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store;
+       mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name;
+       mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm;
+       mk->mp->num++;
 
        /* Fix up all the pointers, since krealloc can move us */
-       for (num = 0; num < new->num; num++)
-               new->grp.attrs[num] = &new->attrs[num].mattr.attr;
-       new->grp.attrs[num] = NULL;
-
-       mk->mp = new;
+       for (i = 0; i < mk->mp->num; i++)
+               mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr;
+       mk->mp->grp.attrs[mk->mp->num] = NULL;
        return 0;
-
-fail_free_new:
-       kfree(new);
-fail:
-       mk->mp = NULL;
-       return err;
 }
 
 #ifdef CONFIG_MODULES
 static void free_module_param_attrs(struct module_kobject *mk)
 {
-       kfree(mk->mp->grp.attrs);
+       if (mk->mp)
+               kfree(mk->mp->grp.attrs);
        kfree(mk->mp);
        mk->mp = NULL;
 }
@@ -685,8 +688,10 @@ int module_param_sysfs_setup(struct module *mod,
                if (kparam[i].perm == 0)
                        continue;
                err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name);
-               if (err)
+               if (err) {
+                       free_module_param_attrs(&mod->mkobj);
                        return err;
+               }
                params = true;
        }
 
@@ -763,7 +768,7 @@ static struct module_kobject * __init locate_module_kobject(const char *name)
 }
 
 static void __init kernel_add_sysfs_param(const char *name,
-                                         struct kernel_param *kparam,
+                                         const struct kernel_param *kparam,
                                          unsigned int name_skip)
 {
        struct module_kobject *mk;
@@ -798,7 +803,7 @@ static void __init kernel_add_sysfs_param(const char *name,
  */
 static void __init param_sysfs_builtin(void)
 {
-       struct kernel_param *kp;
+       const struct kernel_param *kp;
        unsigned int name_len;
        char modname[MODULE_NAME_LEN];