Re: [PATCH] staging: rtl8712: Fix memory leak in r8712_init_recv_priv

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a syzbot warning, right?  If so, it needs a Reported-by tag.

I don't really understand why rtl871x_load_fw_cb() calls:

	usb_put_dev(udev);
	usb_set_intfdata(usb_intf, NULL);

That just seems like a layering violation and it keeps causing bugs and
I think everything would be simpler if we deleted that.  That way we
could remove both checks for if pnetdev is NULL.

[PATCH 10/x] staging: rtl8712: delete bogus clean up code in firmware callback

    This clean up is supposed to be done in r871xu_dev_remove().
    Setting the usb USB interface leads to leaks which syzbot detects.
	<stack trace>
    Reported-by: Sysbot blah blah

Patch 10 because their are some other easier patches which could be
done first.  Always do the easiest patch first in case a patch set needs
to be changed, it's easier to change the latter patches.

Your patch fixes one sysbot warning but then syzbot will complain about
something else because there are a bunch of other things which need to
be freed as well.  Of course, it's complicated to know which things need
to be freed and which not because the code is really bad.  It's better
to just re-write the cleanup code and fix everything at once.

I always encourage everyone to follow the "free the most recently
allocated resource" system of error handling.  This is the only
*systematic* way I know to do error handling.  Everything else is
ad-hoc, impossible to review and has proven bug prone in real life.

The rules are:
1) Every function cleans up it's own allocations.  Never partially
   allocate things.  Never leave the caller to clean up your resources.
2) Keep track in your head of the most recently allocated resource.
   Keeping track of just one thing is a lot easier than keeping track of
   everything.
3) Use good label names which say what the labels free.

err_free_netdev:
	free_netdev(netdev);

4) Every allocator function has a mirror free function.

How it works in real life is like this:

int my_probe(...)
{
	a = alloc();
	if (!a)
		return -ENOMEM;

	b = alloc();
	if (!b) {
		ret = -ENOMEM;
		goto free_a;  // <-- a is most recent allocation
	}

	c = alloc();
	if (!c) {
		ret = -ENOMEM;
		goto free_b;
	}

	return 0;

free_b:
	free(b);
free_a:
	free(a);

	return ret;
}

Then the mirror function basically writes its self.  You just have to
cut and paste the clean up code and add a kfree(c) to the start.

void my_release(...)
{
	free(c);
	free(b);
	free(a);
}

Once we move all the frees to the correct place and in the right order
then it becomes simpler.

drivers/staging/rtl8712/usb_intf.c
   345  static int r871xu_drv_init(struct usb_interface *pusb_intf,
   346                             const struct usb_device_id *pdid)
   347  {
   348          uint status;

Keep status for status = padapter->dvobj_init() but everything else
should be int ret.  Eventually "status" will be deleted.

   349          struct _adapter *padapter = NULL;
   350          struct dvobj_priv *pdvobjpriv;
   351          struct net_device *pnetdev;
   352          struct usb_device *udev;
   353  
   354          /* In this probe function, O.S. will provide the usb interface pointer
   355           * to driver. We have to increase the reference count of the usb device
   356           * structure by using the usb_get_dev function.
   357           */
   358          udev = interface_to_usbdev(pusb_intf);
   359          usb_get_dev(udev);
                ^^^^^^^^^^^^^^^^^
First resource allocation/thing to be freed.  Is this really required?
I'm not sure.  Anyway, it's harmless.

   360          pintf = pusb_intf;
   361          /* step 1. */

Delete all these step 1,2,3...  comments.  The authors were trying to
keep track of what they had allocated but unfortunately writing it down
did not help them.

   362          pnetdev = r8712_init_netdev();
   363          if (!pnetdev)
   364                  goto error;

	if (!pnetdev) {
		ret = -ENOMEM;
		goto put_dev;
	}

r8712_init_netdev() needs a free function.  Right now the free_netdev()
is hidden in r8712_free_drv_sw() which is the wrong place.

void r8712_free_netdev(struct net_device *dev)
{
	free_netdev(dev);
}

"pnetdev" is now our current resource.

[PATCH 6/x] staging: rtl8712: create r8712_free_netdev() function

    This is a release function for r8712_init_netdev().  I changed
    the free_netdev() in r871xu_drv_init() to use this and I moved
    the free_netdev() out of r8712_free_drv_sw() and into the
    r871xu_dev_remove() function where it belongs.

   365          padapter = netdev_priv(pnetdev);
   366          disable_ht_for_spec_devid(pdid, padapter);
   367          pdvobjpriv = &padapter->dvobjpriv;
   368          pdvobjpriv->padapter = padapter;
   369          padapter->dvobjpriv.pusbdev = udev;
   370          padapter->pusb_intf = pusb_intf;
   371          usb_set_intfdata(pusb_intf, pnetdev);
   372          SET_NETDEV_DEV(pnetdev, &pusb_intf->dev);
   373          pnetdev->dev.type = &wlan_type;
   374          /* step 2. */
   375          padapter->dvobj_init = r8712_usb_dvobj_init;
   376          padapter->dvobj_deinit = r8712_usb_dvobj_deinit;
   377          padapter->halpriv.hal_bus_init = r8712_usb_hal_bus_init;
   378          padapter->dvobjpriv.inirp_init = r8712_usb_inirp_init;
   379          padapter->dvobjpriv.inirp_deinit = r8712_usb_inirp_deinit;

These function pointers are garbage.  We should try to move this code
to calling the functions directly and delete the pointers from the
struct.

   380          /* step 3.
   381           * initialize the dvobj_priv
   382           */
   383          if (!padapter->dvobj_init) {
   384                  goto error;


We set ->dvobj_init on line 375 so it can't be NULL.  Delete this.

[PATCH 1/x] staging: rtl8712: delete impossible NULL check

   385          } else {
   386                  status = padapter->dvobj_init(padapter);
   387                  if (status != _SUCCESS)
   388                          goto error;

Since we know that ->dvobj_init is r8712_usb_dvobj_init() then lets call
that directly.

	status = r8712_usb_dvobj_init(padapter);
	if (status != _SUCCESS) {
		ret = -ENOMEM;
		goto free_netdev;
	}

[PATCH 2/x] staging: rtl8712: Get rid of ->dvobj_init/deinit function pointers

    The "padapter->dvobj_init/->dvobj_deinit" pointers are not required.
    We can call the functions directly.

This usb_dvobj (dumb name) is now our current resource.  Unfortunately
the r8712_usb_dvobj_deinit() function is an empty stub function.  It
should be:

static void r8712_usb_dvobj_deinit(struct _adapter *padapter)
{
	r8712_free_io_queue(padapter);
}

Currently the call to r8712_free_io_queue() is hidden inside the
r8712_free_drv_sw() function but that's the wrong place for it.

[PATCH 8/x] staging: rtl8712: implement r8712_usb_dvobj_deinit()

    The r8712_usb_dvobj_deinit() function is supposed to clean up from
    r8712_usb_dvobj_deinit().  Currently that is open coded in
    r8712_free_drv_sw(), but it should be implemented as a separate
    function and called from r871xu_dev_remove().

   389          }
   390          /* step 4. */
   391          status = r8712_init_drv_sw(padapter);
   392          if (status)
   393                  goto error;

	ret = r8712_init_drv_sw(padapter);
	if (ret)
		goto free_usb_dvobj;

The r8712_init_drv_sw() function does not clean up after itself on
error.  

[PATCH 3/x] staging: rtl8712: fix leaks in r8712_init_drv_sw().

The r8712_free_drv_sw() exists but it is not a mirror of the
r8712_init_drv_sw() function.  As we've noticed, it frees some things
which it should not but it also leaves timers running so presumably
that leads to a use after free.

[PATCH 9/x] staging: rtl8712: re-write r8712_free_drv_sw()

    The r8712_free_drv_sw() should clean up everything allocated in
    r8712_init_drv_sw() but it does not.  Some of it was done in
    r8712_stop_drv_timers() and so I have moved it here and deleted
    that code.

PATCH 9 is slightly risky.  Be careful not to introduce any double
frees!

   394          /* step 5. read efuse/eeprom data and get mac_addr */
   395          {
   396                  int i, offset;
   397                  u8 mac[6];
   398                  u8 tmpU1b, AutoloadFail, eeprom_CustomerID;
   399                  u8 *pdata = padapter->eeprompriv.efuse_eeprom_data;

Declare this at the top.  Pull the code in one tab.

[PATCH 4/x] staging: rtl8712: pull code in one tab

   400  
   401                  tmpU1b = r8712_read8(padapter, EE_9346CR);/*CR9346*/
   402  
   403                  /* To check system boot selection.*/
   404                  dev_info(&udev->dev, "r8712u: Boot from %s: Autoload %s\n",
   405                           (tmpU1b & _9356SEL) ? "EEPROM" : "EFUSE",
   406                           (tmpU1b & _EEPROM_EN) ? "OK" : "Failed");
   407  
   408                  /* To check autoload success or not.*/
   409                  if (tmpU1b & _EEPROM_EN) {
   410                          AutoloadFail = true;

Put the rest of this if statement after the "AutoloadFail = true;" into
a separate function.  Set AutoloadFail = false at the top of the
function:

	bool AutoloadFail = false;

[PATCH 5/x] staging: rtl8712: move code to a separate function


   411                          /* The following operations prevent Efuse leakage by
   412                           * turning on 2.5V.
   413                           */
   414                          tmpU1b = r8712_read8(padapter, EFUSE_TEST + 3);
   415                          r8712_write8(padapter, EFUSE_TEST + 3, tmpU1b | 0x80);
   416                          msleep(20);
   417                          r8712_write8(padapter, EFUSE_TEST + 3,
   418                                       (tmpU1b & (~BIT(7))));
   419  
   420                          /* Retrieve Chip version.
   421                           * Recognize IC version by Reg0x4 BIT15.
   422                           */
   423                          tmpU1b = (u8)((r8712_read32(padapter, PMC_FSM) >> 15) &
   424                                                      0x1F);
   425                          if (tmpU1b == 0x3)
   426                                  padapter->registrypriv.chip_version =
   427                                       RTL8712_3rdCUT;
   428                          else
   429                                  padapter->registrypriv.chip_version =
   429                                  padapter->registrypriv.chip_version =
   430                                       (tmpU1b >> 1) + 1;
   431                          switch (padapter->registrypriv.chip_version) {
   432                          case RTL8712_1stCUT:
   433                          case RTL8712_2ndCUT:
   434                          case RTL8712_3rdCUT:
   435                                  break;
   436                          default:
   437                                  padapter->registrypriv.chip_version =
   438                                       RTL8712_2ndCUT;
   439                                  break;
   440                          }
   441  
   442                          for (i = 0, offset = 0; i < 128; i += 8, offset++)
   443                                  r8712_efuse_pg_packet_read(padapter, offset,
   444                                                       &pdata[i]);
   445  
   446                          if (!r8712_initmac || !mac_pton(r8712_initmac, mac)) {
   447                                  /* Use the mac address stored in the Efuse
   448                                   * offset = 0x12 for usb in efuse
   449                                   */
   450                                  ether_addr_copy(mac, &pdata[0x12]);
   451                          }
   452                          eeprom_CustomerID = pdata[0x52];
   453                          switch (eeprom_CustomerID) {
   454                          case EEPROM_CID_ALPHA:
   455                                  padapter->eeprompriv.CustomerID =
   456                                                   RT_CID_819x_ALPHA;
   457                                  break;
   458                          case EEPROM_CID_CAMEO:
   459                                  padapter->eeprompriv.CustomerID =
   460                                                   RT_CID_819x_CAMEO;
   461                                  break;
   462                          case EEPROM_CID_SITECOM:
   463                                  padapter->eeprompriv.CustomerID =
   464                                                   RT_CID_819x_Sitecom;
   465                                  break;
   466                          case EEPROM_CID_COREGA:
   467                                  padapter->eeprompriv.CustomerID =
   468                                                   RT_CID_COREGA;
   469                                  break;
   470                          case EEPROM_CID_Senao:
   471                                  padapter->eeprompriv.CustomerID =
   472                                                   RT_CID_819x_Senao;
   473                                  break;
   474                          case EEPROM_CID_EDIMAX_BELKIN:
   475                                  padapter->eeprompriv.CustomerID =
   476                                                   RT_CID_819x_Edimax_Belkin;
   477                                  break;
   478                          case EEPROM_CID_SERCOMM_BELKIN:
   479                                  padapter->eeprompriv.CustomerID =
   480                                                   RT_CID_819x_Sercomm_Belkin;
   481                                  break;
   482                          case EEPROM_CID_WNC_COREGA:
   483                                  padapter->eeprompriv.CustomerID =
   484                                                   RT_CID_819x_WNC_COREGA;
   485                                  break;
   486                          case EEPROM_CID_WHQL:
   487                                  break;
   488                          case EEPROM_CID_NetCore:
   489                                  padapter->eeprompriv.CustomerID =
   490                                                   RT_CID_819x_Netcore;
   491                                  break;
   492                          case EEPROM_CID_CAMEO1:
   493                                  padapter->eeprompriv.CustomerID =
   494                                                   RT_CID_819x_CAMEO1;
   495                                  break;
   496                          case EEPROM_CID_CLEVO:
   497                                  padapter->eeprompriv.CustomerID =
   498                                                   RT_CID_819x_CLEVO;
   499                                  break;
   500                          default:
   501                                  padapter->eeprompriv.CustomerID =
   502                                                   RT_CID_DEFAULT;
   503                                  break;
   504                          }
   505                          dev_info(&udev->dev, "r8712u: CustomerID = 0x%.4x\n",
   506                                   padapter->eeprompriv.CustomerID);
   507                          /* Led mode */
   508                          switch (padapter->eeprompriv.CustomerID) {
   509                          case RT_CID_DEFAULT:
   510                          case RT_CID_819x_ALPHA:
   510                          case RT_CID_819x_ALPHA:
   511                          case RT_CID_819x_CAMEO:
   512                                  padapter->ledpriv.LedStrategy = SW_LED_MODE1;
   513                                  padapter->ledpriv.bRegUseLed = true;
   514                                  break;
   515                          case RT_CID_819x_Sitecom:
   516                                  padapter->ledpriv.LedStrategy = SW_LED_MODE2;
   517                                  padapter->ledpriv.bRegUseLed = true;
   518                                  break;
   519                          case RT_CID_COREGA:
   520                          case RT_CID_819x_Senao:
   521                                  padapter->ledpriv.LedStrategy = SW_LED_MODE3;
   522                                  padapter->ledpriv.bRegUseLed = true;
   523                                  break;
   524                          case RT_CID_819x_Edimax_Belkin:
   525                                  padapter->ledpriv.LedStrategy = SW_LED_MODE4;
   526                                  padapter->ledpriv.bRegUseLed = true;
   527                                  break;
   528                          case RT_CID_819x_Sercomm_Belkin:
   529                                  padapter->ledpriv.LedStrategy = SW_LED_MODE5;
   530                                  padapter->ledpriv.bRegUseLed = true;
   531                                  break;
   532                          case RT_CID_819x_WNC_COREGA:
   533                                  padapter->ledpriv.LedStrategy = SW_LED_MODE6;
   534                                  padapter->ledpriv.bRegUseLed = true;
   535                                  break;
   536                          default:
   537                                  padapter->ledpriv.LedStrategy = SW_LED_MODE0;
   538                                  padapter->ledpriv.bRegUseLed = false;
   539                                  break;
   540                          }
   541                  } else {
   542                          AutoloadFail = false;
   543                  }
   544                  if (((mac[0] == 0xff) && (mac[1] == 0xff) &&
   545                       (mac[2] == 0xff) && (mac[3] == 0xff) &&
   546                       (mac[4] == 0xff) && (mac[5] == 0xff)) ||
   547                      ((mac[0] == 0x00) && (mac[1] == 0x00) &&
   548                       (mac[2] == 0x00) && (mac[3] == 0x00) &&
   549                       (mac[4] == 0x00) && (mac[5] == 0x00)) ||
   550                       (!AutoloadFail)) {
   551                          mac[0] = 0x00;
   552                          mac[1] = 0xe0;
   553                          mac[2] = 0x4c;
   554                          mac[3] = 0x87;
   555                          mac[4] = 0x00;
   556                          mac[5] = 0x00;
   557                  }
   558                  if (r8712_initmac) {
   559                          /* Make sure the user did not select a multicast
   560                           * address by setting bit 1 of first octet.
   561                           */
   562                          mac[0] &= 0xFE;
   563                          dev_info(&udev->dev,
   564                                  "r8712u: MAC Address from user = %pM\n", mac);
   565                  } else {
   566                          dev_info(&udev->dev,
   567                                  "r8712u: MAC Address from efuse = %pM\n", mac);
   568                  }
   569                  ether_addr_copy(pnetdev->dev_addr, mac);
   570          }
   571          /* step 6. Load the firmware asynchronously */
   572          if (rtl871x_load_fw(padapter))
   573                  goto error;

The big indent block didn't allocate anything so our most recent
allocation is still drv_sw.

		ret = rtl871x_load_fw(padapter);
		if (ret)
			goto free_drv_sw;

   574          spin_lock_init(&padapter->lock_rx_ff0_filter);
   575          mutex_init(&padapter->mutex_start);
   576          return 0;
   577  error:
   578          usb_put_dev(udev);
   579          usb_set_intfdata(pusb_intf, NULL);
   580          if (padapter && padapter->dvobj_deinit)
   581                  padapter->dvobj_deinit(padapter);
   582          if (pnetdev)
   583                  free_netdev(pnetdev);
   584          return -ENODEV;
   585  }

dvobj_deinit() is a no-op as discussed.  This also doesn't release
the drv_sw.  So there are some leaks.  When we fix and implement the
mirrored clean up functions it becomes:

	return 0;

free_drv_sw:
	r8712_free_drv_sw(padapter);
free_usb_dvobj:
	r8712_usb_dvobj_deinit(padapter);
free_netdev:
	r8712_free_netdev(pnetdev);
put_dev:
	usb_put_dev(udev);
	usb_set_intfdata(pusb_intf, NULL);

	return ret;
}

Now we can implement a remove function.  It's complicated because
we don't know if the firmware loaded successfully and if the network
device is registered.  We don't really need to test if (adapter->fw)
becaue release_firmware(NULL) is a no-op but I did it anyway.

Based on what we know so far, this is what it should look like:

static void r871xu_dev_remove(struct usb_interface *pusb_intf)
{
	struct net_device *pnetdev = usb_get_intfdata(pusb_intf);
	struct usb_device *udev = interface_to_usbdev(pusb_intf);
	struct _adapter *padapter = netdev_priv(pnetdev);

	/* never exit with a firmware callback pending */
	wait_for_completion(&padapter->rtl8712_fw_ready);
	if (pnetdev->reg_state != NETREG_UNINITIALIZED)
		unregister_netdev(pnetdev); /* will call netdev_close() */
	if (adapter->fw)
		release_firmware(padapter->fw);
	r8712_free_drv_sw(padapter);
	r8712_usb_dvobj_deinit(padapter);
	r8712_free_netdev(pnetdev);
	usb_put_dev(udev);
	usb_set_intfdata(pusb_intf, NULL);
}

The kernel's r871xu_dev_remove() look different so there are some
remaining questions.

   585  static void r871xu_dev_remove(struct usb_interface *pusb_intf)
   586  {
   587          struct net_device *pnetdev = usb_get_intfdata(pusb_intf);
   588          struct usb_device *udev = interface_to_usbdev(pusb_intf);
   589  
   590          if (pnetdev) {

These checks are no longer required now that we deleted the two lines
from rtl871x_load_fw_cb().

   591                  struct _adapter *padapter = netdev_priv(pnetdev);
   592  
   593                  /* never exit with a firmware callback pending */
   594                  wait_for_completion(&padapter->rtl8712_fw_ready);
   595                  pnetdev = usb_get_intfdata(pusb_intf);

This assignment is not required becuse "pnetdev" remains the same.

   596                  usb_set_intfdata(pusb_intf, NULL);
   597                  if (!pnetdev)
   598                          goto firmware_load_fail;
   599                  release_firmware(padapter->fw);
   600                  if (drvpriv.drv_registered)
   601                          padapter->surprise_removed = true;

All the "padapter->surprise_removed" code is bogus, but it needs to
be audited and deleted before any other fixes to the error handling can
be done.

[PATCH 7/x] staging: rtl8712: delete padapter->surprise_removed


   602                  if (pnetdev->reg_state != NETREG_UNINITIALIZED)
   603                          unregister_netdev(pnetdev); /* will call netdev_close() */
   604                  flush_scheduled_work();
   605                  udelay(1);

This is a layering violation.  If it's really required then it needs to
be done in the correct location...

   606                  /* Stop driver mlme relation timer */
   607                  r8712_stop_drv_timers(padapter);

Once r8712_free_drv_sw() is fixed I think it will take care of the
timers.

   608                  r871x_dev_unload(padapter);

The r871x_dev_unload() stuff is suposed to be move to netdev_close(), I
think?

   609                  r8712_free_drv_sw(padapter);
   610  
   611                  /* decrease the reference count of the usb device structure
   612                   * when disconnect
   613                   */

Pointless comment.

   614                  usb_put_dev(udev);
   615          }
   616  firmware_load_fail:
   617          /* If we didn't unplug usb dongle and remove/insert module, driver
   618           * fails on sitesurvey for the first time when device is up.
   619           * Reset usb port for sitesurvey fail issue.
   620           */
   621          if (udev->state != USB_STATE_NOTATTACHED)
   622                  usb_reset_device(udev);

This feels wrong.  Also it feels like using "udev" after calling
usb_put_dev(udev) would be a use after free, but I think the stuff
with usb_get/put_dev() is not really required so it doesn't matter.

Anyway, leave this stuff.  Even though, we might not like it we can't
change it without a way to test it using real hardware.  That's the
same for the flush_scheduled_work() and the udelay(1).  We don't like it
but we can't test it.

   623  }

It would probably take a 15 patch series to fix this code.  The ordering
is important but slightly tricky so be careful with it.

regards,
dan carpenter




[Index of Archives]     [Linux Driver Development]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux