* [PATCH v2] v4l: async: Protect against double notifier registrations
@ 2018-01-16 23:47 ` Kieran Bingham
0 siblings, 0 replies; 4+ messages in thread
From: Kieran Bingham @ 2018-01-16 23:47 UTC (permalink / raw)
To: linux-media, sakari.ailus
Cc: niklas.soderlund, Laurent Pinchart, hans.verkuil, mchehab,
linux-renesas-soc, Kieran Bingham
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
It can be easy to attempt to register the same notifier twice
in mis-handled error cases such as working with -EPROBE_DEFER.
This results in odd kernel crashes where the notifier_list becomes
corrupted due to adding the same entry twice.
Protect against this so that a developer has some sense of the pending
failure, and use a WARN_ON to identify the fault.
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
v2:
- Reduce verbosity
- use WARN_ON()
- Move notifier list initialisation after registration check
drivers/media/v4l2-core/v4l2-async.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 2b08d03b251d..17a779440ae3 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -374,17 +374,26 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
struct device *dev =
notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL;
struct v4l2_async_subdev *asd;
+ struct v4l2_async_notifier *n;
int ret;
int i;
if (notifier->num_subdevs > V4L2_MAX_SUBDEVS)
return -EINVAL;
+ mutex_lock(&list_lock);
+
+ /* Avoid re-registering a notifier. */
+ list_for_each_entry(n, ¬ifier_list, list) {
+ if (WARN_ON(n == notifier)) {
+ ret = -EEXIST;
+ goto err_unlock;
+ }
+ }
+
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
- mutex_lock(&list_lock);
-
for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i];
--
2.7.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2] v4l: async: Protect against double notifier registrations
@ 2018-01-16 23:47 ` Kieran Bingham
0 siblings, 0 replies; 4+ messages in thread
From: Kieran Bingham @ 2018-01-16 23:47 UTC (permalink / raw)
To: linux-media, sakari.ailus
Cc: niklas.soderlund, Laurent Pinchart, hans.verkuil, mchehab,
linux-renesas-soc, Kieran Bingham
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
It can be easy to attempt to register the same notifier twice
in mis-handled error cases such as working with -EPROBE_DEFER.
This results in odd kernel crashes where the notifier_list becomes
corrupted due to adding the same entry twice.
Protect against this so that a developer has some sense of the pending
failure, and use a WARN_ON to identify the fault.
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
v2:
- Reduce verbosity
- use WARN_ON()
- Move notifier list initialisation after registration check
drivers/media/v4l2-core/v4l2-async.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 2b08d03b251d..17a779440ae3 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -374,17 +374,26 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
struct device *dev =
notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL;
struct v4l2_async_subdev *asd;
+ struct v4l2_async_notifier *n;
int ret;
int i;
if (notifier->num_subdevs > V4L2_MAX_SUBDEVS)
return -EINVAL;
+ mutex_lock(&list_lock);
+
+ /* Avoid re-registering a notifier. */
+ list_for_each_entry(n, ¬ifier_list, list) {
+ if (WARN_ON(n == notifier)) {
+ ret = -EEXIST;
+ goto err_unlock;
+ }
+ }
+
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
- mutex_lock(&list_lock);
-
for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i];
--
2.7.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] v4l: async: Protect against double notifier registrations
2018-01-16 23:47 ` Kieran Bingham
(?)
@ 2018-01-17 8:00 ` Geert Uytterhoeven
2018-01-25 21:40 ` Kieran Bingham
-1 siblings, 1 reply; 4+ messages in thread
From: Geert Uytterhoeven @ 2018-01-17 8:00 UTC (permalink / raw)
To: Kieran Bingham
Cc: Linux Media Mailing List, Sakari Ailus, Niklas Söderlund,
Laurent Pinchart, Hans Verkuil, Mauro Carvalho Chehab,
Linux-Renesas, Kieran Bingham
Hi Kieran,
On Wed, Jan 17, 2018 at 12:47 AM, Kieran Bingham
<kieran.bingham@ideasonboard.com> wrote:
> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>
> It can be easy to attempt to register the same notifier twice
> in mis-handled error cases such as working with -EPROBE_DEFER.
>
> This results in odd kernel crashes where the notifier_list becomes
> corrupted due to adding the same entry twice.
>
> Protect against this so that a developer has some sense of the pending
> failure, and use a WARN_ON to identify the fault.
>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Thanks for your patch!
However, I have several comments:
1. Instead of walking notifier_list (O(n)), can't you just check if
notifier.list is part of a list or not (O(1))?
2. Isn't notifier usually (always?) allocated dynamically, so if will be a
different pointer after a previous -EPROBE_DEFER anyway?
3. If you enable CONFIG_DEBUG_LIST, it should scream, too.
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -374,17 +374,26 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
> struct device *dev =
> notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL;
> struct v4l2_async_subdev *asd;
> + struct v4l2_async_notifier *n;
> int ret;
> int i;
>
> if (notifier->num_subdevs > V4L2_MAX_SUBDEVS)
> return -EINVAL;
>
> + mutex_lock(&list_lock);
> +
> + /* Avoid re-registering a notifier. */
> + list_for_each_entry(n, ¬ifier_list, list) {
> + if (WARN_ON(n == notifier)) {
> + ret = -EEXIST;
> + goto err_unlock;
> + }
> + }
> +
> INIT_LIST_HEAD(¬ifier->waiting);
> INIT_LIST_HEAD(¬ifier->done);
>
> - mutex_lock(&list_lock);
> -
> for (i = 0; i < notifier->num_subdevs; i++) {
> asd = notifier->subdevs[i];
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] v4l: async: Protect against double notifier registrations
2018-01-17 8:00 ` Geert Uytterhoeven
@ 2018-01-25 21:40 ` Kieran Bingham
0 siblings, 0 replies; 4+ messages in thread
From: Kieran Bingham @ 2018-01-25 21:40 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Linux Media Mailing List, Sakari Ailus, Niklas Söderlund,
Laurent Pinchart, Hans Verkuil, Mauro Carvalho Chehab,
Linux-Renesas, Kieran Bingham
[-- Attachment #1: Type: text/plain, Size: 4105 bytes --]
Hi Geert,
Thanks for the review
On 17/01/18 08:00, Geert Uytterhoeven wrote:
> Hi Kieran,
>
> On Wed, Jan 17, 2018 at 12:47 AM, Kieran Bingham
> <kieran.bingham@ideasonboard.com> wrote:
>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>
>> It can be easy to attempt to register the same notifier twice
>> in mis-handled error cases such as working with -EPROBE_DEFER.
>>
>> This results in odd kernel crashes where the notifier_list becomes
>> corrupted due to adding the same entry twice.
>>
>> Protect against this so that a developer has some sense of the pending
>> failure, and use a WARN_ON to identify the fault.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>
> Thanks for your patch!
>
> However, I have several comments:
> 1. Instead of walking notifier_list (O(n)), can't you just check if
> notifier.list is part of a list or not (O(1))?
Not safely as far as I can see: (unless you know better)
Looks like I'd have to at least check something like the following:
notifier->next != LIST_POISON1 && notifier->next != NULL &&
notifier->prev != LIST_POISON2 && notifier->prev != NULL &&
notifier->next != notifier->prev
Although - that doesn't count the possibility that the struct list_head in the
object being added is essentially un-initialised before being added to the list
- so it could technically contain any value ...
(Looking forward to being told I'm completely missing something obvious here...)
> 2. Isn't notifier usually (always?) allocated dynamically, so if will be a
> different pointer after a previous -EPROBE_DEFER anyway?
Nope. The notifier can be part of the device context structure to reduce
allocations.
> 3. If you enable CONFIG_DEBUG_LIST, it should scream, too.
Aha - maybe that was my missing link. -E_NOT_ENOUGH_DEBUG_ENABLED.
Although I've just looked through the code that checks against a double entry.
It may have helped me find my bug in fact, but I think that would only fire if
the entry tried to add twice consecutively, which certainly wouldn't be
guaranteed if a driver returned with -EPROBE_DEFER.
So - I've just tested that if you have A B C and HEAD,
list_add(A, HEAD);
list_add(A, HEAD);
// would fire in __list_add_valid as (new == prev || new == next)
However,
list_add(A, HEAD);
list_add(B, HEAD);
list_add(C, HEAD);
list_add(B, HEAD);
Will not catch this double-add-B in __list_add_valid(), and will generate an
infinitely looping list if you try to then walk it with list_for_each_entry()
(As demonstrated by the attached list-test.c module which I used to test this)
Oh what fun :D
--
Kieran
>
>> --- a/drivers/media/v4l2-core/v4l2-async.c
>> +++ b/drivers/media/v4l2-core/v4l2-async.c
>> @@ -374,17 +374,26 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
>> struct device *dev =
>> notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL;
>> struct v4l2_async_subdev *asd;
>> + struct v4l2_async_notifier *n;
>> int ret;
>> int i;
>>
>> if (notifier->num_subdevs > V4L2_MAX_SUBDEVS)
>> return -EINVAL;
>>
>> + mutex_lock(&list_lock);
>> +
>> + /* Avoid re-registering a notifier. */
>> + list_for_each_entry(n, ¬ifier_list, list) {
>> + if (WARN_ON(n == notifier)) {
>> + ret = -EEXIST;
>> + goto err_unlock;
>> + }
>> + }
>> +
>> INIT_LIST_HEAD(¬ifier->waiting);
>> INIT_LIST_HEAD(¬ifier->done);
>>
>> - mutex_lock(&list_lock);
>> -
>> for (i = 0; i < notifier->num_subdevs; i++) {
>> asd = notifier->subdevs[i];
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: list-test.c --]
[-- Type: text/x-csrc; name="list-test.c", Size: 951 bytes --]
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
struct item {
struct list_head list;
int i;
};
struct item items[5];
int __init helloworld_init(void)
{
int a;
struct list_head head;
struct item * ob;
bool catch_double_add = 1;
INIT_LIST_HEAD(&head);
printk("Hello World !\n");
for (a = 0; a < 5; a++) {
items[a].i = a;
list_add(&items[a].list, &head);
}
for (a = 0; a < 5; a++)
printk("Item[%d] = %d\n", a, items[a].i);
list_for_each_entry(ob, &head, list) {
printk("ob = %d\n", ob->i);
}
if (catch_double_add)
list_add(&items[4].list, &head);
else
list_add(&items[2].list, &head);
list_for_each_entry(ob, &head, list) {
printk("ob = %d\n", ob->i);
}
return 0;
}
void __exit helloworld_exit(void)
{
pr_info("Goodbye cruel world...\n");
}
module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2018-01-25 21:40 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-16 23:47 [PATCH v2] v4l: async: Protect against double notifier registrations Kieran Bingham
2018-01-16 23:47 ` Kieran Bingham
2018-01-17 8:00 ` Geert Uytterhoeven
2018-01-25 21:40 ` Kieran Bingham
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.