Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 907b29eb authored by Rusty Russell's avatar Rusty Russell
Browse files

param: locking for kernel parameters



There may be cases (most obviously, sysfs-writable charp parameters) where
a module needs to prevent sysfs access to parameters.

Rather than express this in terms of a big lock, the functions are
expressed in terms of what they protect against.  This is clearer, esp.
if the implementation changes to a module-level or even param-level lock.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Reviewed-by: default avatarTakashi Iwai <tiwai@suse.de>
Tested-by: default avatarPhil Carmody <ext-phil.2.carmody@nokia.com>
parent 914dcaa8
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -130,6 +130,62 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
#define module_param(name, type, perm)				\
	module_param_named(name, name, type, perm)

/**
 * kparam_block_sysfs_write - make sure a parameter isn't written via sysfs.
 * @name: the name of the parameter
 *
 * There's no point blocking write on a paramter that isn't writable via sysfs!
 */
#define kparam_block_sysfs_write(name)			\
	do {						\
		BUG_ON(!(__param_##name.perm & 0222));	\
		__kernel_param_lock();			\
	} while (0)

/**
 * kparam_unblock_sysfs_write - allows sysfs to write to a parameter again.
 * @name: the name of the parameter
 */
#define kparam_unblock_sysfs_write(name)		\
	do {						\
		BUG_ON(!(__param_##name.perm & 0222));	\
		__kernel_param_unlock();		\
	} while (0)

/**
 * kparam_block_sysfs_read - make sure a parameter isn't read via sysfs.
 * @name: the name of the parameter
 *
 * This also blocks sysfs writes.
 */
#define kparam_block_sysfs_read(name)			\
	do {						\
		BUG_ON(!(__param_##name.perm & 0444));	\
		__kernel_param_lock();			\
	} while (0)

/**
 * kparam_unblock_sysfs_read - allows sysfs to read a parameter again.
 * @name: the name of the parameter
 */
#define kparam_unblock_sysfs_read(name)			\
	do {						\
		BUG_ON(!(__param_##name.perm & 0444));	\
		__kernel_param_unlock();		\
	} while (0)

#ifdef CONFIG_SYSFS
extern void __kernel_param_lock(void);
extern void __kernel_param_unlock(void);
#else
static inline void __kernel_param_lock(void)
{
}
static inline void __kernel_param_unlock(void)
{
}
#endif

#ifndef MODULE
/**
 * core_param - define a historical core kernel parameter.
+26 −7
Original line number Diff line number Diff line
@@ -31,12 +31,14 @@
#define DEBUGP(fmt, a...)
#endif

/* Protects all parameters, and incidentally kmalloced_param list. */
static DEFINE_MUTEX(param_lock);

/* This just allows us to keep track of which parameters are kmalloced. */
struct kmalloced_param {
	struct list_head list;
	char val[];
};
static DEFINE_MUTEX(param_lock);
static LIST_HEAD(kmalloced_params);

static void *kmalloc_parameter(unsigned int size)
@@ -47,10 +49,7 @@ static void *kmalloc_parameter(unsigned int size)
	if (!p)
		return NULL;

	mutex_lock(&param_lock);
	list_add(&p->list, &kmalloced_params);
	mutex_unlock(&param_lock);

	return p->val;
}

@@ -59,7 +58,6 @@ static void maybe_kfree_parameter(void *param)
{
	struct kmalloced_param *p;

	mutex_lock(&param_lock);
	list_for_each_entry(p, &kmalloced_params, list) {
		if (p->val == param) {
			list_del(&p->list);
@@ -67,7 +65,6 @@ static void maybe_kfree_parameter(void *param)
			break;
		}
	}
	mutex_unlock(&param_lock);
}

static inline char dash2underscore(char c)
@@ -93,6 +90,7 @@ static int parse_one(char *param,
		     int (*handle_unknown)(char *param, char *val))
{
	unsigned int i;
	int err;

	/* Find parameter */
	for (i = 0; i < num_params; i++) {
@@ -102,7 +100,10 @@ static int parse_one(char *param,
				return -EINVAL;
			DEBUGP("They are equal!  Calling %p\n",
			       params[i].ops->set);
			return params[i].ops->set(val, &params[i]);
			mutex_lock(&param_lock);
			err = params[i].ops->set(val, &params[i]);
			mutex_unlock(&param_lock);
			return err;
		}
	}

@@ -400,6 +401,7 @@ static int param_array(const char *name,
		/* nul-terminate and parse */
		save = val[len];
		((char *)val)[len] = '\0';
		BUG_ON(!mutex_is_locked(&param_lock));
		ret = set(val, &kp);

		if (ret != 0)
@@ -438,6 +440,7 @@ static int param_array_get(char *buffer, const struct kernel_param *kp)
		if (i)
			buffer[off++] = ',';
		p.arg = arr->elem + arr->elemsize * i;
		BUG_ON(!mutex_is_locked(&param_lock));
		ret = arr->ops->get(buffer + off, &p);
		if (ret < 0)
			return ret;
@@ -522,7 +525,9 @@ static ssize_t param_attr_show(struct module_attribute *mattr,
	if (!attribute->param->ops->get)
		return -EPERM;

	mutex_lock(&param_lock);
	count = attribute->param->ops->get(buf, attribute->param);
	mutex_unlock(&param_lock);
	if (count > 0) {
		strcat(buf, "\n");
		++count;
@@ -541,7 +546,9 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
	if (!attribute->param->ops->set)
		return -EPERM;

	mutex_lock(&param_lock);
	err = attribute->param->ops->set(buf, attribute->param);
	mutex_unlock(&param_lock);
	if (!err)
		return len;
	return err;
@@ -555,6 +562,18 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
#endif

#ifdef CONFIG_SYSFS
void __kernel_param_lock(void)
{
	mutex_lock(&param_lock);
}
EXPORT_SYMBOL(__kernel_param_lock);

void __kernel_param_unlock(void)
{
	mutex_unlock(&param_lock);
}
EXPORT_SYMBOL(__kernel_param_unlock);

/*
 * add_sysfs_param - add a parameter to sysfs
 * @mk: struct module_kobject