I'm using wpanusb
kernel module with kernel 4.14.112 on a Raspberry Pi to send IEEE802.15.4 frames over USB to a nrf52840 MCU running wpanusb
from samples of Zephyr OS. The kernel module I use comes from this repo : https://github.com/jadonk/wpanusb/blob/master/wpanusb.c. This module is almost a copy from atusb
mainline kernel module. As wpanusb
is outdated compared to atusb
, I've integrated 2 fixes which were missing:
- "use after free at disconnect": https://github.com/torvalds/linux/commit/7fd25e6fc035f4b04b75bca6d7e8daa069603a76
- "memory leak at probe": https://github.com/torvalds/linux/commit/6b9fbe16955152626557ec6f439f3407b7769941
Sadly, kmemleak
keeps reporting the following after each ifup/ifdown of the wpan0
interface:
unreferenced object 0x9a422f00 (size 128):
comm "kworker/0:1", pid 29, jiffies 4294939089 (age 2003.690s)
hex dump (first 32 bytes):
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 14 2f 42 9a 14 2f 42 9a 00 01 00 00 ...../B../B.....
backtrace:
[<7f1fbd28>] wpanusb_probe+0x144/0x504 [wpanusb]
[<804a7f08>] usb_probe_interface+0xd0/0x290
[<8046b1e0>] driver_probe_device+0x2a8/0x424
[<804694d0>] bus_for_each_drv+0x70/0x94
[<8046ae04>] __device_attach+0xb0/0x134
[<8046a26c>] bus_probe_device+0x84/0x8c
[<80468498>] device_add+0x368/0x5c0
[<804a5df0>] usb_set_configuration+0x488/0x9e0
[<804b0998>] generic_probe+0x4c/0x74
[<8046b1e0>] driver_probe_device+0x2a8/0x424
[<804694d0>] bus_for_each_drv+0x70/0x94
[<8046ae04>] __device_attach+0xb0/0x134
[<8046a26c>] bus_probe_device+0x84/0x8c
[<80468498>] device_add+0x368/0x5c0
[<8049be28>] usb_new_device+0x260/0x464
[<8049dcb8>] hub_event+0xf50/0x13a4
Before getting the network interface up, I perform a hard reset of the MCU on its RESET pin. This is to ensure to be able to communicate again with the MCU in case it gets stuck. If I don't perform this hard reset, the memory leak doesn't show up because disconnect() function is not called.
I provide here an extract of the functions called at probe and disconnect :
static int wpanusb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct ieee802154_hw *hw;
struct wpanusb *wpanusb;
int ret;
hw = ieee802154_alloc_hw(sizeof(struct wpanusb), &wpanusb_ops);
if (!hw)
return -ENOMEM;
wpanusb = hw->priv;
wpanusb->hw = hw;
wpanusb->udev = usb_get_dev(udev);
usb_set_intfdata(interface, wpanusb);
wpanusb->shutdown = 0;
INIT_DELAYED_WORK(&wpanusb->work, wpanusb_work_urbs);
init_usb_anchor(&wpanusb->idle_urbs);
init_usb_anchor(&wpanusb->rx_urbs);
ret = wpanusb_alloc_urbs(wpanusb, WPANUSB_NUM_RX_URBS);
if (ret)
goto fail;
wpanusb->tx_dr.bRequestType = VENDOR_OUT;
wpanusb->tx_dr.bRequest = TX;
wpanusb->tx_dr.wValue = cpu_to_le16(0);
wpanusb->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!wpanusb->tx_urb)
goto fail;
hw->parent = &udev->dev;
ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), RESET,
NULL, 0);
if (ret < 0) {
dev_err(&udev->dev, "Failed to RESET ieee802154");
goto fail;
}
ret = wpanusb_get_device_capabilities(hw);
if (ret < 0) {
dev_err(&udev->dev, "Failed to get device capabilities");
goto fail;
}
ret = wpanusb_set_extended_addr(hw);
if (ret < 0) {
dev_err(&udev->dev, "Failed to set permanent address");
goto fail;
}
ret = ieee802154_register_hw(hw);
if (ret) {
dev_err(&udev->dev, "Failed to register ieee802154");
goto fail;
}
dev_dbg(&udev->dev, "ieee802154 ready to go");
return 0;
fail:
dev_err(&udev->dev, "Failed ieee802154 probe");
wpanusb_free_urbs(wpanusb);
usb_kill_urb(wpanusb->tx_urb);
usb_free_urb(wpanusb->tx_urb);
usb_put_dev(udev);
ieee802154_free_hw(hw);
return ret;
}
static void wpanusb_disconnect(struct usb_interface *interface)
{
struct wpanusb *wpanusb = usb_get_intfdata(interface);
wpanusb->shutdown = 1;
cancel_delayed_work_sync(&wpanusb->work);
usb_kill_anchored_urbs(&wpanusb->rx_urbs);
wpanusb_free_urbs(wpanusb);
usb_kill_urb(wpanusb->tx_urb);
usb_free_urb(wpanusb->tx_urb);
ieee802154_unregister_hw(wpanusb->hw);
usb_put_dev(wpanusb->udev);
ieee802154_free_hw(wpanusb->hw);
usb_set_intfdata(interface, NULL);
}
New information 22.09.22: I've tried to put at line 709 a goto fail;
in wpanusb_probe()
to check what's happening. No memory leak seen.