module: add support for unsafe, tainting parameters
Subsystems (6)
cover module: add support for unsafe, tainting parameters
This is a generic version of Daniel's patch [1] letting us have unsafe module parameters (experimental, debugging, testing, etc.) that taint the kernel when set. Quoting Daniel,
"""
Users just love to set random piles of options since surely enabling all the experimental stuff helps. Later on we get bug reports because it all fell apart.
Even more fun when it's labelled a regression when some change only just made the feature possible (e.g. stolen memory fixes suddenly making fbc possible).
Make it clear that users are playing with fire here. """
Patches 1-3 add the core functionality, patch 4 is our use case as an example.
BR,
Jani.
[1] http://lkml.kernel.org/r/[email protected]
Jani Nikula (4):
module: rename KERNEL_PARAM_FL_NOARG to avoid confusion
module: make it possible to have unsafe, tainting module params
module: add module_param_unsafe and module_param_named_unsafe drm/i915: taint the kernel if unsafe module parameters are set
drivers/gpu/drm/i915/i915_params.c | 8 ++--- drivers/tty/serial/8250/8250_core.c | 2 +- include/linux/moduleparam.h | 64 +++++++++++++++++++++++++++++++------ kernel/module.c | 2 +- kernel/params.c | 17 ++++++++-- security/apparmor/lsm.c | 4 +-- 6 files changed, 76 insertions(+), 21 deletions(-)
Signature · 1 line
1.9.1
PATCH 1/4 module: rename KERNEL_PARAM_FL_NOARG to avoid confusion
Make it clear this is about kernel_param_ops, not kernel_param (which will soon have a flags field of its own). No functional changes.
--- include/linux/moduleparam.h | 2 +- kernel/module.c | 2 +- kernel/params.c | 6 +++--- security/apparmor/lsm.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-)
include/linux/moduleparam.h +1 -1
@@ -42,7 +42,7 @@ struct kernel_param;* NOARG - the parameter allows for no argument (foo instead of foo=1) */ enum { -KERNEL_PARAM_FL_NOARG = (1 <<0 ) +KERNEL_PARAM_OPS_FL_NOARG = (1 <<0 ) };struct kernel_param_ops {
kernel/module.c +1 -1
@@ -135,7 +135,7 @@ static int param_set_bool_enable_only(const char *val, }static const struct kernel_param_ops param_ops_bool_enable_only = { - .flags =KERNEL_PARAM_FL_NOARG , + .flags =KERNEL_PARAM_OPS_FL_NOARG , .set = param_set_bool_enable_only, .get = param_get_bool, };
kernel/params.c +3 -3
@@ -104,7 +104,7 @@ 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);
@@ -318,7 +318,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 +369,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, };
security/apparmor/lsm.c +2 -2
@@ -668,7 +668,7 @@ static int param_set_aabool(const char *val, const struct kernel_param *kp);static int param_get_aabool (char *buffer,const struct kernel_param *kp);#define param_check_aabool param_check_boolstatic struct kernel_param_ops param_ops_aabool = { - .flags =KERNEL_PARAM_FL_NOARG , + .flags =KERNEL_PARAM_OPS_FL_NOARG , .set = param_set_aabool, .get = param_get_aabool };
@@ -685,7 +685,7 @@ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kpstatic int param_get_aalockpolicy (char *buffer,const struct kernel_param *kp);#define param_check_aalockpolicy param_check_boolstatic struct kernel_param_ops param_ops_aalockpolicy = { - .flags =KERNEL_PARAM_FL_NOARG , + .flags =KERNEL_PARAM_OPS_FL_NOARG , .set = param_set_aalockpolicy, .get = param_get_aalockpolicy };
Signature · 1 line
1.9.1
PATCH 2/4 module: make it possible to have unsafe, tainting module params
In reply to Jani NikulaAdd flags field to struct kernel_params, and add the first flag: unsafe parameter. Modifying a kernel parameter with the unsafe flag set, either via the kernel command line or sysfs, will issue a warning and taint the kernel.
--- drivers/tty/serial/8250/8250_core.c | 2 +- include/linux/moduleparam.h | 44 +++++++++++++++++++++++++++++-------- kernel/params.c | 11 ++++++++++ 3 files changed, 47 insertions(+), 10 deletions(-)
drivers/tty/serial/8250/8250_core.c +1 -1
@@ -3587,7 +3587,7 @@ static void __used s8250_options(void)#ifdef CONFIG_SERIAL_8250_RSA __module_param_call (MODULE_PARAM_PREFIX , probe_rsa, ¶m_array_ops, .arr = &__param_arr_probe_rsa, -0444 ,-1 ); +0444 ,-1 ,0 ); #endif }#else
include/linux/moduleparam.h +35 -9
108 lines not on this page · raw message
kernel/params.c +11 -0
@@ -233,6 +233,7 @@ char *parse_args(const char *doing,#define STANDARD_PARAM_DEF (name, type, format, strtolfn) \ int param_set_##name(const char *val, const struct kernel_param *kp) \ { \ + param_check_unsafe(kp); \ return strtolfn(val, 0, (type *)kp->arg); \ } \ int param_get_##name(char *buffer, const struct kernel_param *kp) \
@@ -265,6 +266,8 @@ int param_set_charp(const char *val, const struct kernel_param *kp)return -ENOSPC ; } +param_check_unsafe (kp); +maybe_kfree_parameter (*(char **)kp->arg);/* This is a hack. We can't kmalloc in early boot, and we
@@ -302,6 +305,8 @@ EXPORT_SYMBOL(param_ops_charp);/* Actually could be a bool or an int, for historical reasons. */ int param_set_bool (const char *val,const struct kernel_param *kp) { +param_check_unsafe (kp); +/* No equals means "set"... */ if (!val) val ="1" ;
@@ -331,6 +336,8 @@ int param_set_invbool(const char *val, const struct kernel_param *kp)bool boolval;struct kernel_param dummy; +param_check_unsafe (kp); + dummy.arg = &boolval; ret =param_set_bool (val, &dummy);if (ret ==0 )
@@ -357,6 +364,8 @@ int param_set_bint(const char *val, const struct kernel_param *kp)bool v;int ret; +param_check_unsafe (kp); +/* Match bool exactly, by re-using it. */ boolkp = *kp; boolkp.arg = &v;
@@ -476,6 +485,8 @@ int param_set_copystring(const char *val, const struct kernel_param *kp) {const struct kparam_string *kps = kp->str; +param_check_unsafe (kp); +if (strlen (val)+1 > kps->maxlen) {pr_err ("%s: string doesn't fit in %u chars.\n" , kp->name, kps->maxlen-1 );
Signature · 1 line
1.9.1
PATCH 3/4 module: add module_param_unsafe and module_param_named_unsafe
In reply to Jani NikulaAdd the helpers to be used by modules wishing to expose unsafe debugging or testing module parameters that taint the kernel when set.
--- include/linux/moduleparam.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
include/linux/moduleparam.h +18 -0
@@ -123,6 +123,12 @@ struct kparam_arraymodule_param_named (name, name, type, perm)/** +* module_param_unsafe - same as module_param but taints kernel + */ +#define module_param_unsafe (name, type, perm) \ + module_param_named_unsafe(name, name, type, perm) + +/** * module_param_named - typesafe helper for a renamed module/cmdline parameter * @name: a valid C identifier which is the parameter name. * @value: the actual lvalue to alter.
@@ -139,6 +145,14 @@ struct kparam_array __MODULE_PARM_TYPE(name, #type)/** +* module_param_named_unsafe - same as module_param_named but taints kernel +*/ +#define module_param_named_unsafe (name, value, type, perm) \ + param_check_##type(name, &(value)); \ + module_param_cb_unsafe(name, ¶m_ops_##type, &value, perm); \ + __MODULE_PARM_TYPE(name, #type) + +/** * module_param_cb - general callback for a module/cmdline parameter * @name: a valid C identifier which is the parameter name. * @ops: the set & get operations for this parameter.
@@ -149,6 +163,10 @@ struct kparam_array#define module_param_cb (name, ops, arg, perm) \ __module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0) +#define module_param_cb_unsafe (name, ops, arg, perm) \ + __module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, \ + KERNEL_PARAM_FL_UNSAFE) +/** * <level>_param_cb - general callback for a module/cmdline parameter * to be evaluated before certain initcall level
Signature · 1 line
1.9.1
PATCH 4/4 drm/i915: taint the kernel if unsafe module parameters are set
In reply to Jani NikulaTaint the kernel if the semaphores, enable_rc6, enable_fbc, or ppgtt module parameters are modified. These module parameters are for debugging and testing only, and should never be changed from their platform specific default values by the users. We do not provide support for people enabling all the experimental features. Make this clear by tainting the kernel if the parameters are set.
--- drivers/gpu/drm/i915/i915_params.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpu/drm/i915/i915_params.c +4 -4
@@ -66,12 +66,12 @@ module_param_named(powersave, i915.powersave, int, 0600);MODULE_PARM_DESC (powersave,"Enable powersavings, fbc, downclocking, etc. (default: true)" ); -module_param_named (semaphores, i915.semaphores, int,0400 ); +module_param_named_unsafe (semaphores, i915.semaphores, int,0400 );MODULE_PARM_DESC (semaphores,"Use semaphores for inter-ring sync " "(default: -1 (use per-chip defaults))" ); -module_param_named (enable_rc6, i915.enable_rc6, int,0400 ); +module_param_named_unsafe (enable_rc6, i915.enable_rc6, int,0400 );MODULE_PARM_DESC (enable_rc6,"Enable power-saving render C-state 6. " "Different stages can be selected via bitmask values "
@@ -79,7 +79,7 @@ MODULE_PARM_DESC(enable_rc6,"For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. " "default: -1 (use per-chip default)"); -module_param_named (enable_fbc, i915.enable_fbc, int,0600 ); +module_param_named_unsafe (enable_fbc, i915.enable_fbc, int,0600 );MODULE_PARM_DESC (enable_fbc,"Enable frame buffer compression for power savings " "(default: -1 (use per-chip default))" );
@@ -113,7 +113,7 @@ MODULE_PARM_DESC(enable_hangcheck,"WARNING: Disabling this can cause system wide hangs. " "(default: true)"); -module_param_named (enable_ppgtt, i915.enable_ppgtt, int,0400 ); +module_param_named_unsafe (enable_ppgtt, i915.enable_ppgtt, int,0400 );MODULE_PARM_DESC (enable_ppgtt,"Override PPGTT usage. " "(-1=auto [default], 0=disabled, 1=aliasing, 2=full)" );
Signature · 1 line
1.9.1
Quoted 3 lines · Jani Nikula
This is a generic version of Daniel's patch [1] letting us have unsafe module parameters (experimental, debugging, testing, etc.) that taint the kernel when set. Quoting Daniel,
OK, I think the idea is fine, but we'll probably only want this for a few types (eg. int and bool). So for the moment I prefer a more naive approach.
Does this work for you?
Subject: module: add taint_int type
An int parameter which taints the kernel if set; i915 at least wants this.
include/linux/moduleparam.h +4 -0
@@ -408,6 +408,10 @@ extern int param_set_bint(const char *val, const struct kernel_param *kp);#define param_get_bint param_get_int#define param_check_bint param_check_int +/* An int, which taints the kernel if set. */ +extern struct kernel_param_ops param_ops_taint_int; +#define param_check_taint_int param_check_int +/** * module_param_array - a parameter which is an array of some type * @name: the name of the array variable
kernel/params.c +14 -0
@@ -375,6 +375,20 @@ struct kernel_param_ops param_ops_bint = { };EXPORT_SYMBOL (param_ops_bint); +static int param_set_taint_int (const char *val,const struct kernel_param *kp) +{ +pr_warn ("Setting dangerous option %s - tainting kernel\n" , kp->name); +add_taint (TAINT_USER ,LOCKDEP_STILL_OK ); + +return param_set_int (val, kp); +} + +struct kernel_param_ops param_ops_taint_int = { + .set = param_set_taint_int, + .get = param_get_int, +}; +EXPORT_SYMBOL (param_ops_taint_int); +/* We break the rule and mangle the string. */ static int param_array(const char *name, const char *val,
Quoted 10 lines · Rusty Russell
Quoted 3 lines · Jani Nikula
This is a generic version of Daniel's patch [1] letting us have unsafe module parameters (experimental, debugging, testing, etc.) that taint the kernel when set. Quoting Daniel,
OK, I think the idea is fine, but we'll probably only want this for a few types (eg. int and bool). So for the moment I prefer a more naive approach.
Does this work for you?
Can you please discuss this with yourself from a few months back? We've done the general version since you suggested that just doing it for int is a bit lame ;-) And I actually agreed so asked Jani to look into that.
http://mid.mail-archive.com/[email protected]
"If this is a good idea, you can write a macro module_param_unsafe_named which is a general wrapper."
Cheers, Daniel
Signature · 3 lines
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
Rusty Russell
AppliedQuoted 10 lines · Daniel Vetter
Quoted 9 lines · Rusty Russell
Quoted 3 lines · Jani Nikula
This is a generic version of Daniel's patch [1] letting us have unsafe module parameters (experimental, debugging, testing, etc.) that taint the kernel when set. Quoting Daniel,
OK, I think the idea is fine, but we'll probably only want this for a few types (eg. int and bool). So for the moment I prefer a more naive approach.
Quoted 6 lines
Quoted 1 line
Does this work for you?
Can you please discuss this with yourself from a few months back? We've done the general version since you suggested that just doing it for int is a bit lame ;-) And I actually agreed so asked Jani to look into that.
Don't listen to me, I'm an idiot!
Applied.
I've applied this cleanup on top, however.
Cheers,
Rusty.
Subject: param: check for tainting before calling set op.
This means every set op doesn't need to call it, and it can move into params.c.
include/linux/moduleparam.h +0 -16
@@ -374,22 +374,6 @@ static inline void destroy_params(const struct kernel_param *params,#define __param_check (name, p, type) \ static inline type __always_unused *__check_##name(void) { return(p); } -/** -* param_check_unsafe - Warn and taint the kernel if setting dangerous options. -* -* This gets called from all the standard param setters, but can be used from -* custom setters as well. -*/ -static inline 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 ); - } -} -extern struct kernel_param_ops param_ops_byte;extern int param_set_byte (const char *val,const struct kernel_param *kp);extern int param_get_byte (char *buffer,const struct kernel_param *kp);
kernel/params.c +11 -11
@@ -83,6 +83,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,
@@ -109,6 +119,7 @@ static int parse_one(char *param,pr_debug ("handling %s with %p\n" , param, params[i].ops->set);mutex_lock (¶m_lock); +param_check_unsafe (¶ms[i]); err = params[i].ops->set (val, ¶ms[i]);mutex_unlock (¶m_lock);return err;
@@ -233,7 +244,6 @@ char *parse_args(const char *doing,#define STANDARD_PARAM_DEF (name, type, format, strtolfn) \ int param_set_##name(const char *val, const struct kernel_param *kp) \ { \ - param_check_unsafe(kp); \ return strtolfn(val, 0, (type *)kp->arg); \ } \ int param_get_##name(char *buffer, const struct kernel_param *kp) \
@@ -266,8 +276,6 @@ int param_set_charp(const char *val, const struct kernel_param *kp)return -ENOSPC ; } -param_check_unsafe (kp); -maybe_kfree_parameter (*(char **)kp->arg);/* This is a hack. We can't kmalloc in early boot, and we
@@ -305,8 +313,6 @@ EXPORT_SYMBOL(param_ops_charp);/* Actually could be a bool or an int, for historical reasons. */ int param_set_bool (const char *val,const struct kernel_param *kp) { -param_check_unsafe (kp); -/* No equals means "set"... */ if (!val) val ="1" ;
@@ -336,8 +342,6 @@ int param_set_invbool(const char *val, const struct kernel_param *kp)bool boolval;struct kernel_param dummy; -param_check_unsafe (kp); - dummy.arg = &boolval; ret =param_set_bool (val, &dummy);if (ret ==0 )
@@ -364,8 +368,6 @@ int param_set_bint(const char *val, const struct kernel_param *kp)bool v;int ret; -param_check_unsafe (kp); -/* Match bool exactly, by re-using it. */ boolkp = *kp; boolkp.arg = &v;
@@ -485,8 +487,6 @@ int param_set_copystring(const char *val, const struct kernel_param *kp) {const struct kparam_string *kps = kp->str; -param_check_unsafe (kp); -if (strlen (val)+1 > kps->maxlen) {pr_err ("%s: string doesn't fit in %u chars.\n" , kp->name, kps->maxlen-1 );
@@ -563,6 +563,7 @@ static ssize_t param_attr_store(struct module_attribute *mattr,return -EPERM ;mutex_lock (¶m_lock); +param_check_unsafe (attribute->param); err = attribute->param->ops->set (buf, attribute->param);mutex_unlock (¶m_lock);if (!err)
Quoted 9 lines · Rusty Russell
I've applied this cleanup on top, however.
Cheers,
Rusty.Subject: param: check for tainting before calling set op.
This means every set op doesn't need to call it, and it can move into
params.c.
Much better, thanks. I was looking for a way to do something like this, but obviously didn't look hard enough.
BR,
Jani.
Signature · 1 line
Jani Nikula, Intel Open Source Technology Center