staging: gpib: Fix NULL deref and code cleanup
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
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.
--- 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
@@ -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
Greg KH
maintainerRe: [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_devinitialization can cause a NULL pointer dereference. This happens when the usb device is not found in the attach procedure.Remove the
usb_devvariable and initialization and change the dev in thedev_infomessage from theusb_devto thegpib_dev.Fixes fbae7090f30c ("staging: gpib: Update messaging and usb_device refs in agilent_usb")Signed-off-by Dave Penkler <[email protected]>--- 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@@ -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
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_devinitialization can cause a NULL pointer dereference. This happens when the usb device is not found in the attach procedure.Remove the
usb_devvariable and initialization and change the dev in thedev_infomessage from theusb_devto thegpib_dev.Fixes fbae7090f30c ("staging: gpib: Update messaging and usb_device refs in agilent_usb")Signed-off-by Dave Penkler <[email protected]>--- 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@@ -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
Re: [PATCH 1/2] staging: gpib: Fix NULL pointer dereference in detach
In reply to Dave PenklerQuoted 5 lines · Dave Penkler
When the detach function is called after a failed attach the
usb_devinitialization 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
PATCH 2/2 staging: gpib: Agilent usb code cleanup
In reply to Dave PenklerRemove 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
@@ -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_tbreak ;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
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_CONFIGcode.
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_errin attach todev_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
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 citoolso I can highlight and click to add lines to a commit. In this code there were somedev_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_CONFIGcode.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 thefoo->frobpointer is an error pointer
3) Failure but thefoo->frobpointer needs to be freed.
4) Failure but thefoo->frobpointer 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_errin attach todev_errThese 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
-2if (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 matchagilent_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