Skip to contentSkip to conversation

staging: gpib: Fix NULL deref and code cleanup

PATCH In review Dave Penkler 3 participants 8 messages Jan 18, 2025 – Jan 20, 2025 Staging

lore
Share & tools
lore
#0 lore raw

cover staging: gpib: Fix NULL deref and code cleanup

Patch 1 Fixes a NULL dereference issue in the detach function Patch 2 Removes useless code
Use kzalloc
Adds proper cleanup on failure exit in the attach function. Minor messaging changes

Dave Penkler (2):
staging: gpib: Fix NULL pointer dereference in detach
staging: gpib: Agilent usb code cleanup

 .../gpib/agilent_82357a/agilent_82357a.c      | 88 ++++++++-----------
 1 file changed, 37 insertions(+), 51 deletions(-)
Signature · 1 line
2.47.1
#1 lore raw

PATCH 1/2 staging: gpib: Fix NULL pointer dereference in detach

When the detach function is called after a failed attach the usb_dev initialization can cause a NULL pointer dereference. This happens when the usb device is not found in the attach procedure.

Remove the usb_dev variable and initialization and change the dev in the dev_info message from the usb_dev to the gpib_dev.

Fixes fbae7090f30c ("staging: gpib: Update messaging and usb_device refs in agilent_usb")
---
 drivers/staging/gpib/agilent_82357a/agilent_82357a.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +1 -3
index 34d85a1bdb37..2aaccebc3c7b 100644
@@ -1442,12 +1442,10 @@ static int agilent_82357a_go_idle(gpib_board_t *board)
 static void agilent_82357a_detach(gpib_board_t *board)
 {
 	struct agilent_82357a_priv *a_priv;
-	struct usb_device *usb_dev;
 
 	mutex_lock(&agilent_82357a_hotplug_lock);
 
 	a_priv = board->private_data;
-	usb_dev = interface_to_usbdev(a_priv->bus_interface);
 	if (a_priv) {
 		if (a_priv->bus_interface) {
 			agilent_82357a_go_idle(board);
@@ -1459,7 +1457,7 @@ static void agilent_82357a_detach(gpib_board_t *board)
 		agilent_82357a_cleanup_urbs(a_priv);
 		agilent_82357a_free_private(a_priv);
 	}
-	dev_info(&usb_dev->dev, "%s: detached\n", __func__);
+	dev_info(board->gpib_dev, "%s: detached\n", __func__);
 	mutex_unlock(&agilent_82357a_hotplug_lock);
 }
 
Signature · 1 line
2.47.1
maintainer
#2 lore raw

Re: [PATCH 1/2] staging: gpib: Fix NULL pointer dereference in detach

Quoted 32 lines · Dave Penkler

When the detach function is called after a failed attach the usb_dev initialization can cause a NULL pointer dereference. This happens when the usb device is not found in the attach procedure.

Remove the usb_dev variable and initialization and change the dev in the dev_info message from the usb_dev to the gpib_dev.

Fixes fbae7090f30c ("staging: gpib: Update messaging and usb_device refs in agilent_usb")
---
 drivers/staging/gpib/agilent_82357a/agilent_82357a.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +0 -2
index 34d85a1bdb37..2aaccebc3c7b 100644
@@ -1442,12 +1442,10 @@ static int agilent_82357a_go_idle(gpib_board_t *board)
 static void agilent_82357a_detach(gpib_board_t *board)
 {
 	struct agilent_82357a_priv *a_priv;
-	struct usb_device *usb_dev;
 
 	mutex_lock(&agilent_82357a_hotplug_lock);
 
 	a_priv = board->private_data;
-	usb_dev = interface_to_usbdev(a_priv->bus_interface);
 	if (a_priv) {
 		if (a_priv->bus_interface) {
 			agilent_82357a_go_idle(board);

drivers/staging/gpib/agilent_82357a/agilent_82357a.c static void agilent_82357a_detach(gpib_board_t *board) · line 1460

Quoted 5 lines
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +1 -1
@@ -1459,7 +1457,7 @@ static void agilent_82357a_detach(gpib_board_t *board)
 		agilent_82357a_cleanup_urbs(a_priv);
 		agilent_82357a_free_private(a_priv);
 	}
-	dev_info(&usb_dev->dev, "%s: detached\n", __func__);
+	dev_info(board->gpib_dev, "%s: detached\n", __func__);

In the future, these dev_info() lines all need to go away as when drivers work properly, they should be quiet. I'll take this fix for now as it's needed, just something to plan on for the future cleanups here.

thanks,

greg k-h

#3 lore raw

Re: [PATCH 1/2] staging: gpib: Fix NULL pointer dereference in detach

Quoted 43 lines · Greg KH
Quoted 32 lines · Dave Penkler

When the detach function is called after a failed attach the usb_dev initialization can cause a NULL pointer dereference. This happens when the usb device is not found in the attach procedure.

Remove the usb_dev variable and initialization and change the dev in the dev_info message from the usb_dev to the gpib_dev.

Fixes fbae7090f30c ("staging: gpib: Update messaging and usb_device refs in agilent_usb")
---
 drivers/staging/gpib/agilent_82357a/agilent_82357a.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +0 -2
index 34d85a1bdb37..2aaccebc3c7b 100644
@@ -1442,12 +1442,10 @@ static int agilent_82357a_go_idle(gpib_board_t *board)
 static void agilent_82357a_detach(gpib_board_t *board)
 {
 	struct agilent_82357a_priv *a_priv;
-	struct usb_device *usb_dev;
 
 	mutex_lock(&agilent_82357a_hotplug_lock);
 
 	a_priv = board->private_data;
-	usb_dev = interface_to_usbdev(a_priv->bus_interface);
 	if (a_priv) {
 		if (a_priv->bus_interface) {
 			agilent_82357a_go_idle(board);

drivers/staging/gpib/agilent_82357a/agilent_82357a.c static void agilent_82357a_detach(gpib_board_t *board) · line 1460

Quoted 5 lines
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +1 -1
@@ -1459,7 +1457,7 @@ static void agilent_82357a_detach(gpib_board_t *board)
 		agilent_82357a_cleanup_urbs(a_priv);
 		agilent_82357a_free_private(a_priv);
 	}
-	dev_info(&usb_dev->dev, "%s: detached\n", __func__);
+	dev_info(board->gpib_dev, "%s: detached\n", __func__);

In the future, these dev_info() lines all need to go away as when drivers work properly, they should be quiet. I'll take this fix for now as it's needed, just something to plan on for the future cleanups here.

Quoted 3 lines

thanks,

greg k-h

OK
thanks,
-dave

#4 lore raw

Re: [PATCH 1/2] staging: gpib: Fix NULL pointer dereference in detach

In reply to Dave Penkler
Quoted 5 lines · Dave Penkler

When the detach function is called after a failed attach the usb_dev initialization can cause a NULL pointer dereference. This happens when the usb device is not found in the attach procedure.

What you're describing is true. The ibonline() calls ->detach if the ->attach() function fails:

drivers/staging/gpib/agilent_82357a/agilent_82357a.c

   230          retval = board->interface->attach(board, &board->config);
                                           ^^^^^^
   231          if (retval < 0) {
   232                  board->interface->detach(board);
                                          ^^^^^^

233 pr_err("gpib: interface attach failed\n");
234 return retval;
235 }

But this is a class of bugs which is very typical when we free things that are not allocated. We would normally say, "don't detach things which are not attached". I would be surprised if this is the last bug caused by this particular call to ->detach().

In fact, I notice that agilent_82357a_setup_urbs() has a kfree(a_priv->interrupt_buffer) so that's a double free bug. It's better to have a system so that everyone knows exactly where to put the kfree(). Fortunately #SelfPromotion I have described such a system in my blog.

https://staticthinking.wordpress.com/2022/04/28/free-the-last-thing-style/

regards,
dan carpenter

#5 lore raw

PATCH 2/2 staging: gpib: Agilent usb code cleanup

In reply to Dave Penkler

Remove useless #ifdef RESET_USB_CONFIG code.

Change kalloc / memset to kzalloc

The attach function was not freeing the private data on error returns. Separate the releasing of urbs and private data and add a common error exit for attach failure.

Set the board private data pointer to NULL after freeing the private data.

Reduce console spam by emitting only one attach message.

Change last pr_err in attach to dev_err

---
 .../gpib/agilent_82357a/agilent_82357a.c      | 84 ++++++++-----------
 1 file changed, 36 insertions(+), 48 deletions(-)
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +36 -48
index 2aaccebc3c7b..69f0e490d401 100644
@@ -1146,25 +1146,6 @@ static int agilent_82357a_setup_urbs(gpib_board_t *board)
 	return retval;
 }
 
-#ifdef RESET_USB_CONFIG
-static int agilent_82357a_reset_usb_configuration(gpib_board_t *board)
-{
-	struct agilent_82357a_priv *a_priv = board->private_data;
-	struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
-	struct usb_device *usb_dev;
-	int retval;
-
-	if (!a_priv->bus_interface)
-		return -ENODEV;
-	usb_dev = interface_to_usbdev(a_priv->bus_interface);
-	retval = usb_reset_configuration(usb_dev);
-	if (retval)
-		dev_err(&usb_dev->dev, "%s: usb_reset_configuration() returned %i\n",
-			__func__, retval);
-	return retval;
-}
-#endif
-
 static void agilent_82357a_cleanup_urbs(struct agilent_82357a_priv *a_priv)
 {
 	if (a_priv && a_priv->bus_interface) {
@@ -1175,15 +1156,23 @@ static void agilent_82357a_cleanup_urbs(struct agilent_82357a_priv *a_priv)
 	}
 };
 
+static void agilent_82357a_release_urbs(struct agilent_82357a_priv *a_priv)
+{
+	if (a_priv) {
+		usb_free_urb(a_priv->interrupt_urb);
+		a_priv->interrupt_urb = NULL;
+		kfree(a_priv->interrupt_buffer);
+	}
+}
+
 static int agilent_82357a_allocate_private(gpib_board_t *board)
 {
 	struct agilent_82357a_priv *a_priv;
 
-	board->private_data = kmalloc(sizeof(struct agilent_82357a_priv), GFP_KERNEL);
+	board->private_data = kzalloc(sizeof(struct agilent_82357a_priv), GFP_KERNEL);
 	if (!board->private_data)
 		return -ENOMEM;
 	a_priv = board->private_data;
-	memset(a_priv, 0, sizeof(struct agilent_82357a_priv));
 	mutex_init(&a_priv->bulk_transfer_lock);
 	mutex_init(&a_priv->bulk_alloc_lock);
 	mutex_init(&a_priv->control_alloc_lock);
@@ -1191,11 +1180,11 @@ static int agilent_82357a_allocate_private(gpib_board_t *board)
 	return 0;
 }
 
-static void agilent_82357a_free_private(struct agilent_82357a_priv *a_priv)
+static void agilent_82357a_free_private(gpib_board_t *board)
 {
-	usb_free_urb(a_priv->interrupt_urb);
-	kfree(a_priv->interrupt_buffer);
-	kfree(a_priv);
+	kfree(board->private_data);
+	board->private_data = NULL;
+
 }
 
 static int agilent_82357a_init(gpib_board_t *board)
@@ -1342,16 +1331,14 @@ static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t
 			a_priv->bus_interface = agilent_82357a_driver_interfaces[i];
 			usb_set_intfdata(agilent_82357a_driver_interfaces[i], board);
 			usb_dev = interface_to_usbdev(a_priv->bus_interface);
-			dev_info(&usb_dev->dev,
-				 "bus %d dev num %d attached to gpib minor %d, agilent usb interface %i\n",
-				 usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
 			break;
 		}
 	}
 	if (i == MAX_NUM_82357A_INTERFACES) {
-		mutex_unlock(&agilent_82357a_hotplug_lock);
-		pr_err("No Agilent 82357 gpib adapters found, have you loaded its firmware?\n");
-		return -ENODEV;
+		dev_err(board->gpib_dev,
+			"No Agilent 82357 gpib adapters found, have you loaded its firmware?\n");
+		retval = -ENODEV;
+		goto attach_fail;
 	}
 	product_id = le16_to_cpu(interface_to_usbdev(a_priv->bus_interface)->descriptor.idProduct);
 	switch (product_id) {
@@ -1365,21 +1352,13 @@ static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t
 		break;
 	default:
 		dev_err(&usb_dev->dev, "bug, unhandled product_id in switch?\n");
-		mutex_unlock(&agilent_82357a_hotplug_lock);
-		return -EIO;
-	}
-#ifdef RESET_USB_CONFIG
-	retval = agilent_82357a_reset_usb_configuration(board);
-	if (retval < 0)	{
-		mutex_unlock(&agilent_82357a_hotplug_lock);
-		return retval;
+		retval = -EIO;
+		goto attach_fail;
 	}
-#endif
+
 	retval = agilent_82357a_setup_urbs(board);
-	if (retval < 0)	{
-		mutex_unlock(&agilent_82357a_hotplug_lock);
-		return retval;
-	}
+	if (retval < 0)
+		goto attach_fail;
 
 	timer_setup(&a_priv->bulk_timer, agilent_82357a_timeout_handler, 0);
 
@@ -1388,11 +1367,19 @@ static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t
 	retval = agilent_82357a_init(board);
 
 	if (retval < 0)	{
-		mutex_unlock(&agilent_82357a_hotplug_lock);
-		return retval;
+		agilent_82357a_cleanup_urbs(a_priv);
+		agilent_82357a_release_urbs(a_priv);
+		goto attach_fail;
 	}
 
-	dev_info(&usb_dev->dev, "%s: attached\n", __func__);
+	dev_info(&usb_dev->dev,
+		 "bus %d dev num %d attached to gpib minor %d, agilent usb interface %i\n",
+		 usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
+	mutex_unlock(&agilent_82357a_hotplug_lock);
+	return retval;
+
+attach_fail:
+	agilent_82357a_free_private(board);
 	mutex_unlock(&agilent_82357a_hotplug_lock);
 	return retval;
 }
@@ -1455,7 +1442,8 @@ static void agilent_82357a_detach(gpib_board_t *board)
 		mutex_lock(&a_priv->bulk_alloc_lock);
 		mutex_lock(&a_priv->interrupt_alloc_lock);
 		agilent_82357a_cleanup_urbs(a_priv);
-		agilent_82357a_free_private(a_priv);
+		agilent_82357a_release_urbs(a_priv);
+		agilent_82357a_free_private(board);
 	}
 	dev_info(board->gpib_dev, "%s: detached\n", __func__);
 	mutex_unlock(&agilent_82357a_hotplug_lock);
Signature · 1 line
2.47.1
#6 lore raw

Re: [PATCH 2/2] staging: gpib: Agilent usb code cleanup

This patch does too many things... It should be split up. People complain about this requirement a lot, but eventually it will become instinctive. I use git citool so I can highlight and click to add lines to a commit. In this code there were some dev_info() changes mixed into the unwind code in ->attach() that were hard to separate out into their own commit but it wasn't too complicated.

Quoted 2 lines · Dave Penkler

Remove useless #ifdef RESET_USB_CONFIG code.

patch 1.

Quoted 2 lines

Change kalloc / memset to kzalloc

patch 2.

Quoted 6 lines

The attach function was not freeing the private data on error returns. Separate the releasing of urbs and private data and add a common error exit for attach failure.

Set the board private data pointer to NULL after freeing the private data.

By setting the private data, this patch actually does fix the double free that I mentioned earlier. It changes the ->detach into a no-op if ->attach fails. Needs a Fixes tag. ;)

But I still hope my blog will convince you that the error handling can be re-written in a better way. It shouldn't matter if ->private_data is NULL or non-NULL because the caller should only have to handle success or failure. The caller shouldn't have to handle a dozen different failure modes:

1) Failure but the ->private_data is NULL
2) Failure but the foo->frob pointer is an error pointer
3) Failure but the foo->frob pointer needs to be freed.
4) Failure but the foo->frob pointer contains other pointers which need to be freed.
5) ...

It should just be

1) Success: Everything is allocated
2) Failure: Everything is cleaned up and any accesses are probably a use after free.

Quoted 5 lines

Reduce console spam by emitting only one attach message.

Change last pr_err in attach to dev_err

These last two can probably be combined into one patch?

drivers/staging/gpib/agilent_82357a/agilent_82357a.c static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t · line 1373

Quoted 10 lines
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +3 -2
@@ -1388,11 +1367,19 @@ static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t
 	retval = agilent_82357a_init(board);
 
 	if (retval < 0)	{
-		mutex_unlock(&agilent_82357a_hotplug_lock);
-		return retval;
+		agilent_82357a_cleanup_urbs(a_priv);
+		agilent_82357a_release_urbs(a_priv);
+		goto attach_fail;
 	}

In my blog talk about how every allocation function should have a matching free() function. These two functions match agilent_82357a_setup_urbs() so we should have a single function to release the urbs.

regards,
dan carpenter

#7 lore raw

Re: [PATCH 2/2] staging: gpib: Agilent usb code cleanup

Quoted 59 lines · Dan Carpenter

This patch does too many things... It should be split up. People complain about this requirement a lot, but eventually it will become instinctive. I use git citool so I can highlight and click to add lines to a commit. In this code there were some dev_info() changes mixed into the unwind code in ->attach() that were hard to separate out into their own commit but it wasn't too complicated.

Quoted 2 lines · Dave Penkler

Remove useless #ifdef RESET_USB_CONFIG code.

patch 1.

Quoted 2 lines

Change kalloc / memset to kzalloc

patch 2.

Quoted 6 lines

The attach function was not freeing the private data on error returns. Separate the releasing of urbs and private data and add a common error exit for attach failure.

Set the board private data pointer to NULL after freeing the private data.

By setting the private data, this patch actually does fix the double free that I mentioned earlier. It changes the ->detach into a no-op if ->attach fails. Needs a Fixes tag. ;)

But I still hope my blog will convince you that the error handling can be re-written in a better way. It shouldn't matter if ->private_data is NULL or non-NULL because the caller should only have to handle success or failure. The caller shouldn't have to handle a dozen different failure modes:

1) Failure but the ->private_data is NULL
2) Failure but the foo->frob pointer is an error pointer
3) Failure but the foo->frob pointer needs to be freed.
4) Failure but the foo->frob pointer contains other pointers which need to be freed.
5) ...

It should just be

1) Success: Everything is allocated
2) Failure: Everything is cleaned up and any accesses are probably a use after free.

Quoted 5 lines

Reduce console spam by emitting only one attach message.

Change last pr_err in attach to dev_err

These last two can probably be combined into one patch?

Quoted 3 lines
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +0 -0
@@ -1388,11 +1367,19 @@ static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t
 	retval = agilent_82357a_init(board);
 
Quoted 12 lines

drivers/staging/gpib/agilent_82357a/agilent_82357a.c static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t · line 1373

Quoted 7 lines
drivers/staging/gpib/agilent_82357a/agilent_82357a.c +3 -2
 	if (retval < 0)	{
-		mutex_unlock(&agilent_82357a_hotplug_lock);
-		return retval;
+		agilent_82357a_cleanup_urbs(a_priv);
+		agilent_82357a_release_urbs(a_priv);
+		goto attach_fail;
 	}

In my blog talk about how every allocation function should have a matching free() function. These two functions match agilent_82357a_setup_urbs() so we should have a single function to release the urbs.

Hi,
I fully agree with you and this is the direction we are pursuing in the gpib driver code base. We have very long way to go still and I apologize for not splitting up the changes into multiple patches. Thanks for the pointer to git citool.
-dave

Quoted 4 lines

regards,
dan carpenter