Commit d5d24bcc authored by Alison Schofield's avatar Alison Schofield Committed by Jonathan Cameron

iio: trigger: close race condition in acquiring trigger reference

In iio_trigger_write_current() we find the trigger we want while
holding mutex on the list of triggers, but we don't actually do a
get on it while holding mutex.  We wait until further validations
are completed and we're sure it's the one we want.  Race condition
is that it could be freed by the time we do the get.

Solution is to grab the trigger (iio_trigger_get) as soon as we
find it while holding mutex on the list of triggers.  If later
we decide it's not the right one, put it back. (iio_trigger_put).
Signed-off-by: default avatarAlison Schofield <amsfield22@gmail.com>
Suggested-by: default avatarLars-Peter Clausen <lars@metafoo.de>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent dfebd8d8
...@@ -147,8 +147,7 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name) ...@@ -147,8 +147,7 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
return NULL; return NULL;
} }
static struct iio_trigger *iio_trigger_find_by_name(const char *name, static struct iio_trigger *iio_trigger_acquire_by_name(const char *name)
size_t len)
{ {
struct iio_trigger *trig = NULL, *iter; struct iio_trigger *trig = NULL, *iter;
...@@ -156,6 +155,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name, ...@@ -156,6 +155,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,
list_for_each_entry(iter, &iio_trigger_list, list) list_for_each_entry(iter, &iio_trigger_list, list)
if (sysfs_streq(iter->name, name)) { if (sysfs_streq(iter->name, name)) {
trig = iter; trig = iter;
iio_trigger_get(trig);
break; break;
} }
mutex_unlock(&iio_trigger_list_lock); mutex_unlock(&iio_trigger_list_lock);
...@@ -416,20 +416,22 @@ static ssize_t iio_trigger_write_current(struct device *dev, ...@@ -416,20 +416,22 @@ static ssize_t iio_trigger_write_current(struct device *dev,
} }
mutex_unlock(&indio_dev->mlock); mutex_unlock(&indio_dev->mlock);
trig = iio_trigger_find_by_name(buf, len); trig = iio_trigger_acquire_by_name(buf);
if (oldtrig == trig) if (oldtrig == trig) {
return len; ret = len;
goto out_trigger_put;
}
if (trig && indio_dev->info->validate_trigger) { if (trig && indio_dev->info->validate_trigger) {
ret = indio_dev->info->validate_trigger(indio_dev, trig); ret = indio_dev->info->validate_trigger(indio_dev, trig);
if (ret) if (ret)
return ret; goto out_trigger_put;
} }
if (trig && trig->ops->validate_device) { if (trig && trig->ops->validate_device) {
ret = trig->ops->validate_device(trig, indio_dev); ret = trig->ops->validate_device(trig, indio_dev);
if (ret) if (ret)
return ret; goto out_trigger_put;
} }
indio_dev->trig = trig; indio_dev->trig = trig;
...@@ -441,13 +443,16 @@ static ssize_t iio_trigger_write_current(struct device *dev, ...@@ -441,13 +443,16 @@ static ssize_t iio_trigger_write_current(struct device *dev,
iio_trigger_put(oldtrig); iio_trigger_put(oldtrig);
} }
if (indio_dev->trig) { if (indio_dev->trig) {
iio_trigger_get(indio_dev->trig);
if (indio_dev->modes & INDIO_EVENT_TRIGGERED) if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
iio_trigger_attach_poll_func(indio_dev->trig, iio_trigger_attach_poll_func(indio_dev->trig,
indio_dev->pollfunc_event); indio_dev->pollfunc_event);
} }
return len; return len;
out_trigger_put:
iio_trigger_put(trig);
return ret;
} }
static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment