public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [patch] gpio: potential null dereference
@ 2010-04-26 19:25 Dan Carpenter
  2010-04-26 23:05 ` Daniel Glöckner
  0 siblings, 1 reply; 6+ messages in thread
From: Dan Carpenter @ 2010-04-26 19:25 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Jani Nikula, David Brownell, Daniel Glöckner, Andi Kleen,
	linux-kernel, kernel-janitors

Smatch found a potential null dereference in gpio_setup_irq().  The 
"pdesc" variable is allocated with idr_find() that can return NULL.  If
gpio_setup_irq() is called with 0 as gpio_flags and "pdesc" is null, it
would OOPs here.

Signed-off-by: Dan Carpenter <error27@gmail.com>

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 76be229..eb0c3fe 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -416,7 +416,8 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
 	return 0;
 
 free_sd:
-	sysfs_put(pdesc->value_sd);
+	if (pdesc)
+		sysfs_put(pdesc->value_sd);
 free_id:
 	idr_remove(&pdesc_idr, id);
 	desc->flags &= GPIO_FLAGS_MASK;

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

* Re: [patch] gpio: potential null dereference
  2010-04-26 19:25 [patch] gpio: potential null dereference Dan Carpenter
@ 2010-04-26 23:05 ` Daniel Glöckner
  2010-04-26 23:14   ` Andrew Morton
  2010-04-27  9:05   ` Dan Carpenter
  0 siblings, 2 replies; 6+ messages in thread
From: Daniel Glöckner @ 2010-04-26 23:05 UTC (permalink / raw)
  To: Dan Carpenter, Andrew Morton, Jani Nikula, David Brownell,
	Andi Kleen, linux-kernel, kernel-janitors

On Mon, Apr 26, 2010 at 09:25:20PM +0200, Dan Carpenter wrote:
> Smatch found a potential null dereference in gpio_setup_irq().  The 
> "pdesc" variable is allocated with idr_find() that can return NULL.  If
> gpio_setup_irq() is called with 0 as gpio_flags and "pdesc" is null, it
> would OOPs here.

idr_find() doesn't allocate, idr_get_new_above() does.
Assuming idr_find() never fails for an id if idr_get_new_above()
successfully allocated that id, I don't think we can reach that
line with pdesc being NULL:

- There are two gotos leading to free_sd
- #2 is after a block that allocates pdesc
- #1 is in an if (!gpio_flags) block
- We exit early if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
- Therefore (desc->flags & GPIO_TRIGGER_MASK) must be != 0 to reach #1
- Trigger flags are added to desc->flags only after we have
  successfully allocated pdesc (i.e. right before return 0)
- We start off with no trigger flags set

  Daniel


-- 
Dipl.-Math. Daniel Glöckner, emlix GmbH, http://www.emlix.com
Fon +49 551 30664-0, Fax -11, Bahnhofsallee 1b, 37081 Göttingen, Germany
Sitz der Gesellschaft: Göttingen, Amtsgericht Göttingen HR B 3160
Geschäftsführung: Dr. Uwe Kracke, Ust-IdNr.: DE 205 198 055

emlix - your embedded linux partner

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

* Re: [patch] gpio: potential null dereference
  2010-04-26 23:05 ` Daniel Glöckner
@ 2010-04-26 23:14   ` Andrew Morton
  2010-04-27  9:05   ` Dan Carpenter
  1 sibling, 0 replies; 6+ messages in thread
From: Andrew Morton @ 2010-04-26 23:14 UTC (permalink / raw)
  To: Daniel Glöckner
  Cc: Dan Carpenter, Jani Nikula, David Brownell, Andi Kleen,
	linux-kernel, kernel-janitors

On Tue, 27 Apr 2010 01:05:15 +0200
Daniel Gl__ckner <dg@emlix.com> wrote:

> On Mon, Apr 26, 2010 at 09:25:20PM +0200, Dan Carpenter wrote:
> > Smatch found a potential null dereference in gpio_setup_irq().  The 
> > "pdesc" variable is allocated with idr_find() that can return NULL.  If
> > gpio_setup_irq() is called with 0 as gpio_flags and "pdesc" is null, it
> > would OOPs here.
> 
> idr_find() doesn't allocate, idr_get_new_above() does.
> Assuming idr_find() never fails for an id if idr_get_new_above()
> successfully allocated that id, I don't think we can reach that
> line with pdesc being NULL:
> 
> - There are two gotos leading to free_sd
> - #2 is after a block that allocates pdesc
> - #1 is in an if (!gpio_flags) block
> - We exit early if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
> - Therefore (desc->flags & GPIO_TRIGGER_MASK) must be != 0 to reach #1
> - Trigger flags are added to desc->flags only after we have
>   successfully allocated pdesc (i.e. right before return 0)
> - We start off with no trigger flags set
> 

heh, thanks.  I figured that something like that might be going on but
couldn't be bothered picking through it all.

That being said, the above sequence of steps is awfully (and unusually)
intricate and is hence hard to preserve and could get broken at some
future time.


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

* Re: [patch] gpio: potential null dereference
  2010-04-26 23:05 ` Daniel Glöckner
  2010-04-26 23:14   ` Andrew Morton
@ 2010-04-27  9:05   ` Dan Carpenter
  2010-04-27  9:41     ` Daniel Glöckner
  1 sibling, 1 reply; 6+ messages in thread
From: Dan Carpenter @ 2010-04-27  9:05 UTC (permalink / raw)
  To: Daniel Glöckner
  Cc: Andrew Morton, Jani Nikula, David Brownell, Andi Kleen,
	linux-kernel, kernel-janitors

On Tue, Apr 27, 2010 at 01:05:15AM +0200, Daniel Glöckner wrote:
> On Mon, Apr 26, 2010 at 09:25:20PM +0200, Dan Carpenter wrote:
> > Smatch found a potential null dereference in gpio_setup_irq().  The 
> > "pdesc" variable is allocated with idr_find() that can return NULL.  If
> > gpio_setup_irq() is called with 0 as gpio_flags and "pdesc" is null, it
> > would OOPs here.
> 
> idr_find() doesn't allocate, idr_get_new_above() does.
> Assuming idr_find() never fails for an id if idr_get_new_above()
> successfully allocated that id, I don't think we can reach that
> line with pdesc being NULL:
> 
> - There are two gotos leading to free_sd
> - #2 is after a block that allocates pdesc
> - #1 is in an if (!gpio_flags) block
> - We exit early if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
> - Therefore (desc->flags & GPIO_TRIGGER_MASK) must be != 0 to reach #1
> - Trigger flags are added to desc->flags only after we have
>   successfully allocated pdesc (i.e. right before return 0)
> - We start off with no trigger flags set
> 

Are you sure?  If we know that the call to idr_find() returns a valid 
pointer we could remove a lot of error handling code...

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 76be229..54922a6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -330,14 +330,6 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 	return IRQ_HANDLED;
 }
 
-static void gpio_notify_sysfs(struct work_struct *work)
-{
-	struct poll_desc	*pdesc;
-
-	pdesc = container_of(work, struct poll_desc, work);
-	sysfs_notify_dirent(pdesc->value_sd);
-}
-
 static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
 		unsigned long gpio_flags)
 {
@@ -353,14 +345,10 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
 		return -EIO;
 
 	id = desc->flags >> PDESC_ID_SHIFT;
+	/* idr_find() always returns a valid pointer here */
 	pdesc = idr_find(&pdesc_idr, id);
-	if (pdesc) {
-		free_irq(irq, &pdesc->work);
-		cancel_work_sync(&pdesc->work);
-	}
 
 	desc->flags &= ~GPIO_TRIGGER_MASK;
-
 	if (!gpio_flags) {
 		ret = 0;
 		goto free_sd;
@@ -374,39 +362,6 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
 		irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
 			IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
 
-	if (!pdesc) {
-		pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
-		if (!pdesc) {
-			ret = -ENOMEM;
-			goto err_out;
-		}
-
-		do {
-			ret = -ENOMEM;
-			if (idr_pre_get(&pdesc_idr, GFP_KERNEL))
-				ret = idr_get_new_above(&pdesc_idr,
-						pdesc, 1, &id);
-		} while (ret == -EAGAIN);
-
-		if (ret)
-			goto free_mem;
-
-		desc->flags &= GPIO_FLAGS_MASK;
-		desc->flags |= (unsigned long)id << PDESC_ID_SHIFT;
-
-		if (desc->flags >> PDESC_ID_SHIFT != id) {
-			ret = -ERANGE;
-			goto free_id;
-		}
-
-		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
-		if (!pdesc->value_sd) {
-			ret = -ENODEV;
-			goto free_id;
-		}
-		INIT_WORK(&pdesc->work, gpio_notify_sysfs);
-	}
-
 	ret = request_irq(irq, gpio_sysfs_irq, irq_flags,
 			"gpiolib", &pdesc->work);
 	if (ret)
@@ -417,12 +372,9 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
 
 free_sd:
 	sysfs_put(pdesc->value_sd);
-free_id:
 	idr_remove(&pdesc_idr, id);
 	desc->flags &= GPIO_FLAGS_MASK;
-free_mem:
 	kfree(pdesc);
-err_out:
 	return ret;
 }
 


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

* Re: [patch] gpio: potential null dereference
  2010-04-27  9:05   ` Dan Carpenter
@ 2010-04-27  9:41     ` Daniel Glöckner
  2010-04-27 10:30       ` Dan Carpenter
  0 siblings, 1 reply; 6+ messages in thread
From: Daniel Glöckner @ 2010-04-27  9:41 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Andrew Morton, Jani Nikula, David Brownell, Andi Kleen,
	linux-kernel, kernel-janitors

On 04/27/2010 11:05 AM, Dan Carpenter wrote:
> On Tue, Apr 27, 2010 at 01:05:15AM +0200, Daniel Glöckner wrote:
>> On Mon, Apr 26, 2010 at 09:25:20PM +0200, Dan Carpenter wrote:
>>> Smatch found a potential null dereference in gpio_setup_irq().  The 
>>> "pdesc" variable is allocated with idr_find() that can return NULL.  If
>>> gpio_setup_irq() is called with 0 as gpio_flags and "pdesc" is null, it
>>> would OOPs here.
>> idr_find() doesn't allocate, idr_get_new_above() does.
>> Assuming idr_find() never fails for an id if idr_get_new_above()
>> successfully allocated that id, I don't think we can reach that
>> line with pdesc being NULL:
>>
>> - There are two gotos leading to free_sd
>> - #2 is after a block that allocates pdesc
>> - #1 is in an if (!gpio_flags) block
>> - We exit early if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
>> - Therefore (desc->flags & GPIO_TRIGGER_MASK) must be != 0 to reach #1
>> - Trigger flags are added to desc->flags only after we have
>>   successfully allocated pdesc (i.e. right before return 0)
>> - We start off with no trigger flags set
>>
> 
> Are you sure?  If we know that the call to idr_find() returns a valid 
> pointer we could remove a lot of error handling code...

It returns a valid pointer iff (desc->flags & GPIO_TRIGGER_MASK).

When !(desc->flags & GPIO_TRIGGER_MASK) it returns NULL and everything
gets allocated (unless !gpio_flags as well). What you wanted to remove
is the allocation code, not error handling code. It is always run when
the trigger is set from "none" to something else.

  Daniel

-- 
Dipl.-Math. Daniel Glöckner, emlix GmbH, http://www.emlix.com
Fon +49 551 30664-0, Fax -11, Bahnhofsallee 1b, 37081 Göttingen, Germany
Sitz der Gesellschaft: Göttingen, Amtsgericht Göttingen HR B 3160
Geschäftsführer: Dr. Uwe Kracke, Ust-IdNr.: DE 205 198 055

emlix - your embedded linux partner

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

* Re: [patch] gpio: potential null dereference
  2010-04-27  9:41     ` Daniel Glöckner
@ 2010-04-27 10:30       ` Dan Carpenter
  0 siblings, 0 replies; 6+ messages in thread
From: Dan Carpenter @ 2010-04-27 10:30 UTC (permalink / raw)
  To: Daniel Glöckner
  Cc: Andrew Morton, Jani Nikula, David Brownell, Andi Kleen,
	linux-kernel, kernel-janitors

On Tue, Apr 27, 2010 at 11:41:16AM +0200, Daniel Glöckner wrote:
> It returns a valid pointer iff (desc->flags & GPIO_TRIGGER_MASK).
> 
> When !(desc->flags & GPIO_TRIGGER_MASK) it returns NULL and everything
> gets allocated (unless !gpio_flags as well). What you wanted to remove
> is the allocation code, not error handling code. 

No no.  I don't want to remove anything.  I'm happy with the code as is
now that you've explained it to me.  :)

regards,
dan carpenter

> It is always run when
> the trigger is set from "none" to something else.
> 
>   Daniel
>

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

end of thread, other threads:[~2010-04-27 10:34 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-04-26 19:25 [patch] gpio: potential null dereference Dan Carpenter
2010-04-26 23:05 ` Daniel Glöckner
2010-04-26 23:14   ` Andrew Morton
2010-04-27  9:05   ` Dan Carpenter
2010-04-27  9:41     ` Daniel Glöckner
2010-04-27 10:30       ` Dan Carpenter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox