linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* (still) NULL pointer crashes with nuvoton_cir driver
@ 2012-08-15 16:51 Matthijs Kooijman
  2012-08-16  8:09 ` Matthijs Kooijman
  2012-10-15 11:01 ` Matthijs Kooijman
  0 siblings, 2 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-08-15 16:51 UTC (permalink / raw)
  To: linux-media; +Cc: Luis Henriques, Jarod Wilson

[-- Attachment #1: Type: text/plain, Size: 2176 bytes --]

(Please keep me CC'd, I'm not on the list)

Hi folks,

I've been suffering from a NULL pointer crash while initializing the
nuvoton_cir driver. It occurs 100% reliable on a 3.5 kernel when I
bombard my receiver with IR signals during bootup.

It seems that 9ef449c: [media] rc: Postpone ISR registration attempts to
fix this very issue, but does not fix it completely.

The error I'm seeing is a NULL pointer dereference at
ir_raw_event_store_with_filter+0xd. I couldn't get gdb to disassemble
this function for some reason, but given the low offset, it's probably
very early in the fuction (code taken from linux-media master):

    int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
    {
	    if (!dev->raw)
		return -EINVAL;

	    /* Ignore spaces in idle mode */
	    if (dev->idle && !ev->pulse)
		    return 0;

I suspect that the NULL pointer is because dev is NULL.

The only place where ir_raw_event_store_with_filter is called by
nuvoton_cir is in nvt_process_rx_ir_data:

    ir_raw_event_store_with_filter(nvt->rdev, &rawir);

This would mean nvt->rdev is NULL.

I couldn't get at a backtrace, but the backtrace I got from running 3.2
was:
    ir_raw_event_store_with_filter
    nvt_process_rx_ir_data
    nvt_get_rx_ir_data
    nvt_cir_isr

In other words, the ISR is fired before nvt->rdev is initialized.

Looking at nvt_probe, I see that nvt->rdev is indeed assigned _after_
the request_irq calls are made, which would explain the crash I'm
seeing.

I was going to prepare a patch to move rdev initialization further up
(for all ir drivers involved in the previous "Postpone ISR registration
patch", except ene_cir.c and windbond_cir.c which seem to have that
already), but I'm not sure if this is sufficient.

In particular, I'm not sure if it's ok to start firing IRQ's before
rc_register_device is called. rc_register_device also calls
ir_raw_event_register to init rdev->raw, which is again used by
ir_raw_event_store_with_filter.

I'm currently compiling a 3.5 kernel with just the rdev initialization
moved up to see if this will fix my problem at all, but I'd like your
view on this in the meantime as well.

Gr.

Matthijs

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (still) NULL pointer crashes with nuvoton_cir driver
  2012-08-15 16:51 (still) NULL pointer crashes with nuvoton_cir driver Matthijs Kooijman
@ 2012-08-16  8:09 ` Matthijs Kooijman
  2012-08-17 15:04   ` Luis Henriques
  2012-10-15 11:01 ` Matthijs Kooijman
  1 sibling, 1 reply; 16+ messages in thread
From: Matthijs Kooijman @ 2012-08-16  8:09 UTC (permalink / raw)
  To: linux-media; +Cc: Luis Henriques, Jarod Wilson

[-- Attachment #1: Type: text/plain, Size: 1024 bytes --]

Hi folks,

> I'm currently compiling a 3.5 kernel with just the rdev initialization
> moved up to see if this will fix my problem at all, but I'd like your
> view on this in the meantime as well.
Ok, this seems to fix my problem:

--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -1066,6 +1066,7 @@
        /* tx bits */
        rdev->tx_resolution = XYZ;
 #endif
+       nvt->rdev = rdev;
 
        ret = -EBUSY;
        /* now claim resources */
@@ -1090,7 +1091,6 @@
                goto failure5;
 
        device_init_wakeup(&pdev->dev, true);
-       nvt->rdev = rdev;
        nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
        if (debug) {
                cir_dump_regs(nvt);


I'm still not sure if the rc_register_device shouldn't also be moved up. It
seems this doesn't trigger a problem right now, but if there is a problem, I
suspect its trigger window is a lot smaller than with the rdev initialization
problem...

Gr.

Matthijs

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (still) NULL pointer crashes with nuvoton_cir driver
  2012-08-16  8:09 ` Matthijs Kooijman
@ 2012-08-17 15:04   ` Luis Henriques
  2012-09-10 14:46     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 16+ messages in thread
From: Luis Henriques @ 2012-08-17 15:04 UTC (permalink / raw)
  To: Matthijs Kooijman; +Cc: linux-media, Jarod Wilson, Mauro Carvalho Chehab

(Adding Mauro to CC has he is the maintainer)

On Thu, Aug 16, 2012 at 10:09:32AM +0200, Matthijs Kooijman wrote:
> Hi folks,
> 
> > I'm currently compiling a 3.5 kernel with just the rdev initialization
> > moved up to see if this will fix my problem at all, but I'd like your
> > view on this in the meantime as well.
> Ok, this seems to fix my problem:
> 
> --- a/drivers/media/rc/nuvoton-cir.c
> +++ b/drivers/media/rc/nuvoton-cir.c
> @@ -1066,6 +1066,7 @@
>         /* tx bits */
>         rdev->tx_resolution = XYZ;
>  #endif
> +       nvt->rdev = rdev;

This makes sense to me.  Note however that there are more drivers with
a similar problem (e.g., fintek-cir.c).

>  
>         ret = -EBUSY; /* now claim resources */ @@ -1090,7 +1091,6
> @@ goto failure5;
>  
>         device_init_wakeup(&pdev->dev, true);
> -       nvt->rdev = rdev;
>         nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
>         if (debug) {
>                 cir_dump_regs(nvt);
> 
> 
> I'm still not sure if the rc_register_device shouldn't also be moved up. It
> seems this doesn't trigger a problem right now, but if there is a problem, I
> suspect its trigger window is a lot smaller than with the rdev initialization
> problem...

I'm not sure as well, I'm not very familiar with this code.  However,
it looks like the IRQ request should actually be one of the last
things to do here.

Cheers,
--
Luis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (still) NULL pointer crashes with nuvoton_cir driver
  2012-08-17 15:04   ` Luis Henriques
@ 2012-09-10 14:46     ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2012-09-10 14:46 UTC (permalink / raw)
  To: Luis Henriques, Jarod Wilson
  Cc: Matthijs Kooijman, linux-media, Mauro Carvalho Chehab

Em 17-08-2012 12:04, Luis Henriques escreveu:
> (Adding Mauro to CC has he is the maintainer)

I'm actually waiting for Jarod's comments on it, as he wrote this driver
and the similar ones.

Jarod?

Regards,
Mauro

> 
> On Thu, Aug 16, 2012 at 10:09:32AM +0200, Matthijs Kooijman wrote:
>> Hi folks,
>>
>>> I'm currently compiling a 3.5 kernel with just the rdev initialization
>>> moved up to see if this will fix my problem at all, but I'd like your
>>> view on this in the meantime as well.
>> Ok, this seems to fix my problem:
>>
>> --- a/drivers/media/rc/nuvoton-cir.c
>> +++ b/drivers/media/rc/nuvoton-cir.c
>> @@ -1066,6 +1066,7 @@
>>         /* tx bits */
>>         rdev->tx_resolution = XYZ;
>>  #endif
>> +       nvt->rdev = rdev;
> 
> This makes sense to me.  Note however that there are more drivers with
> a similar problem (e.g., fintek-cir.c).
> 
>>  
>>         ret = -EBUSY; /* now claim resources */ @@ -1090,7 +1091,6
>> @@ goto failure5;
>>  
>>         device_init_wakeup(&pdev->dev, true);
>> -       nvt->rdev = rdev;
>>         nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
>>         if (debug) {
>>                 cir_dump_regs(nvt);
>>
>>
>> I'm still not sure if the rc_register_device shouldn't also be moved up. It
>> seems this doesn't trigger a problem right now, but if there is a problem, I
>> suspect its trigger window is a lot smaller than with the rdev initialization
>> problem...
> 
> I'm not sure as well, I'm not very familiar with this code.  However,
> it looks like the IRQ request should actually be one of the last
> things to do here.
> 
> Cheers,
> --
> Luis
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (still) NULL pointer crashes with nuvoton_cir driver
  2012-08-15 16:51 (still) NULL pointer crashes with nuvoton_cir driver Matthijs Kooijman
  2012-08-16  8:09 ` Matthijs Kooijman
@ 2012-10-15 11:01 ` Matthijs Kooijman
  2012-10-15 11:13   ` [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure Matthijs Kooijman
                     ` (2 more replies)
  1 sibling, 3 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-10-15 11:01 UTC (permalink / raw)
  To: linux-media, Luis Henriques, Jarod Wilson, Stephan Raue,
	Mauro Carvalho Chehab

[-- Attachment #1: Type: text/plain, Size: 2665 bytes --]

Hey Folks,

> I've been suffering from a NULL pointer crash while initializing the
> nuvoton_cir driver. It occurs 100% reliable on a 3.5 kernel when I
> bombard my receiver with IR signals during bootup.
>
> It seems that 9ef449c: [media] rc: Postpone ISR registration attempts to
> fix this very issue, but does not fix it completely.

I've looked a bit more closely at the code and I do not really
understand why this commit actually fixed the crash for some (ite-cir)
users. Looking at the bugreport linked to in that commit [1], users
report their bug is fixed, but I don't really understand why. Perhaps
the window for this race condition is just made smaller, but not
entirely eliminated?

Note that the actual patch tested by the Launchpad users (which is
included in the Ubuntu kernel) is a smaller patch than the one in git,
but the essence is still the same.

[1]: http://bugs.launchpad.net/bugs/972723

> In particular, I'm not sure if it's ok to start firing IRQ's before
> rc_register_device is called. rc_register_device also calls
> ir_raw_event_register to init rdev->raw, which is again used by
> ir_raw_event_store_with_filter.
It seems this particular thing did not cause a (reproducible) problem,
since ir_raw_event_store_with_filter bails out when rdev->raw is NULL
(returning -EINVAL, but that's ignored by nvt_process_rx_ir_data).

Still, since there is still a small time window in which rdev->raw is
not NULL, but not completely initialized either, I believe that the irqs
should not be registered until after rc_register_device was called.


I've also looked at the USB IR drivers and it seems some potentially
have the same problem. In particular, usb_fill_int_urb (for iguanair and
ati-remote) or redrat3_enable_detector (for redrat3) is called before
rc_register_device and other initialization, but I don't know the USB
subsystem well enough to know if this is really a problem or if simply
moving th usb_fill_int_urb / redrat3_enable_detector would help at all.

The mceusb, streamzap and gpio-ir-recv drivers look like they initialize the interrupt
usb at the end and shouldn't suffer from this problem.


I'll send a patch series for this bug in a minute. It also fixes some
problem in cleanup of ene-ir I noticed while reading the code and
renames some of the cleanup labels (which should make the last patch
easier to review). I've tried to separate changes as much as possible,
if there's anything you'd like to see different, just let me know.

Since I only have nuvoton-cir hardware, the changes to the other drivers
are compile-tested only (but the drivers are similar enough that I
expect no problems there).

Gr.

Matthijs

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure
  2012-10-15 11:01 ` Matthijs Kooijman
@ 2012-10-15 11:13   ` Matthijs Kooijman
  2012-10-15 11:13     ` [PATCH 2/4] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
                       ` (2 more replies)
  2012-10-15 12:32   ` (still) NULL pointer crashes with nuvoton_cir driver Luis Henriques
  2012-11-02 13:13   ` Updated patches for (potential) NULL pointer crashes in cir drivers Matthijs Kooijman
  2 siblings, 3 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-10-15 11:13 UTC (permalink / raw)
  To: linux-media
  Cc: Luis Henriques, Jarod Wilson, Stephan Raue, Mauro Carvalho Chehab,
	Matthijs Kooijman

This makes the cleanup on probe failure more consistent with other
drivers. This is similar to what commit
f27b853ea24a9b70585f9251384d97929e6551c3 ("[media] rc: Fix invalid
free_region and/or free_irq on probe failure") did for some other
drivers.

In addition to making the cleanup more consistent, this also fixes a
case where (on a ene_hw_detect failure) free_region would be called on a
region that was not requested yet.

This last problem was probably introduced by the moving of code in
commit b31b021988fed9e3741a46918f14ba9b063811db ("[media] ene_ir: Fix
driver initialisation") and commit
9ef449c6b31bb6a8e6dedc24de475a3b8c79be20 ("[media] rc: Postpone ISR
registration").

Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl>
---
 drivers/media/rc/ene_ir.c |   29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 647dd95..62f9076 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1000,7 +1000,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
 	rdev = rc_allocate_device();
 	if (!dev || !rdev)
-		goto error1;
+		goto failure;
 
 	/* validate resources */
 	error = -ENODEV;
@@ -1011,10 +1011,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 
 	if (!pnp_port_valid(pnp_dev, 0) ||
 	    pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE)
-		goto error;
+		goto failure;
 
 	if (!pnp_irq_valid(pnp_dev, 0))
-		goto error;
+		goto failure;
 
 	spin_lock_init(&dev->hw_lock);
 
@@ -1030,7 +1030,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	/* detect hardware version and features */
 	error = ene_hw_detect(dev);
 	if (error)
-		goto error;
+		goto failure;
 
 	if (!dev->hw_learning_and_tx_capable && txsim) {
 		dev->hw_learning_and_tx_capable = true;
@@ -1075,30 +1075,27 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	/* claim the resources */
 	error = -EBUSY;
 	if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
-		dev->hw_io = -1;
-		dev->irq = -1;
-		goto error;
+		goto failure;
 	}
 
 	dev->irq = pnp_irq(pnp_dev, 0);
 	if (request_irq(dev->irq, ene_isr,
 			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
-		dev->irq = -1;
-		goto error;
+		goto failure2;
 	}
 
 	error = rc_register_device(rdev);
 	if (error < 0)
-		goto error;
+		goto failure3;
 
 	pr_notice("driver has been successfully loaded\n");
 	return 0;
-error:
-	if (dev && dev->irq >= 0)
-		free_irq(dev->irq, dev);
-	if (dev && dev->hw_io >= 0)
-		release_region(dev->hw_io, ENE_IO_SIZE);
-error1:
+
+failure3:
+	free_irq(dev->irq, dev);
+failure2:
+	release_region(dev->hw_io, ENE_IO_SIZE);
+failure:
 	rc_free_device(rdev);
 	kfree(dev);
 	return error;
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/4] [media] rc: Make probe cleanup goto labels more verbose
  2012-10-15 11:13   ` [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure Matthijs Kooijman
@ 2012-10-15 11:13     ` Matthijs Kooijman
  2012-10-15 11:13     ` [PATCH 3/4] [media] rc: Set rdev before irq setup Matthijs Kooijman
  2012-10-15 11:13     ` [PATCH 4/4] [media] rc: Call rc_register_device " Matthijs Kooijman
  2 siblings, 0 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-10-15 11:13 UTC (permalink / raw)
  To: linux-media
  Cc: Luis Henriques, Jarod Wilson, Stephan Raue, Mauro Carvalho Chehab,
	Matthijs Kooijman

Before, labels were simply numbered. Now, the labels are named after the
cleanup action they'll perform (first), based on how the winbond-cir
driver does it. This makes the code a bit more clear and makes changes
in the ordering of labels easier to review.

This change is applied only to the rc drivers that do significant
cleanup in their probe functions: ati-remote, ene-ir, fintek-cir,
gpio-ir-recv, ite-cir, nuvoton-cir.

This commit should not change any code, it just renames goto labels.

Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl>
---
 drivers/media/rc/ati_remote.c   |   27 ++++++++++++++++-----------
 drivers/media/rc/ene_ir.c       |   20 ++++++++++----------
 drivers/media/rc/fintek-cir.c   |   20 ++++++++++----------
 drivers/media/rc/gpio-ir-recv.c |   19 +++++++++----------
 drivers/media/rc/ite-cir.c      |   18 +++++++++---------
 drivers/media/rc/nuvoton-cir.c  |   30 +++++++++++++++---------------
 6 files changed, 69 insertions(+), 65 deletions(-)

diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 8fa72e2..58fce6a 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -877,11 +877,11 @@ static int ati_remote_probe(struct usb_interface *interface,
 	ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
 	rc_dev = rc_allocate_device();
 	if (!ati_remote || !rc_dev)
-		goto fail1;
+		goto exit_free_dev_rdev;
 
 	/* Allocate URB buffers, URBs */
 	if (ati_remote_alloc_buffers(udev, ati_remote))
-		goto fail2;
+		goto exit_free_buffers;
 
 	ati_remote->endpoint_in = endpoint_in;
 	ati_remote->endpoint_out = endpoint_out;
@@ -929,12 +929,12 @@ static int ati_remote_probe(struct usb_interface *interface,
 	/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
 	err = ati_remote_initialize(ati_remote);
 	if (err)
-		goto fail3;
+		goto exit_kill_urbs;
 
 	/* Set up and register rc device */
 	err = rc_register_device(ati_remote->rdev);
 	if (err)
-		goto fail3;
+		goto exit_kill_urbs;
 
 	/* use our delay for rc_dev */
 	ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay;
@@ -943,26 +943,31 @@ static int ati_remote_probe(struct usb_interface *interface,
 	if (mouse) {
 		input_dev = input_allocate_device();
 		if (!input_dev)
-			goto fail4;
+			goto exit_unregister_device;
 
 		ati_remote->idev = input_dev;
 		ati_remote_input_init(ati_remote);
 		err = input_register_device(input_dev);
 
 		if (err)
-			goto fail5;
+			goto exit_free_input_device;
 	}
 
 	usb_set_intfdata(interface, ati_remote);
 	return 0;
 
- fail5:	input_free_device(input_dev);
- fail4:	rc_unregister_device(rc_dev);
+ exit_free_input_device:
+	input_free_device(input_dev);
+ exit_unregister_device:
+	rc_unregister_device(rc_dev);
 	rc_dev = NULL;
- fail3:	usb_kill_urb(ati_remote->irq_urb);
+ exit_kill_urbs:
+	usb_kill_urb(ati_remote->irq_urb);
 	usb_kill_urb(ati_remote->out_urb);
- fail2:	ati_remote_free_buffers(ati_remote);
- fail1:	rc_free_device(rc_dev);
+ exit_free_buffers:
+	ati_remote_free_buffers(ati_remote);
+ exit_free_dev_rdev:
+	 rc_free_device(rc_dev);
 	kfree(ati_remote);
 	return err;
 }
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 62f9076..7337816 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1000,7 +1000,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
 	rdev = rc_allocate_device();
 	if (!dev || !rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	/* validate resources */
 	error = -ENODEV;
@@ -1011,10 +1011,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 
 	if (!pnp_port_valid(pnp_dev, 0) ||
 	    pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (!pnp_irq_valid(pnp_dev, 0))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	spin_lock_init(&dev->hw_lock);
 
@@ -1030,7 +1030,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	/* detect hardware version and features */
 	error = ene_hw_detect(dev);
 	if (error)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (!dev->hw_learning_and_tx_capable && txsim) {
 		dev->hw_learning_and_tx_capable = true;
@@ -1075,27 +1075,27 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	/* claim the resources */
 	error = -EBUSY;
 	if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	dev->irq = pnp_irq(pnp_dev, 0);
 	if (request_irq(dev->irq, ene_isr,
 			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
-		goto failure2;
+		goto exit_release_hw_io;
 	}
 
 	error = rc_register_device(rdev);
 	if (error < 0)
-		goto failure3;
+		goto exit_free_irq;
 
 	pr_notice("driver has been successfully loaded\n");
 	return 0;
 
-failure3:
+exit_free_irq:
 	free_irq(dev->irq, dev);
-failure2:
+exit_release_hw_io:
 	release_region(dev->hw_io, ENE_IO_SIZE);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(dev);
 	return error;
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index ab30c64..8284d28 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -495,18 +495,18 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 	/* input device for IR remote (and tx) */
 	rdev = rc_allocate_device();
 	if (!rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	ret = -ENODEV;
 	/* validate pnp resources */
 	if (!pnp_port_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_irq_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "IR PNP IRQ not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	fintek->cir_addr = pnp_port_start(pdev, 0);
@@ -523,7 +523,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 
 	ret = fintek_hw_detect(fintek);
 	if (ret)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	/* Initialize CIR & CIR Wake Logical Devices */
 	fintek_config_mode_enable(fintek);
@@ -556,15 +556,15 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 	/* now claim resources */
 	if (!request_region(fintek->cir_addr,
 			    fintek->cir_port_len, FINTEK_DRIVER_NAME))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,
 			FINTEK_DRIVER_NAME, (void *)fintek))
-		goto failure2;
+		goto exit_free_cir_addr;
 
 	ret = rc_register_device(rdev);
 	if (ret)
-		goto failure3;
+		goto exit_free_irq;
 
 	device_init_wakeup(&pdev->dev, true);
 	fintek->rdev = rdev;
@@ -574,11 +574,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 
 	return 0;
 
-failure3:
+exit_free_irq:
 	free_irq(fintek->cir_irq, fintek);
-failure2:
+exit_free_cir_addr:
 	release_region(fintek->cir_addr, fintek->cir_port_len);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(fintek);
 
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 04cb272..0c03b7d 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -79,7 +79,7 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
 	rcdev = rc_allocate_device();
 	if (!rcdev) {
 		rc = -ENOMEM;
-		goto err_allocate_device;
+		goto exit_free_dev;
 	}
 
 	rcdev->priv = gpio_dev;
@@ -104,15 +104,15 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
 
 	rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
 	if (rc < 0)
-		goto err_gpio_request;
+		goto exit_free_rdev;
 	rc  = gpio_direction_input(pdata->gpio_nr);
 	if (rc < 0)
-		goto err_gpio_direction_input;
+		goto exit_free_gpio;
 
 	rc = rc_register_device(rcdev);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "failed to register rc device\n");
-		goto err_register_rc_device;
+		goto exit_free_gpio;
 	}
 
 	platform_set_drvdata(pdev, gpio_dev);
@@ -122,20 +122,19 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
 			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
 					"gpio-ir-recv-irq", gpio_dev);
 	if (rc < 0)
-		goto err_request_irq;
+		goto exit_unregister_device;
 
 	return 0;
 
-err_request_irq:
+exit_unregister_device:
 	platform_set_drvdata(pdev, NULL);
 	rc_unregister_device(rcdev);
-err_register_rc_device:
-err_gpio_direction_input:
+exit_free_gpio:
 	gpio_free(pdata->gpio_nr);
-err_gpio_request:
+exit_free_rdev:
 	rc_free_device(rcdev);
 	rcdev = NULL;
-err_allocate_device:
+exit_free_dev:
 	kfree(gpio_dev);
 	return rc;
 }
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 36fe5a3..77cb21f 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1472,7 +1472,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	/* input device for IR remote (and tx) */
 	rdev = rc_allocate_device();
 	if (!rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	ret = -ENODEV;
 
@@ -1497,12 +1497,12 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	if (!pnp_port_valid(pdev, io_rsrc_no) ||
 	    pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) {
 		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_irq_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	/* store resource values */
@@ -1594,26 +1594,26 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	/* now claim resources */
 	if (!request_region(itdev->cir_addr,
 				dev_desc->io_region_size, ITE_DRIVER_NAME))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED,
 			ITE_DRIVER_NAME, (void *)itdev))
-		goto failure2;
+		goto exit_release_cir_addr;
 
 	ret = rc_register_device(rdev);
 	if (ret)
-		goto failure3;
+		goto exit_free_irq;
 
 	itdev->rdev = rdev;
 	ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 
 	return 0;
 
-failure3:
+exit_free_irq:
 	free_irq(itdev->cir_irq, itdev);
-failure2:
+exit_release_cir_addr:
 	release_region(itdev->cir_addr, itdev->params.io_region_size);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(itdev);
 
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 699eef3..8ab6843 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -986,25 +986,25 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 	/* input device for IR remote (and tx) */
 	rdev = rc_allocate_device();
 	if (!rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	ret = -ENODEV;
 	/* validate pnp resources */
 	if (!pnp_port_valid(pdev, 0) ||
 	    pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) {
 		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_irq_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_port_valid(pdev, 1) ||
 	    pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) {
 		dev_err(&pdev->dev, "Wake PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	nvt->cir_addr = pnp_port_start(pdev, 0);
@@ -1027,7 +1027,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 
 	ret = nvt_hw_detect(nvt);
 	if (ret)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	/* Initialize CIR & CIR Wake Logical Devices */
 	nvt_efm_enable(nvt);
@@ -1070,23 +1070,23 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 	/* now claim resources */
 	if (!request_region(nvt->cir_addr,
 			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
 			NVT_DRIVER_NAME, (void *)nvt))
-		goto failure2;
+		goto exit_release_cir_addr;
 
 	if (!request_region(nvt->cir_wake_addr,
 			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
-		goto failure3;
+		goto exit_free_irq;
 
 	if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
 			NVT_DRIVER_NAME, (void *)nvt))
-		goto failure4;
+		goto exit_release_cir_wake_addr;
 
 	ret = rc_register_device(rdev);
 	if (ret)
-		goto failure5;
+		goto exit_free_wake_irq;
 
 	device_init_wakeup(&pdev->dev, true);
 	nvt->rdev = rdev;
@@ -1098,15 +1098,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 
 	return 0;
 
-failure5:
+exit_free_wake_irq:
 	free_irq(nvt->cir_wake_irq, nvt);
-failure4:
+exit_release_cir_wake_addr:
 	release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
-failure3:
+exit_free_irq:
 	free_irq(nvt->cir_irq, nvt);
-failure2:
+exit_release_cir_addr:
 	release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(nvt);
 
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/4] [media] rc: Set rdev before irq setup
  2012-10-15 11:13   ` [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure Matthijs Kooijman
  2012-10-15 11:13     ` [PATCH 2/4] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
@ 2012-10-15 11:13     ` Matthijs Kooijman
  2012-10-15 11:13     ` [PATCH 4/4] [media] rc: Call rc_register_device " Matthijs Kooijman
  2 siblings, 0 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-10-15 11:13 UTC (permalink / raw)
  To: linux-media
  Cc: Luis Henriques, Jarod Wilson, Stephan Raue, Mauro Carvalho Chehab,
	Matthijs Kooijman

This fixes a problem in fintek-cir, ite-cir and nuvoton-cir where the
irq handler would trigger during module load before the rdev member was
set, causing a NULL pointer crash.

It seems this crash is very reproducible (just bombard the receiver with
IR signals during module load), probably because when request_irq is
called, any pending intterupt is handled immediately, before
request_irq returns and rdev can be set.

This same crash was supposed to be fixed by commit
9ef449c6b31bb6a8e6dedc24de475a3b8c79be20 ("[media] rc: Postpone ISR
registration"), but the crash was still observed on the nuvoton-cir
driver.

This commit was tested on nuvoton-cir only.

Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl>
---
 drivers/media/rc/fintek-cir.c  |    4 +++-
 drivers/media/rc/ite-cir.c     |    3 ++-
 drivers/media/rc/nuvoton-cir.c |    3 ++-
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 8284d28..54809b8 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -552,6 +552,8 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 	/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
 	rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
 
+	fintek->rdev = rdev;
+
 	ret = -EBUSY;
 	/* now claim resources */
 	if (!request_region(fintek->cir_addr,
@@ -567,7 +569,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 		goto exit_free_irq;
 
 	device_init_wakeup(&pdev->dev, true);
-	fintek->rdev = rdev;
+
 	fit_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 	if (debug)
 		cir_dump_regs(fintek);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 77cb21f..158bd0a 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1590,6 +1590,8 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	rdev->driver_name = ITE_DRIVER_NAME;
 	rdev->map_name = RC_MAP_RC6_MCE;
 
+	itdev->rdev = rdev;
+
 	ret = -EBUSY;
 	/* now claim resources */
 	if (!request_region(itdev->cir_addr,
@@ -1604,7 +1606,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	if (ret)
 		goto exit_free_irq;
 
-	itdev->rdev = rdev;
 	ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 
 	return 0;
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 8ab6843..a1b6be6 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -1065,6 +1065,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 	/* tx bits */
 	rdev->tx_resolution = XYZ;
 #endif
+	nvt->rdev = rdev;
 
 	ret = -EBUSY;
 	/* now claim resources */
@@ -1089,7 +1090,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 		goto exit_free_wake_irq;
 
 	device_init_wakeup(&pdev->dev, true);
-	nvt->rdev = rdev;
+
 	nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 	if (debug) {
 		cir_dump_regs(nvt);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/4] [media] rc: Call rc_register_device before irq setup
  2012-10-15 11:13   ` [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure Matthijs Kooijman
  2012-10-15 11:13     ` [PATCH 2/4] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
  2012-10-15 11:13     ` [PATCH 3/4] [media] rc: Set rdev before irq setup Matthijs Kooijman
@ 2012-10-15 11:13     ` Matthijs Kooijman
  2 siblings, 0 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-10-15 11:13 UTC (permalink / raw)
  To: linux-media
  Cc: Luis Henriques, Jarod Wilson, Stephan Raue, Mauro Carvalho Chehab,
	Matthijs Kooijman

This should fix a potential race condition, when the irq handler
triggers while rc_register_device is still setting up the rdev->raw
device.

This crash has not been observed in practice, but there should be a very
small window where it could occur. Since ir_raw_event_store_with_filter
checks if rdev->raw is not NULL before using it, this bug is not
triggered if the request_irq triggers a pending irq directly (since
rdev->raw will still be NULL then).

This commit was tested on nuvoton-cir only.

Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl>
---
 drivers/media/rc/ene_ir.c      |   14 +++++++-------
 drivers/media/rc/ite-cir.c     |   14 +++++++-------
 drivers/media/rc/nuvoton-cir.c |   14 +++++++-------
 drivers/media/rc/winbond-cir.c |   14 +++++++-------
 4 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 7337816..17b38a9 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1072,10 +1072,14 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	device_set_wakeup_capable(&pnp_dev->dev, true);
 	device_set_wakeup_enable(&pnp_dev->dev, true);
 
+	error = rc_register_device(rdev);
+	if (error < 0)
+		goto exit_free_dev_rdev;
+
 	/* claim the resources */
 	error = -EBUSY;
 	if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
-		goto exit_free_dev_rdev;
+		goto exit_unregister_device;
 	}
 
 	dev->irq = pnp_irq(pnp_dev, 0);
@@ -1084,17 +1088,13 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 		goto exit_release_hw_io;
 	}
 
-	error = rc_register_device(rdev);
-	if (error < 0)
-		goto exit_free_irq;
-
 	pr_notice("driver has been successfully loaded\n");
 	return 0;
 
-exit_free_irq:
-	free_irq(dev->irq, dev);
 exit_release_hw_io:
 	release_region(dev->hw_io, ENE_IO_SIZE);
+exit_unregister_device:
+	rc_unregister_device(rdev);
 exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(dev);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 158bd0a..974836a 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1592,28 +1592,28 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 
 	itdev->rdev = rdev;
 
+	ret = rc_register_device(rdev);
+	if (ret)
+		goto exit_free_dev_rdev;
+
 	ret = -EBUSY;
 	/* now claim resources */
 	if (!request_region(itdev->cir_addr,
 				dev_desc->io_region_size, ITE_DRIVER_NAME))
-		goto exit_free_dev_rdev;
+		goto exit_unregister_device;
 
 	if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED,
 			ITE_DRIVER_NAME, (void *)itdev))
 		goto exit_release_cir_addr;
 
-	ret = rc_register_device(rdev);
-	if (ret)
-		goto exit_free_irq;
-
 	ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 
 	return 0;
 
-exit_free_irq:
-	free_irq(itdev->cir_irq, itdev);
 exit_release_cir_addr:
 	release_region(itdev->cir_addr, itdev->params.io_region_size);
+exit_unregister_device:
+	rc_unregister_device(rdev);
 exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(itdev);
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index a1b6be6..18a50b9 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -1067,11 +1067,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 #endif
 	nvt->rdev = rdev;
 
+	ret = rc_register_device(rdev);
+	if (ret)
+		goto exit_free_dev_rdev;
+
 	ret = -EBUSY;
 	/* now claim resources */
 	if (!request_region(nvt->cir_addr,
 			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
-		goto exit_free_dev_rdev;
+		goto exit_unregister_device;
 
 	if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
 			NVT_DRIVER_NAME, (void *)nvt))
@@ -1085,10 +1089,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 			NVT_DRIVER_NAME, (void *)nvt))
 		goto exit_release_cir_wake_addr;
 
-	ret = rc_register_device(rdev);
-	if (ret)
-		goto exit_free_wake_irq;
-
 	device_init_wakeup(&pdev->dev, true);
 
 	nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
@@ -1099,14 +1099,14 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 
 	return 0;
 
-exit_free_wake_irq:
-	free_irq(nvt->cir_wake_irq, nvt);
 exit_release_cir_wake_addr:
 	release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
 exit_free_irq:
 	free_irq(nvt->cir_irq, nvt);
 exit_release_cir_addr:
 	release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
+exit_unregister_device:
+	rc_unregister_device(rdev);
 exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(nvt);
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 54ee348..1f90e8c 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -1035,11 +1035,15 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
 	data->dev->timeout = MS_TO_NS(100);
 	data->dev->allowed_protos = RC_TYPE_ALL;
 
+	err = rc_register_device(data->dev);
+	if (err)
+		goto exit_free_rc;
+
 	if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
 		dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
 			data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
 		err = -EBUSY;
-		goto exit_free_rc;
+		goto exit_unregister_device;
 	}
 
 	if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
@@ -1064,24 +1068,20 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
 		goto exit_release_sbase;
 	}
 
-	err = rc_register_device(data->dev);
-	if (err)
-		goto exit_free_irq;
-
 	device_init_wakeup(&device->dev, 1);
 
 	wbcir_init_hw(data);
 
 	return 0;
 
-exit_free_irq:
-	free_irq(data->irq, device);
 exit_release_sbase:
 	release_region(data->sbase, SP_IOMEM_LEN);
 exit_release_ebase:
 	release_region(data->ebase, EHFUNC_IOMEM_LEN);
 exit_release_wbase:
 	release_region(data->wbase, WAKEUP_IOMEM_LEN);
+exit_unregister_device:
+	rc_unregister_device(data->dev);
 exit_free_rc:
 	rc_free_device(data->dev);
 exit_unregister_led:
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: (still) NULL pointer crashes with nuvoton_cir driver
  2012-10-15 11:01 ` Matthijs Kooijman
  2012-10-15 11:13   ` [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure Matthijs Kooijman
@ 2012-10-15 12:32   ` Luis Henriques
  2012-10-15 14:44     ` Matthijs Kooijman
  2012-11-02 13:13   ` Updated patches for (potential) NULL pointer crashes in cir drivers Matthijs Kooijman
  2 siblings, 1 reply; 16+ messages in thread
From: Luis Henriques @ 2012-10-15 12:32 UTC (permalink / raw)
  To: Matthijs Kooijman, linux-media, Jarod Wilson, Stephan Raue,
	Mauro Carvalho Chehab

On Mon, Oct 15, 2012 at 01:01:11PM +0200, Matthijs Kooijman wrote:
> Hey Folks,
> 
> > I've been suffering from a NULL pointer crash while initializing the
> > nuvoton_cir driver. It occurs 100% reliable on a 3.5 kernel when I
> > bombard my receiver with IR signals during bootup.
> >
> > It seems that 9ef449c: [media] rc: Postpone ISR registration attempts to
> > fix this very issue, but does not fix it completely.
> 
> I've looked a bit more closely at the code and I do not really
> understand why this commit actually fixed the crash for some (ite-cir)
> users. Looking at the bugreport linked to in that commit [1], users
> report their bug is fixed, but I don't really understand why. Perhaps
> the window for this race condition is just made smaller, but not
> entirely eliminated?

Your diagnosis seem to be correct and I have already tried to address
this:

https://lkml.org/lkml/2012/8/25/84

There's also a bug reported here:

https://bugzilla.kernel.org/attachment.cgi?id=78491

I've submitted a patch to try to fix it but got no comments yet.  My
patch moves the request_irq() invocation further down the
initialisation code, after the rc_register_device() (which I believe
is the correct thing to do).

> 
> Note that the actual patch tested by the Launchpad users (which is
> included in the Ubuntu kernel) is a smaller patch than the one in
> git, but the essence is still the same.

Again, this is correct.  The patch tested was just for a subset of the
drivers and this fix was later ported to the other affected drivers.
But again, as you referred above, this doesn't completely fix the
issue as it just reduced the race window.

Cheers,
--
Luis

> 
> [1]: http://bugs.launchpad.net/bugs/972723
> 
> > In particular, I'm not sure if it's ok to start firing IRQ's before
> > rc_register_device is called. rc_register_device also calls
> > ir_raw_event_register to init rdev->raw, which is again used by
> > ir_raw_event_store_with_filter.
> It seems this particular thing did not cause a (reproducible) problem,
> since ir_raw_event_store_with_filter bails out when rdev->raw is NULL
> (returning -EINVAL, but that's ignored by nvt_process_rx_ir_data).
> 
> Still, since there is still a small time window in which rdev->raw is
> not NULL, but not completely initialized either, I believe that the irqs
> should not be registered until after rc_register_device was called.
> 
> 
> I've also looked at the USB IR drivers and it seems some potentially
> have the same problem. In particular, usb_fill_int_urb (for iguanair and
> ati-remote) or redrat3_enable_detector (for redrat3) is called before
> rc_register_device and other initialization, but I don't know the USB
> subsystem well enough to know if this is really a problem or if simply
> moving th usb_fill_int_urb / redrat3_enable_detector would help at all.
> 
> The mceusb, streamzap and gpio-ir-recv drivers look like they initialize the interrupt
> usb at the end and shouldn't suffer from this problem.
> 
> 
> I'll send a patch series for this bug in a minute. It also fixes some
> problem in cleanup of ene-ir I noticed while reading the code and
> renames some of the cleanup labels (which should make the last patch
> easier to review). I've tried to separate changes as much as possible,
> if there's anything you'd like to see different, just let me know.
> 
> Since I only have nuvoton-cir hardware, the changes to the other drivers
> are compile-tested only (but the drivers are similar enough that I
> expect no problems there).
> 
> Gr.
> 
> Matthijs

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (still) NULL pointer crashes with nuvoton_cir driver
  2012-10-15 12:32   ` (still) NULL pointer crashes with nuvoton_cir driver Luis Henriques
@ 2012-10-15 14:44     ` Matthijs Kooijman
  2012-10-15 16:37       ` Matthijs Kooijman
  0 siblings, 1 reply; 16+ messages in thread
From: Matthijs Kooijman @ 2012-10-15 14:44 UTC (permalink / raw)
  To: Luis Henriques
  Cc: linux-media, Jarod Wilson, Stephan Raue, Mauro Carvalho Chehab

[-- Attachment #1: Type: text/plain, Size: 1848 bytes --]

Hey Luis,

> Your diagnosis seem to be correct and I have already tried to address
> this:
> 
> https://lkml.org/lkml/2012/8/25/84
Oh, w00ps, seems I missed that patch, stupid of me...

> I've submitted a patch to try to fix it but got no comments yet.  My
> patch moves the request_irq() invocation further down the
> initialisation code, after the rc_register_device() (which I believe
> is the correct thing to do).

I applied your patch and ran a diff against my four patches. If I disregard
the label changes, there's still three changes between our patches I can
see:
 - You move the rc_register_device calls only above the request_irq
   call, while I also included the request_region call. I don't think
   this should cause any realy difference.
 - You didn't remove the "dev->hw_io = -1" and "dev->irq = -1" lines in
   ene_ir.c, which I think are no longer needed now there are different
   multiple cleanup labels.
 - You did not move up the "...->rdev = rdev" line in fintek-cir.c,
   nuvoton-cir.c and ite-cir.c, which I think means the bug is not
   completely solved for these drivers with your patch.


As for the last point, I did a test run with your patch and could no
longer reproduce the issue (though I'm pretty confident the original
really was nvt->rdev being NULL). I suspect this means that moving the
rc_register_device() up to before the request_irq, prevents the bug from
triggering because either rc_register_device() did something to trigger
a (pending) interrupt, or it just took long enough for an interrupt to
occur.

In either case, I believe there is still a race condition between the
first interrupt and the initialization of of nvt->rdev, which could be
fixed by also moving the rdev initialization a bit further up. Or am I
missing some point here?

Gr.

Matthijs

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (still) NULL pointer crashes with nuvoton_cir driver
  2012-10-15 14:44     ` Matthijs Kooijman
@ 2012-10-15 16:37       ` Matthijs Kooijman
  0 siblings, 0 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-10-15 16:37 UTC (permalink / raw)
  To: Luis Henriques
  Cc: linux-media, Jarod Wilson, Stephan Raue, Mauro Carvalho Chehab

[-- Attachment #1: Type: text/plain, Size: 269 bytes --]

Hey Luis,

seems we're not the only ones working on this, there's a partially
overlapping patch in v3.6.2 for the ite-cir driver only:

http://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=commit;h=434f4d3f1209d4e4c8b0d577d1c809f349c4f0da

Gr.

Matthijs

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Updated patches for (potential) NULL pointer crashes in cir drivers
  2012-10-15 11:01 ` Matthijs Kooijman
  2012-10-15 11:13   ` [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure Matthijs Kooijman
  2012-10-15 12:32   ` (still) NULL pointer crashes with nuvoton_cir driver Luis Henriques
@ 2012-11-02 13:13   ` Matthijs Kooijman
  2012-11-02 13:13     ` [PATCH 1/3] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
                       ` (2 more replies)
  2 siblings, 3 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-11-02 13:13 UTC (permalink / raw)
  To: linux-media; +Cc: Mauro Carvalho Chehab, Stephan Raue, Luis Henriques

(Please keep me CC'd, I'm not subscribed to the list)

Hi folks,

I've updated my patches to apply against Mauro's staging/for_v3.8
branch. The patches have only been modified to apply against recent
changes in some of the drivers.

Like before, these patches have been really tested only on nuvoton_cir,
the others received compile-testing only.

Gr.

Matthijs


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/3] [media] rc: Make probe cleanup goto labels more verbose
  2012-11-02 13:13   ` Updated patches for (potential) NULL pointer crashes in cir drivers Matthijs Kooijman
@ 2012-11-02 13:13     ` Matthijs Kooijman
  2012-11-02 13:13     ` [PATCH 2/3] [media] rc: Set rdev before irq setup Matthijs Kooijman
  2012-11-02 13:13     ` [PATCH 3/3] [media] rc: Call rc_register_device " Matthijs Kooijman
  2 siblings, 0 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-11-02 13:13 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, Stephan Raue, Luis Henriques,
	Matthijs Kooijman, Maxim Levitsky, Jarod Wilson

Before, labels were simply numbered. Now, the labels are named after the
cleanup action they'll perform (first), based on how the winbond-cir
driver does it. This makes the code a bit more clear and makes changes
in the ordering of labels easier to review.

This change is applied only to the rc drivers that do significant
cleanup in their probe functions: ati-remote, ene-ir, fintek-cir,
gpio-ir-recv, ite-cir, nuvoton-cir.

This commit should not change any code, it just renames goto labels.

Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl>
Cc: Maxim Levitsky <maximlevitsky@gmail.com>
Cc: Jarod Wilson <jarod@redhat.com>
---
 drivers/media/rc/ati_remote.c   |   27 ++++++++++++++++-----------
 drivers/media/rc/ene_ir.c       |   20 ++++++++++----------
 drivers/media/rc/fintek-cir.c   |   20 ++++++++++----------
 drivers/media/rc/gpio-ir-recv.c |   19 +++++++++----------
 drivers/media/rc/ite-cir.c      |   18 +++++++++---------
 drivers/media/rc/nuvoton-cir.c  |   30 +++++++++++++++---------------
 6 files changed, 69 insertions(+), 65 deletions(-)

diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 2d6fb26..4d6a63f 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -872,11 +872,11 @@ static int ati_remote_probe(struct usb_interface *interface,
 	ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
 	rc_dev = rc_allocate_device();
 	if (!ati_remote || !rc_dev)
-		goto fail1;
+		goto exit_free_dev_rdev;
 
 	/* Allocate URB buffers, URBs */
 	if (ati_remote_alloc_buffers(udev, ati_remote))
-		goto fail2;
+		goto exit_free_buffers;
 
 	ati_remote->endpoint_in = endpoint_in;
 	ati_remote->endpoint_out = endpoint_out;
@@ -924,12 +924,12 @@ static int ati_remote_probe(struct usb_interface *interface,
 	/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
 	err = ati_remote_initialize(ati_remote);
 	if (err)
-		goto fail3;
+		goto exit_kill_urbs;
 
 	/* Set up and register rc device */
 	err = rc_register_device(ati_remote->rdev);
 	if (err)
-		goto fail3;
+		goto exit_kill_urbs;
 
 	/* use our delay for rc_dev */
 	ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay;
@@ -939,7 +939,7 @@ static int ati_remote_probe(struct usb_interface *interface,
 		input_dev = input_allocate_device();
 		if (!input_dev) {
 			err = -ENOMEM;
-			goto fail4;
+			goto exit_unregister_device;
 		}
 
 		ati_remote->idev = input_dev;
@@ -947,19 +947,24 @@ static int ati_remote_probe(struct usb_interface *interface,
 		err = input_register_device(input_dev);
 
 		if (err)
-			goto fail5;
+			goto exit_free_input_device;
 	}
 
 	usb_set_intfdata(interface, ati_remote);
 	return 0;
 
- fail5:	input_free_device(input_dev);
- fail4:	rc_unregister_device(rc_dev);
+ exit_free_input_device:
+	input_free_device(input_dev);
+ exit_unregister_device:
+	rc_unregister_device(rc_dev);
 	rc_dev = NULL;
- fail3:	usb_kill_urb(ati_remote->irq_urb);
+ exit_kill_urbs:
+	usb_kill_urb(ati_remote->irq_urb);
 	usb_kill_urb(ati_remote->out_urb);
- fail2:	ati_remote_free_buffers(ati_remote);
- fail1:	rc_free_device(rc_dev);
+ exit_free_buffers:
+	ati_remote_free_buffers(ati_remote);
+ exit_free_dev_rdev:
+	 rc_free_device(rc_dev);
 	kfree(ati_remote);
 	return err;
 }
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 22231dd..f7fdfea 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1003,7 +1003,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
 	rdev = rc_allocate_device();
 	if (!dev || !rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	/* validate resources */
 	error = -ENODEV;
@@ -1014,10 +1014,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 
 	if (!pnp_port_valid(pnp_dev, 0) ||
 	    pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (!pnp_irq_valid(pnp_dev, 0))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	spin_lock_init(&dev->hw_lock);
 
@@ -1033,7 +1033,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	/* detect hardware version and features */
 	error = ene_hw_detect(dev);
 	if (error)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (!dev->hw_learning_and_tx_capable && txsim) {
 		dev->hw_learning_and_tx_capable = true;
@@ -1078,27 +1078,27 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	/* claim the resources */
 	error = -EBUSY;
 	if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	dev->irq = pnp_irq(pnp_dev, 0);
 	if (request_irq(dev->irq, ene_isr,
 			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
-		goto failure2;
+		goto exit_release_hw_io;
 	}
 
 	error = rc_register_device(rdev);
 	if (error < 0)
-		goto failure3;
+		goto exit_free_irq;
 
 	pr_notice("driver has been successfully loaded\n");
 	return 0;
 
-failure3:
+exit_free_irq:
 	free_irq(dev->irq, dev);
-failure2:
+exit_release_hw_io:
 	release_region(dev->hw_io, ENE_IO_SIZE);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(dev);
 	return error;
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 936c3f7..3d5e57c 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -500,18 +500,18 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 	/* input device for IR remote (and tx) */
 	rdev = rc_allocate_device();
 	if (!rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	ret = -ENODEV;
 	/* validate pnp resources */
 	if (!pnp_port_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_irq_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "IR PNP IRQ not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	fintek->cir_addr = pnp_port_start(pdev, 0);
@@ -528,7 +528,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 
 	ret = fintek_hw_detect(fintek);
 	if (ret)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	/* Initialize CIR & CIR Wake Logical Devices */
 	fintek_config_mode_enable(fintek);
@@ -561,15 +561,15 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 	/* now claim resources */
 	if (!request_region(fintek->cir_addr,
 			    fintek->cir_port_len, FINTEK_DRIVER_NAME))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,
 			FINTEK_DRIVER_NAME, (void *)fintek))
-		goto failure2;
+		goto exit_free_cir_addr;
 
 	ret = rc_register_device(rdev);
 	if (ret)
-		goto failure3;
+		goto exit_free_irq;
 
 	device_init_wakeup(&pdev->dev, true);
 	fintek->rdev = rdev;
@@ -579,11 +579,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 
 	return 0;
 
-failure3:
+exit_free_irq:
 	free_irq(fintek->cir_irq, fintek);
-failure2:
+exit_free_cir_addr:
 	release_region(fintek->cir_addr, fintek->cir_port_len);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(fintek);
 
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index ba1a1eb..d240036 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -79,7 +79,7 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
 	rcdev = rc_allocate_device();
 	if (!rcdev) {
 		rc = -ENOMEM;
-		goto err_allocate_device;
+		goto exit_free_dev;
 	}
 
 	rcdev->priv = gpio_dev;
@@ -104,15 +104,15 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
 
 	rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
 	if (rc < 0)
-		goto err_gpio_request;
+		goto exit_free_rdev;
 	rc  = gpio_direction_input(pdata->gpio_nr);
 	if (rc < 0)
-		goto err_gpio_direction_input;
+		goto exit_free_gpio;
 
 	rc = rc_register_device(rcdev);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "failed to register rc device\n");
-		goto err_register_rc_device;
+		goto exit_free_gpio;
 	}
 
 	platform_set_drvdata(pdev, gpio_dev);
@@ -122,20 +122,19 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
 			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
 					"gpio-ir-recv-irq", gpio_dev);
 	if (rc < 0)
-		goto err_request_irq;
+		goto exit_unregister_device;
 
 	return 0;
 
-err_request_irq:
+exit_unregister_device:
 	platform_set_drvdata(pdev, NULL);
 	rc_unregister_device(rcdev);
-err_register_rc_device:
-err_gpio_direction_input:
+exit_free_gpio:
 	gpio_free(pdata->gpio_nr);
-err_gpio_request:
+exit_free_rdev:
 	rc_free_device(rcdev);
 	rcdev = NULL;
-err_allocate_device:
+exit_free_dev:
 	kfree(gpio_dev);
 	return rc;
 }
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 5e5a7f2..8e0e661 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1472,7 +1472,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	/* input device for IR remote (and tx) */
 	rdev = rc_allocate_device();
 	if (!rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 	itdev->rdev = rdev;
 
 	ret = -ENODEV;
@@ -1498,12 +1498,12 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	if (!pnp_port_valid(pdev, io_rsrc_no) ||
 	    pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) {
 		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_irq_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	/* store resource values */
@@ -1595,25 +1595,25 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	/* now claim resources */
 	if (!request_region(itdev->cir_addr,
 				dev_desc->io_region_size, ITE_DRIVER_NAME))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED,
 			ITE_DRIVER_NAME, (void *)itdev))
-		goto failure2;
+		goto exit_release_cir_addr;
 
 	ret = rc_register_device(rdev);
 	if (ret)
-		goto failure3;
+		goto exit_free_irq;
 
 	ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 
 	return 0;
 
-failure3:
+exit_free_irq:
 	free_irq(itdev->cir_irq, itdev);
-failure2:
+exit_release_cir_addr:
 	release_region(itdev->cir_addr, itdev->params.io_region_size);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(itdev);
 
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index e4ea89a..3477e23 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -986,25 +986,25 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 	/* input device for IR remote (and tx) */
 	rdev = rc_allocate_device();
 	if (!rdev)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	ret = -ENODEV;
 	/* validate pnp resources */
 	if (!pnp_port_valid(pdev, 0) ||
 	    pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) {
 		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_irq_valid(pdev, 0)) {
 		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	if (!pnp_port_valid(pdev, 1) ||
 	    pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) {
 		dev_err(&pdev->dev, "Wake PNP Port not valid!\n");
-		goto failure;
+		goto exit_free_dev_rdev;
 	}
 
 	nvt->cir_addr = pnp_port_start(pdev, 0);
@@ -1027,7 +1027,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 
 	ret = nvt_hw_detect(nvt);
 	if (ret)
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	/* Initialize CIR & CIR Wake Logical Devices */
 	nvt_efm_enable(nvt);
@@ -1070,23 +1070,23 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 	/* now claim resources */
 	if (!request_region(nvt->cir_addr,
 			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
-		goto failure;
+		goto exit_free_dev_rdev;
 
 	if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
 			NVT_DRIVER_NAME, (void *)nvt))
-		goto failure2;
+		goto exit_release_cir_addr;
 
 	if (!request_region(nvt->cir_wake_addr,
 			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
-		goto failure3;
+		goto exit_free_irq;
 
 	if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
 			NVT_DRIVER_NAME, (void *)nvt))
-		goto failure4;
+		goto exit_release_cir_wake_addr;
 
 	ret = rc_register_device(rdev);
 	if (ret)
-		goto failure5;
+		goto exit_free_wake_irq;
 
 	device_init_wakeup(&pdev->dev, true);
 	nvt->rdev = rdev;
@@ -1098,15 +1098,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 
 	return 0;
 
-failure5:
+exit_free_wake_irq:
 	free_irq(nvt->cir_wake_irq, nvt);
-failure4:
+exit_release_cir_wake_addr:
 	release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
-failure3:
+exit_free_irq:
 	free_irq(nvt->cir_irq, nvt);
-failure2:
+exit_release_cir_addr:
 	release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
-failure:
+exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(nvt);
 
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/3] [media] rc: Set rdev before irq setup
  2012-11-02 13:13   ` Updated patches for (potential) NULL pointer crashes in cir drivers Matthijs Kooijman
  2012-11-02 13:13     ` [PATCH 1/3] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
@ 2012-11-02 13:13     ` Matthijs Kooijman
  2012-11-02 13:13     ` [PATCH 3/3] [media] rc: Call rc_register_device " Matthijs Kooijman
  2 siblings, 0 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-11-02 13:13 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, Stephan Raue, Luis Henriques,
	Matthijs Kooijman, Jarod Wilson

This fixes a problem in fintek-cir and nuvoton-cir where the
irq handler would trigger during module load before the rdev member was
set, causing a NULL pointer crash.

It seems this crash is very reproducible (just bombard the receiver with
IR signals during module load), probably because when request_irq is
called, any pending intterupt is handled immediately, before
request_irq returns and rdev can be set.

This same crash was supposed to be fixed by commit
9ef449c6b31bb6a8e6dedc24de475a3b8c79be20 ("[media] rc: Postpone ISR
registration"), but the crash was still observed on the nuvoton-cir
driver.

This commit was tested on nuvoton-cir only.

Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl>
Cc: Jarod Wilson <jarod@redhat.com>
---
 drivers/media/rc/fintek-cir.c  |    4 +++-
 drivers/media/rc/nuvoton-cir.c |    3 ++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 3d5e57c..5eefe65 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -557,6 +557,8 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 	/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
 	rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
 
+	fintek->rdev = rdev;
+
 	ret = -EBUSY;
 	/* now claim resources */
 	if (!request_region(fintek->cir_addr,
@@ -572,7 +574,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 		goto exit_free_irq;
 
 	device_init_wakeup(&pdev->dev, true);
-	fintek->rdev = rdev;
+
 	fit_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 	if (debug)
 		cir_dump_regs(fintek);
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 3477e23..c6441e6 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -1065,6 +1065,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 	/* tx bits */
 	rdev->tx_resolution = XYZ;
 #endif
+	nvt->rdev = rdev;
 
 	ret = -EBUSY;
 	/* now claim resources */
@@ -1089,7 +1090,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 		goto exit_free_wake_irq;
 
 	device_init_wakeup(&pdev->dev, true);
-	nvt->rdev = rdev;
+
 	nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 	if (debug) {
 		cir_dump_regs(nvt);
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/3] [media] rc: Call rc_register_device before irq setup
  2012-11-02 13:13   ` Updated patches for (potential) NULL pointer crashes in cir drivers Matthijs Kooijman
  2012-11-02 13:13     ` [PATCH 1/3] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
  2012-11-02 13:13     ` [PATCH 2/3] [media] rc: Set rdev before irq setup Matthijs Kooijman
@ 2012-11-02 13:13     ` Matthijs Kooijman
  2 siblings, 0 replies; 16+ messages in thread
From: Matthijs Kooijman @ 2012-11-02 13:13 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, Stephan Raue, Luis Henriques,
	Matthijs Kooijman, Jarod Wilson, Maxim Levitsky,
	David Härdeman

This should fix a potential race condition, when the irq handler
triggers while rc_register_device is still setting up the rdev->raw
device.

This crash has not been observed in practice, but there should be a very
small window where it could occur. Since ir_raw_event_store_with_filter
checks if rdev->raw is not NULL before using it, this bug is not
triggered if the request_irq triggers a pending irq directly (since
rdev->raw will still be NULL then).

This commit was tested on nuvoton-cir only.

Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl>
Cc: Jarod Wilson <jarod@redhat.com>
Cc: Maxim Levitsky <maximlevitsky@gmail.com>
Cc: David Härdeman <david@hardeman.nu>
---
 drivers/media/rc/ene_ir.c      |   14 +++++++-------
 drivers/media/rc/ite-cir.c     |   14 +++++++-------
 drivers/media/rc/nuvoton-cir.c |   14 +++++++-------
 drivers/media/rc/winbond-cir.c |   14 +++++++-------
 4 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index f7fdfea..e601166 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1075,10 +1075,14 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	device_set_wakeup_capable(&pnp_dev->dev, true);
 	device_set_wakeup_enable(&pnp_dev->dev, true);
 
+	error = rc_register_device(rdev);
+	if (error < 0)
+		goto exit_free_dev_rdev;
+
 	/* claim the resources */
 	error = -EBUSY;
 	if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
-		goto exit_free_dev_rdev;
+		goto exit_unregister_device;
 	}
 
 	dev->irq = pnp_irq(pnp_dev, 0);
@@ -1087,17 +1091,13 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 		goto exit_release_hw_io;
 	}
 
-	error = rc_register_device(rdev);
-	if (error < 0)
-		goto exit_free_irq;
-
 	pr_notice("driver has been successfully loaded\n");
 	return 0;
 
-exit_free_irq:
-	free_irq(dev->irq, dev);
 exit_release_hw_io:
 	release_region(dev->hw_io, ENE_IO_SIZE);
+exit_unregister_device:
+	rc_unregister_device(rdev);
 exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(dev);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 8e0e661..e810846 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1591,28 +1591,28 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 	rdev->driver_name = ITE_DRIVER_NAME;
 	rdev->map_name = RC_MAP_RC6_MCE;
 
+	ret = rc_register_device(rdev);
+	if (ret)
+		goto exit_free_dev_rdev;
+
 	ret = -EBUSY;
 	/* now claim resources */
 	if (!request_region(itdev->cir_addr,
 				dev_desc->io_region_size, ITE_DRIVER_NAME))
-		goto exit_free_dev_rdev;
+		goto exit_unregister_device;
 
 	if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED,
 			ITE_DRIVER_NAME, (void *)itdev))
 		goto exit_release_cir_addr;
 
-	ret = rc_register_device(rdev);
-	if (ret)
-		goto exit_free_irq;
-
 	ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
 
 	return 0;
 
-exit_free_irq:
-	free_irq(itdev->cir_irq, itdev);
 exit_release_cir_addr:
 	release_region(itdev->cir_addr, itdev->params.io_region_size);
+exit_unregister_device:
+	rc_unregister_device(rdev);
 exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(itdev);
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index c6441e6..6cf43cc 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -1067,11 +1067,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 #endif
 	nvt->rdev = rdev;
 
+	ret = rc_register_device(rdev);
+	if (ret)
+		goto exit_free_dev_rdev;
+
 	ret = -EBUSY;
 	/* now claim resources */
 	if (!request_region(nvt->cir_addr,
 			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
-		goto exit_free_dev_rdev;
+		goto exit_unregister_device;
 
 	if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
 			NVT_DRIVER_NAME, (void *)nvt))
@@ -1085,10 +1089,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 			NVT_DRIVER_NAME, (void *)nvt))
 		goto exit_release_cir_wake_addr;
 
-	ret = rc_register_device(rdev);
-	if (ret)
-		goto exit_free_wake_irq;
-
 	device_init_wakeup(&pdev->dev, true);
 
 	nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
@@ -1099,14 +1099,14 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 
 	return 0;
 
-exit_free_wake_irq:
-	free_irq(nvt->cir_wake_irq, nvt);
 exit_release_cir_wake_addr:
 	release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
 exit_free_irq:
 	free_irq(nvt->cir_irq, nvt);
 exit_release_cir_addr:
 	release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
+exit_unregister_device:
+	rc_unregister_device(rdev);
 exit_free_dev_rdev:
 	rc_free_device(rdev);
 	kfree(nvt);
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 45d2fff..de76474 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -1024,11 +1024,15 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
 	data->dev->timeout = MS_TO_NS(100);
 	data->dev->allowed_protos = RC_BIT_ALL;
 
+	err = rc_register_device(data->dev);
+	if (err)
+		goto exit_free_rc;
+
 	if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
 		dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
 			data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
 		err = -EBUSY;
-		goto exit_free_rc;
+		goto exit_unregister_device;
 	}
 
 	if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
@@ -1053,24 +1057,20 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
 		goto exit_release_sbase;
 	}
 
-	err = rc_register_device(data->dev);
-	if (err)
-		goto exit_free_irq;
-
 	device_init_wakeup(&device->dev, 1);
 
 	wbcir_init_hw(data);
 
 	return 0;
 
-exit_free_irq:
-	free_irq(data->irq, device);
 exit_release_sbase:
 	release_region(data->sbase, SP_IOMEM_LEN);
 exit_release_ebase:
 	release_region(data->ebase, EHFUNC_IOMEM_LEN);
 exit_release_wbase:
 	release_region(data->wbase, WAKEUP_IOMEM_LEN);
+exit_unregister_device:
+	rc_unregister_device(data->dev);
 exit_free_rc:
 	rc_free_device(data->dev);
 exit_unregister_led:
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2012-11-02 13:14 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-08-15 16:51 (still) NULL pointer crashes with nuvoton_cir driver Matthijs Kooijman
2012-08-16  8:09 ` Matthijs Kooijman
2012-08-17 15:04   ` Luis Henriques
2012-09-10 14:46     ` Mauro Carvalho Chehab
2012-10-15 11:01 ` Matthijs Kooijman
2012-10-15 11:13   ` [PATCH 1/4] [media] ene-ir: Fix cleanup on probe failure Matthijs Kooijman
2012-10-15 11:13     ` [PATCH 2/4] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
2012-10-15 11:13     ` [PATCH 3/4] [media] rc: Set rdev before irq setup Matthijs Kooijman
2012-10-15 11:13     ` [PATCH 4/4] [media] rc: Call rc_register_device " Matthijs Kooijman
2012-10-15 12:32   ` (still) NULL pointer crashes with nuvoton_cir driver Luis Henriques
2012-10-15 14:44     ` Matthijs Kooijman
2012-10-15 16:37       ` Matthijs Kooijman
2012-11-02 13:13   ` Updated patches for (potential) NULL pointer crashes in cir drivers Matthijs Kooijman
2012-11-02 13:13     ` [PATCH 1/3] [media] rc: Make probe cleanup goto labels more verbose Matthijs Kooijman
2012-11-02 13:13     ` [PATCH 2/3] [media] rc: Set rdev before irq setup Matthijs Kooijman
2012-11-02 13:13     ` [PATCH 3/3] [media] rc: Call rc_register_device " Matthijs Kooijman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).