aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTuukka Toivonen <tuukka.toivonen@intel.com>2017-01-27 05:32:56 -0500
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2017-02-03 11:51:56 -0500
commit47b037a0512d9f8675ec2693bed46c8ea6a884ab (patch)
tree2cc74ab5cd545ba1fb239031d935659994ddf51e
parent79a2eda80c6dab79790c308d9f50ecd2e5021ba3 (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, &notifier->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.c21
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, &notifier->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, &notifier->done);
123
123 if (list_empty(&notifier->waiting) && notifier->complete) 124 if (list_empty(&notifier->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(&notifier->list, &notifier_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(&notifier->list, &notifier_list);
189
189 mutex_unlock(&list_lock); 190 mutex_unlock(&list_lock);
190 191
191 return 0; 192 return 0;