In nfcmrvl_probe(), the driver calls nfcmrvl_nci_register_dev(), which creates and registers the NCI device via nci_register_device(). Once registered, the device is immediately exposed to userspace, which can bring the interface up via netlink. When the interface is opened, nfcmrvl_usb_nci_open() submits bulk RX URBs. However, nfcmrvl_probe() assigns drv_data->priv = priv only after nfcmrvl_nci_register_dev() returns. If an URB completes before drv_data->priv is assigned, the completion handler nfcmrvl_bulk_complete() dereferences drv_data->priv (which is still NULL) when allocating an skb, triggering a general protection fault. Additionally, nfcmrvl_bulk_complete() and nfcmrvl_resume() checked test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags) instead of &priv->flags. Since NFCMRVL_NCI_RUNNING and NFCMRVL_USB_BULK_RUNNING both share the value 1, this inadvertently tested drv_data->flags for NFCMRVL_USB_BULK_RUNNING, masking the issue on the first URB completion while failing on subsequent completions. KASAN report: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027] RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71 Call Trace: __usb_hcd_giveback_urb+0x374/0x530 drivers/usb/core/hcd.c:1657 dummy_timer+0xa91/0x4cf0 drivers/usb/gadget/udc/dummy_hcd.c:2019 __run_hrtimer kernel/time/hrtimer.c:2032 [inline] __hrtimer_run_queues+0x3bc/0xa10 kernel/time/hrtimer.c:2096 hrtimer_run_softirq+0x17a/0x240 kernel/time/hrtimer.c:2113 handle_softirqs+0x225/0x840 kernel/softirq.c:622 Fix this by passing priv as the URB context in nfcmrvl_submit_bulk_urb() and retrieving priv directly from urb->context in nfcmrvl_bulk_complete(). Because priv is already initialized and valid when nfcmrvl_usb_nci_open() is called, this removes the reliance on drv_data->priv. Also, update nfcmrvl_bulk_complete() and nfcmrvl_resume() to test NFCMRVL_NCI_RUNNING against priv->flags. Fixes: f26e30cc6b50 ("NFC: nfcmrvl: Initial commit for Marvell NFC driver") Assisted-by: Gemini:gemini-3.7-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+59d5e6a8ed04e6a000c8@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=59d5e6a8ed04e6a000c8 Link: https://syzkaller.appspot.com/ai_job?id=cf4896f2-e0ed-48cf-888c-6677fdcd9ee8 To: "David Heidelberg" To: To: "Amitkumar Karwar" Cc: "Bartosz Golaszewski" Cc: "Johan Hovold" Cc: "Jakub Kicinski" Cc: "Linus Walleij" Cc: Cc: "Jialu Xu" --- diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index 4babde8e4..ca20030bd 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -56,26 +56,27 @@ static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) static void nfcmrvl_bulk_complete(struct urb *urb) { - struct nfcmrvl_usb_drv_data *drv_data = urb->context; + struct nfcmrvl_private *priv = urb->context; + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; int err; dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n", urb, urb->status, urb->actual_length); - if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) + if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) return; if (!urb->status) { struct sk_buff *skb; - skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, + skb = nci_skb_alloc(priv->ndev, urb->actual_length, GFP_ATOMIC); if (!skb) { nfc_err(&drv_data->udev->dev, "failed to alloc mem\n"); } else { skb_put_data(skb, urb->transfer_buffer, urb->actual_length); - if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) + if (nfcmrvl_nci_recv_frame(priv, skb) < 0) nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n"); } @@ -100,8 +101,9 @@ static void nfcmrvl_bulk_complete(struct urb *urb) } static int -nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) +nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags) { + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; struct urb *urb; unsigned char *buf; unsigned int pipe; @@ -124,7 +126,7 @@ nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) drv_data->bulk_rx_ep->bEndpointAddress); usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, - nfcmrvl_bulk_complete, drv_data); + nfcmrvl_bulk_complete, priv); urb->transfer_flags |= URB_FREE_BUFFER; @@ -174,12 +176,12 @@ static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) drv_data->intf->needs_remote_wakeup = 1; - err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + err = nfcmrvl_submit_bulk_urb(priv, GFP_KERNEL); if (err) goto failed; set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); - nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + nfcmrvl_submit_bulk_urb(priv, GFP_KERNEL); usb_autopm_put_interface(drv_data->intf); return 0; @@ -400,6 +402,7 @@ static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) static int nfcmrvl_resume(struct usb_interface *intf) { struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + struct nfcmrvl_private *priv = drv_data->priv; int err = 0; nfc_info(&drv_data->udev->dev, "intf %p\n", intf); @@ -407,17 +410,17 @@ static int nfcmrvl_resume(struct usb_interface *intf) if (--drv_data->suspend_count) return 0; - if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) + if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) goto done; if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { - err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + err = nfcmrvl_submit_bulk_urb(priv, GFP_NOIO); if (err) { clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); goto failed; } - nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + nfcmrvl_submit_bulk_urb(priv, GFP_NOIO); } spin_lock_irq(&drv_data->txlock); base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f -- This is an AI-generated patch subject to moderation. Reply with '#syz upstream' to Sign-off the patch as a human author and send it to the upstream kernel mailing lists. Reply with '#syz reject' to reject it ('#syz unreject' to undo). See https://goo.gle/syzbot-ai-patches for information about AI-generated patches. You can comment on the patch as usual, syzbot will try to address the comments and send a new version of the patch if necessary. syzbot engineers can be reached at syzkaller@googlegroups.com.