diff options
author | Tuukka Toivonen <tuukka.toivonen@intel.com> | 2017-01-27 05:32:56 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2017-02-03 11:51:56 -0500 |
commit | 47b037a0512d9f8675ec2693bed46c8ea6a884ab (patch) | |
tree | 2cc74ab5cd545ba1fb239031d935659994ddf51e | |
parent | 79a2eda80c6dab79790c308d9f50ecd2e5021ba3 (diff) |
[media] v4l2-async: failing functions shouldn't have side effects
v4l2-async had several functions doing some operations and then
not undoing the operations in a failure situation. For example,
v4l2_async_test_notify() moved a subdev into notifier's done list
even if registering the subdev (v4l2_device_register_subdev) failed.
If the subdev was allocated and v4l2_async_register_subdev() called
from the driver's probe() function, as usually, the probe()
function freed the allocated subdev and returned a failure.
Nevertheless, the subdev was still left into the notifier's done
list, causing an access to already freed memory when the notifier
was later unregistered.
A hand-edited call trace leaving freed subdevs into the notifier:
v4l2_async_register_notifier(notifier, asd)
cameradrv_probe
sd = devm_kzalloc()
v4l2_async_register_subdev(sd)
v4l2_async_test_notify(notifier, sd, asd)
list_move(sd, ¬ifier->done)
v4l2_device_register_subdev(notifier->v4l2_dev, sd)
cameradrv_registered(sd) -> fails
->v4l2_async_register_subdev returns failure
->cameradrv_probe returns failure
->devres frees the allocated sd
->sd was freed but it still remains in the notifier's list.
This patch fixes this and several other cases where a failing
function could leave nodes into a linked list while the caller
might free the node due to a failure.
Signed-off-by: Tuukka Toivonen <tuukka.toivonen@intel.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
-rw-r--r-- | drivers/media/v4l2-core/v4l2-async.c | 21 |
1 files changed, 11 insertions, 10 deletions
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 618135089548..96cc733f35ef 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c | |||
@@ -100,18 +100,11 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, | |||
100 | { | 100 | { |
101 | int ret; | 101 | int ret; |
102 | 102 | ||
103 | /* Remove from the waiting list */ | ||
104 | list_del(&asd->list); | ||
105 | sd->asd = asd; | ||
106 | sd->notifier = notifier; | ||
107 | |||
108 | if (notifier->bound) { | 103 | if (notifier->bound) { |
109 | ret = notifier->bound(notifier, sd, asd); | 104 | ret = notifier->bound(notifier, sd, asd); |
110 | if (ret < 0) | 105 | if (ret < 0) |
111 | return ret; | 106 | return ret; |
112 | } | 107 | } |
113 | /* Move from the global subdevice list to notifier's done */ | ||
114 | list_move(&sd->async_list, ¬ifier->done); | ||
115 | 108 | ||
116 | ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); | 109 | ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); |
117 | if (ret < 0) { | 110 | if (ret < 0) { |
@@ -120,6 +113,14 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, | |||
120 | return ret; | 113 | return ret; |
121 | } | 114 | } |
122 | 115 | ||
116 | /* Remove from the waiting list */ | ||
117 | list_del(&asd->list); | ||
118 | sd->asd = asd; | ||
119 | sd->notifier = notifier; | ||
120 | |||
121 | /* Move from the global subdevice list to notifier's done */ | ||
122 | list_move(&sd->async_list, ¬ifier->done); | ||
123 | |||
123 | if (list_empty(¬ifier->waiting) && notifier->complete) | 124 | if (list_empty(¬ifier->waiting) && notifier->complete) |
124 | return notifier->complete(notifier); | 125 | return notifier->complete(notifier); |
125 | 126 | ||
@@ -169,9 +170,6 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, | |||
169 | 170 | ||
170 | mutex_lock(&list_lock); | 171 | mutex_lock(&list_lock); |
171 | 172 | ||
172 | /* Keep also completed notifiers on the list */ | ||
173 | list_add(¬ifier->list, ¬ifier_list); | ||
174 | |||
175 | list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { | 173 | list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { |
176 | int ret; | 174 | int ret; |
177 | 175 | ||
@@ -186,6 +184,9 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, | |||
186 | } | 184 | } |
187 | } | 185 | } |
188 | 186 | ||
187 | /* Keep also completed notifiers on the list */ | ||
188 | list_add(¬ifier->list, ¬ifier_list); | ||
189 | |||
189 | mutex_unlock(&list_lock); | 190 | mutex_unlock(&list_lock); |
190 | 191 | ||
191 | return 0; | 192 | return 0; |