| 0/0 |
2026/08/26 14:12 |
flow |
patching |
1h10m
Results: map[AckedBy:[] Fixes:map[AuthorEmail:akarwar@marvell.com AuthorName:Amitkumar Karwar Hash:f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa Title:NFC: nfcmrvl: Initial commit for Marvell NFC driver] KernelBranch:master KernelCommit:8d3ae59288f1e7d58d76558a6ee96d533bc5019f KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:NFC: nfcmrvl_usb: fix NULL pointer dereference in bulk completion
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:
<IRQ>
__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
</IRQ>
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. PatchDiff: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);
Recipients:[map[Email:bartosz.golaszewski@oss.qualcomm.com Name:Bartosz Golaszewski To:false] map[Email:david@ixit.cz Name:David Heidelberg To:true] map[Email:johan@kernel.org Name:Johan Hovold To:false] map[Email:kuba@kernel.org Name:Jakub Kicinski To:false] map[Email:linusw@kernel.org Name:Linus Walleij To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:oe-linux-nfc@lists.linux.dev Name: To:true] map[Email:xujialu@vimux.org Name:Jialu Xu To:false] map[Email:akarwar@marvell.com Name:Amitkumar Karwar To:true]] ReportedBy:[] ReviewedBy:[] SuggestedBy:[] TestedBy:[]]
|
| 1/1 |
2026/08/26 14:12 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:8d3ae59288f1e7d58d76558a6ee96d533bc5019f KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/08/26 14:13 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:#define _GNU_SOURCE
#include <fcntl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <linux/usb/ch9.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#define UDC_NAME_LENGTH_MAX 128
struct usb_raw_init {
__u8 driver_name[UDC_NAME_LENGTH_MAX];
__u8 device_name[UDC_NAME_LENGTH_MAX];
__u8 speed;
};
enum usb_raw_event_type {
USB_RAW_EVENT_INVALID = 0,
USB_RAW_EVENT_CONNECT = 1,
USB_RAW_EVENT_CONTROL = 2,
USB_RAW_EVENT_SUSPEND = 3,
USB_RAW_EVENT_RESUME = 4,
USB_RAW_EVENT_RESET = 5,
USB_RAW_EVENT_DISCONNECT = 6,
};
struct usb_raw_event {
__u32 type;
__u32 length;
__u8 data[];
};
struct usb_raw_ep_io {
__u16 ep;
__u16 flags;
__u32 length;
__u8 data[];
};
#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
#define USB_RAW_IOCTL_RUN _IO('U', 1)
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
#define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
#define NLA_ALIGNTO 4
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
#define NLA_NEXT(na,len) ((len) -= NLA_ALIGN((na)->nla_len), \
(struct nlattr*)((char*)(na) + NLA_ALIGN((na)->nla_len)))
#define NLA_OK(na,len) ((len) >= (int)sizeof(struct nlattr) && \
(na)->nla_len >= sizeof(struct nlattr) && \
(na)->nla_len <= (len))
#define GENLMSG_DATA(glh) ((void *)((char*)NLMSG_DATA(glh) + GENL_HDRLEN))
void pin_to_cpu(int cpu) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) < 0) {
printf("[-] Failed to sched_setaffinity: %s\n", strerror(errno));
}
}
struct usb_device_descriptor dev_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0xFF,
.bDeviceSubClass = 4,
.bDeviceProtocol = 1,
.bMaxPacketSize0 = 64,
.idVendor = 0x1286,
.idProduct = 0x2046,
.bcdDevice = 0x0100,
.iManufacturer = 0,
.iProduct = 0,
.iSerialNumber = 0,
.bNumConfigurations = 1,
};
struct usb_config_descriptor conf_desc = {
.bLength = USB_DT_CONFIG_SIZE,
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + 2 * USB_DT_ENDPOINT_SIZE,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CONFIG_ATT_ONE,
.bMaxPower = 50,
};
struct usb_interface_descriptor intf_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 4,
.bInterfaceProtocol = 1,
.iInterface = 0,
};
struct usb_endpoint_descriptor ep1_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN | 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0,
};
struct usb_endpoint_descriptor ep2_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT | 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0,
};
uint8_t config_buf[256];
void build_config() {
uint8_t *p = config_buf;
memcpy(p, &conf_desc, USB_DT_CONFIG_SIZE); p += USB_DT_CONFIG_SIZE;
memcpy(p, &intf_desc, USB_DT_INTERFACE_SIZE); p += USB_DT_INTERFACE_SIZE;
memcpy(p, &ep1_desc, USB_DT_ENDPOINT_SIZE); p += USB_DT_ENDPOINT_SIZE;
memcpy(p, &ep2_desc, USB_DT_ENDPOINT_SIZE); p += USB_DT_ENDPOINT_SIZE;
}
int current_fd = -1;
pthread_mutex_t fd_lock = PTHREAD_MUTEX_INITIALIZER;
volatile int stop_threads = 0;
volatile int usb_configured = 0;
void *ep_write_thread(void *arg) {
int ep1 = (int)(intptr_t)arg;
while (!stop_threads) {
pthread_mutex_lock(&fd_lock);
int fd = current_fd;
pthread_mutex_unlock(&fd_lock);
if (fd < 0) break;
struct usb_raw_ep_io *io = malloc(sizeof(*io) + 64);
if (!io) break;
io->ep = ep1;
io->flags = 0;
io->length = 0; // 0-byte completion
if (ioctl(fd, USB_RAW_IOCTL_EP_WRITE, io) < 0) {
free(io);
break;
}
free(io);
}
return NULL;
}
void *usb_ep0_thread(void *arg) {
int local_fd = (int)(intptr_t)arg;
while (!stop_threads) {
struct usb_raw_event *event = malloc(sizeof(*event) + 1024);
if (!event) break;
event->length = 1024;
if (ioctl(local_fd, USB_RAW_IOCTL_EVENT_FETCH, event) < 0) {
free(event);
break;
}
if (event->type == USB_RAW_EVENT_CONTROL) {
struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event->data;
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
uint8_t desc_type = ctrl->wValue >> 8;
if (desc_type == USB_DT_DEVICE) {
struct usb_raw_ep_io *io = malloc(sizeof(*io) + sizeof(dev_desc));
io->ep = 0; io->flags = 0; io->length = sizeof(dev_desc);
if (io->length > ctrl->wLength) io->length = ctrl->wLength;
memcpy(io->data, &dev_desc, io->length);
ioctl(local_fd, USB_RAW_IOCTL_EP0_WRITE, io);
free(io);
} else if (desc_type == USB_DT_CONFIG) {
struct usb_raw_ep_io *io = malloc(sizeof(*io) + conf_desc.wTotalLength);
io->ep = 0; io->flags = 0; io->length = conf_desc.wTotalLength;
if (io->length > ctrl->wLength) io->length = ctrl->wLength;
memcpy(io->data, config_buf, io->length);
ioctl(local_fd, USB_RAW_IOCTL_EP0_WRITE, io);
free(io);
} else {
ioctl(local_fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
} else if (ctrl->bRequest == USB_REQ_SET_CONFIGURATION) {
ioctl(local_fd, USB_RAW_IOCTL_CONFIGURE, 0);
int ep1_handle = ioctl(local_fd, USB_RAW_IOCTL_EP_ENABLE, &ep1_desc);
int ep2_handle = ioctl(local_fd, USB_RAW_IOCTL_EP_ENABLE, &ep2_desc);
struct usb_raw_ep_io *io = malloc(sizeof(*io));
io->ep = 0; io->flags = 0; io->length = 0;
ioctl(local_fd, USB_RAW_IOCTL_EP0_READ, io);
free(io);
pthread_mutex_lock(&fd_lock);
current_fd = local_fd;
pthread_mutex_unlock(&fd_lock);
usb_configured = 1;
pthread_t th;
pthread_create(&th, NULL, ep_write_thread, (void *)(intptr_t)ep1_handle);
pthread_detach(th);
} else if (ctrl->bRequest == USB_REQ_SET_INTERFACE) {
struct usb_raw_ep_io *io = malloc(sizeof(*io));
io->ep = 0; io->flags = 0; io->length = 0;
ioctl(local_fd, USB_RAW_IOCTL_EP0_READ, io);
free(io);
} else {
ioctl(local_fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
} else {
ioctl(local_fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
}
free(event);
}
return NULL;
}
void *usb_thread(void *arg) {
pin_to_cpu(0);
build_config();
int local_fd = open("/dev/raw-gadget", O_RDWR);
if (local_fd < 0) {
printf("[-] Failed to open /dev/raw-gadget: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened /dev/raw-gadget\n");
struct usb_raw_init init = {
.driver_name = "dummy_udc",
.device_name = "dummy_udc.0",
.speed = USB_SPEED_HIGH,
};
if (ioctl(local_fd, USB_RAW_IOCTL_INIT, &init) < 0) {
printf("[-] Failed to USB_RAW_IOCTL_INIT: %s\n", strerror(errno));
exit(1);
}
printf("[+] USB_RAW_IOCTL_INIT successful\n");
if (ioctl(local_fd, USB_RAW_IOCTL_RUN, 0) < 0) {
printf("[-] Failed to USB_RAW_IOCTL_RUN: %s\n", strerror(errno));
exit(1);
}
printf("[+] USB_RAW_IOCTL_RUN successful\n");
pthread_t ep0_th;
pthread_create(&ep0_th, NULL, usb_ep0_thread, (void *)(intptr_t)local_fd);
sleep(3); // Let it run for 3 seconds to trigger the race
stop_threads = 1;
pthread_mutex_lock(&fd_lock);
current_fd = -1;
pthread_mutex_unlock(&fd_lock);
close(local_fd);
pthread_join(ep0_th, NULL);
return NULL;
}
int resolve_family(int fd, const char *name) {
struct {
struct nlmsghdr n;
struct genlmsghdr g;
char buf[256];
} req;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg;
struct nlattr *na;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
req.n.nlmsg_type = GENL_ID_CTRL;
req.n.nlmsg_flags = NLM_F_REQUEST;
req.g.cmd = CTRL_CMD_GETFAMILY;
req.g.version = 1;
na = (struct nlattr *) GENLMSG_DATA(&req);
na->nla_type = CTRL_ATTR_FAMILY_NAME;
na->nla_len = strlen(name) + 1 + NLA_HDRLEN;
strcpy((char *) NLA_DATA(na), name);
req.n.nlmsg_len += NLA_ALIGN(na->nla_len);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
iov.iov_base = &req;
iov.iov_len = req.n.nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (sendmsg(fd, &msg, 0) < 0) return -1;
char buf[4096];
int len = recv(fd, buf, sizeof(buf), 0);
if (len < 0) return -1;
struct nlmsghdr *h = (struct nlmsghdr *) buf;
if (h->nlmsg_type == NLMSG_ERROR) return -1;
struct genlmsghdr *gh = (struct genlmsghdr *) NLMSG_DATA(h);
struct nlattr *tb[CTRL_ATTR_MAX + 1];
memset(tb, 0, sizeof(tb));
struct nlattr *attr = (struct nlattr *) ((char *) gh + GENL_HDRLEN);
int rem = h->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
while (NLA_OK(attr, rem)) {
if (attr->nla_type <= CTRL_ATTR_MAX)
tb[attr->nla_type] = attr;
attr = NLA_NEXT(attr, rem);
}
if (tb[CTRL_ATTR_FAMILY_ID])
return *(uint16_t *) NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
return -1;
}
void send_dev_up(int fd, int family_id, uint32_t dev_idx) {
struct {
struct nlmsghdr n;
struct genlmsghdr g;
char buf[256];
} req;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg;
struct nlattr *na;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
req.n.nlmsg_type = family_id;
req.n.nlmsg_flags = NLM_F_REQUEST;
req.g.cmd = 2; // NFC_CMD_DEV_UP
req.g.version = 1;
na = (struct nlattr *) GENLMSG_DATA(&req);
na->nla_type = 1; // NFC_ATTR_DEVICE_INDEX
na->nla_len = sizeof(uint32_t) + NLA_HDRLEN;
*(uint32_t *) NLA_DATA(na) = dev_idx;
req.n.nlmsg_len += NLA_ALIGN(na->nla_len);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
iov.iov_base = &req;
iov.iov_len = req.n.nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sendmsg(fd, &msg, 0);
}
void *netlink_thread(void *arg) {
int cpu = (int)(intptr_t)arg;
pin_to_cpu(cpu);
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0) {
printf("[-] Failed to create netlink socket: %s\n", strerror(errno));
return NULL;
}
int family_id = -1;
while (family_id < 0 && !stop_threads) {
family_id = resolve_family(fd, "nfc");
usleep(100000);
}
if (family_id >= 0) {
printf("[+] Resolved nfc family id: %d\n", family_id);
}
while (!usb_configured && !stop_threads) {
usleep(10000);
}
while (!stop_threads) {
for (int i = 0; i < 16; i++) {
send_dev_up(fd, family_id, i);
}
}
close(fd);
return NULL;
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
pthread_t t1, t2, t3, t4;
pthread_create(&t1, NULL, usb_thread, NULL);
pthread_create(&t2, NULL, netlink_thread, (void *)1);
pthread_create(&t3, NULL, netlink_thread, (void *)2);
pthread_create(&t4, NULL, netlink_thread, (void *)3);
pthread_join(t1, NULL);
stop_threads = 1;
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
printf("[+] Done\n");
return 0;
}
]
|
| 3/1 |
2026/08/26 14:13 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/b5692495be56e63dd0879dd861d23f69899ae44d]
|
| 4/1 |
2026/08/26 14:13 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/1c4c6cd51b8dc54cb91defbe8b0a37466fffae8d]
|
| 5/1 |
2026/08/26 14:13 |
action |
crash-reproducer |
0m
Results: map[OtherCrashReports:<nil> ReproducedBugTitle:general protection fault in nfcmrvl_bulk_complete ReproducedCrashReport:Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
ReproducedFaultInjection:]
|
| 6/1 |
2026/08/26 14:13 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/08/26 14:14 |
agent |
debugger |
3m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The following C code is a draft of the vulnerable syscall sequence. Keep in mind that
it may lack the precise threading, sandboxing, and some arguments of a working reproducer:
#define _GNU_SOURCE
#include <fcntl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <linux/usb/ch9.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#define UDC_NAME_LENGTH_MAX 128
struct usb_raw_init {
__u8 driver_name[UDC_NAME_LENGTH_MAX];
__u8 device_name[UDC_NAME_LENGTH_MAX];
__u8 speed;
};
enum usb_raw_event_type {
USB_RAW_EVENT_INVALID = 0,
USB_RAW_EVENT_CONNECT = 1,
USB_RAW_EVENT_CONTROL = 2,
USB_RAW_EVENT_SUSPEND = 3,
USB_RAW_EVENT_RESUME = 4,
USB_RAW_EVENT_RESET = 5,
USB_RAW_EVENT_DISCONNECT = 6,
};
struct usb_raw_event {
__u32 type;
__u32 length;
__u8 data[];
};
struct usb_raw_ep_io {
__u16 ep;
__u16 flags;
__u32 length;
__u8 data[];
};
#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
#define USB_RAW_IOCTL_RUN _IO('U', 1)
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
#define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
#define NLA_ALIGNTO 4
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
#define NLA_NEXT(na,len) ((len) -= NLA_ALIGN((na)->nla_len), \
(struct nlattr*)((char*)(na) + NLA_ALIGN((na)->nla_len)))
#define NLA_OK(na,len) ((len) >= (int)sizeof(struct nlattr) && \
(na)->nla_len >= sizeof(struct nlattr) && \
(na)->nla_len <= (len))
#define GENLMSG_DATA(glh) ((void *)((char*)NLMSG_DATA(glh) + GENL_HDRLEN))
void pin_to_cpu(int cpu) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) < 0) {
printf("[-] Failed to sched_setaffinity: %s\n", strerror(errno));
}
}
struct usb_device_descriptor dev_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0xFF,
.bDeviceSubClass = 4,
.bDeviceProtocol = 1,
.bMaxPacketSize0 = 64,
.idVendor = 0x1286,
.idProduct = 0x2046,
.bcdDevice = 0x0100,
.iManufacturer = 0,
.iProduct = 0,
.iSerialNumber = 0,
.bNumConfigurations = 1,
};
struct usb_config_descriptor conf_desc = {
.bLength = USB_DT_CONFIG_SIZE,
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + 2 * USB_DT_ENDPOINT_SIZE,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CONFIG_ATT_ONE,
.bMaxPower = 50,
};
struct usb_interface_descriptor intf_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 4,
.bInterfaceProtocol = 1,
.iInterface = 0,
};
struct usb_endpoint_descriptor ep1_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN | 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0,
};
struct usb_endpoint_descriptor ep2_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT | 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0,
};
uint8_t config_buf[256];
void build_config() {
uint8_t *p = config_buf;
memcpy(p, &conf_desc, USB_DT_CONFIG_SIZE); p += USB_DT_CONFIG_SIZE;
memcpy(p, &intf_desc, USB_DT_INTERFACE_SIZE); p += USB_DT_INTERFACE_SIZE;
memcpy(p, &ep1_desc, USB_DT_ENDPOINT_SIZE); p += USB_DT_ENDPOINT_SIZE;
memcpy(p, &ep2_desc, USB_DT_ENDPOINT_SIZE); p += USB_DT_ENDPOINT_SIZE;
}
int current_fd = -1;
pthread_mutex_t fd_lock = PTHREAD_MUTEX_INITIALIZER;
volatile int stop_threads = 0;
volatile int usb_configured = 0;
void *ep_write_thread(void *arg) {
int ep1 = (int)(intptr_t)arg;
while (!stop_threads) {
pthread_mutex_lock(&fd_lock);
int fd = current_fd;
pthread_mutex_unlock(&fd_lock);
if (fd < 0) break;
struct usb_raw_ep_io *io = malloc(sizeof(*io) + 64);
if (!io) break;
io->ep = ep1;
io->flags = 0;
io->length = 0; // 0-byte completion
if (ioctl(fd, USB_RAW_IOCTL_EP_WRITE, io) < 0) {
free(io);
break;
}
free(io);
}
return NULL;
}
void *usb_ep0_thread(void *arg) {
int local_fd = (int)(intptr_t)arg;
while (!stop_threads) {
struct usb_raw_event *event = malloc(sizeof(*event) + 1024);
if (!event) break;
event->length = 1024;
if (ioctl(local_fd, USB_RAW_IOCTL_EVENT_FETCH, event) < 0) {
free(event);
break;
}
if (event->type == USB_RAW_EVENT_CONTROL) {
struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event->data;
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
uint8_t desc_type = ctrl->wValue >> 8;
if (desc_type == USB_DT_DEVICE) {
struct usb_raw_ep_io *io = malloc(sizeof(*io) + sizeof(dev_desc));
io->ep = 0; io->flags = 0; io->length = sizeof(dev_desc);
if (io->length > ctrl->wLength) io->length = ctrl->wLength;
memcpy(io->data, &dev_desc, io->length);
ioctl(local_fd, USB_RAW_IOCTL_EP0_WRITE, io);
free(io);
} else if (desc_type == USB_DT_CONFIG) {
struct usb_raw_ep_io *io = malloc(sizeof(*io) + conf_desc.wTotalLength);
io->ep = 0; io->flags = 0; io->length = conf_desc.wTotalLength;
if (io->length > ctrl->wLength) io->length = ctrl->wLength;
memcpy(io->data, config_buf, io->length);
ioctl(local_fd, USB_RAW_IOCTL_EP0_WRITE, io);
free(io);
} else {
ioctl(local_fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
} else if (ctrl->bRequest == USB_REQ_SET_CONFIGURATION) {
ioctl(local_fd, USB_RAW_IOCTL_CONFIGURE, 0);
int ep1_handle = ioctl(local_fd, USB_RAW_IOCTL_EP_ENABLE, &ep1_desc);
int ep2_handle = ioctl(local_fd, USB_RAW_IOCTL_EP_ENABLE, &ep2_desc);
struct usb_raw_ep_io *io = malloc(sizeof(*io));
io->ep = 0; io->flags = 0; io->length = 0;
ioctl(local_fd, USB_RAW_IOCTL_EP0_READ, io);
free(io);
pthread_mutex_lock(&fd_lock);
current_fd = local_fd;
pthread_mutex_unlock(&fd_lock);
usb_configured = 1;
pthread_t th;
pthread_create(&th, NULL, ep_write_thread, (void *)(intptr_t)ep1_handle);
pthread_detach(th);
} else if (ctrl->bRequest == USB_REQ_SET_INTERFACE) {
struct usb_raw_ep_io *io = malloc(sizeof(*io));
io->ep = 0; io->flags = 0; io->length = 0;
ioctl(local_fd, USB_RAW_IOCTL_EP0_READ, io);
free(io);
} else {
ioctl(local_fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
} else {
ioctl(local_fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
}
free(event);
}
return NULL;
}
void *usb_thread(void *arg) {
pin_to_cpu(0);
build_config();
int local_fd = open("/dev/raw-gadget", O_RDWR);
if (local_fd < 0) {
printf("[-] Failed to open /dev/raw-gadget: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened /dev/raw-gadget\n");
struct usb_raw_init init = {
.driver_name = "dummy_udc",
.device_name = "dummy_udc.0",
.speed = USB_SPEED_HIGH,
};
if (ioctl(local_fd, USB_RAW_IOCTL_INIT, &init) < 0) {
printf("[-] Failed to USB_RAW_IOCTL_INIT: %s\n", strerror(errno));
exit(1);
}
printf("[+] USB_RAW_IOCTL_INIT successful\n");
if (ioctl(local_fd, USB_RAW_IOCTL_RUN, 0) < 0) {
printf("[-] Failed to USB_RAW_IOCTL_RUN: %s\n", strerror(errno));
exit(1);
}
printf("[+] USB_RAW_IOCTL_RUN successful\n");
pthread_t ep0_th;
pthread_create(&ep0_th, NULL, usb_ep0_thread, (void *)(intptr_t)local_fd);
sleep(3); // Let it run for 3 seconds to trigger the race
stop_threads = 1;
pthread_mutex_lock(&fd_lock);
current_fd = -1;
pthread_mutex_unlock(&fd_lock);
close(local_fd);
pthread_join(ep0_th, NULL);
return NULL;
}
int resolve_family(int fd, const char *name) {
struct {
struct nlmsghdr n;
struct genlmsghdr g;
char buf[256];
} req;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg;
struct nlattr *na;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
req.n.nlmsg_type = GENL_ID_CTRL;
req.n.nlmsg_flags = NLM_F_REQUEST;
req.g.cmd = CTRL_CMD_GETFAMILY;
req.g.version = 1;
na = (struct nlattr *) GENLMSG_DATA(&req);
na->nla_type = CTRL_ATTR_FAMILY_NAME;
na->nla_len = strlen(name) + 1 + NLA_HDRLEN;
strcpy((char *) NLA_DATA(na), name);
req.n.nlmsg_len += NLA_ALIGN(na->nla_len);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
iov.iov_base = &req;
iov.iov_len = req.n.nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (sendmsg(fd, &msg, 0) < 0) return -1;
char buf[4096];
int len = recv(fd, buf, sizeof(buf), 0);
if (len < 0) return -1;
struct nlmsghdr *h = (struct nlmsghdr *) buf;
if (h->nlmsg_type == NLMSG_ERROR) return -1;
struct genlmsghdr *gh = (struct genlmsghdr *) NLMSG_DATA(h);
struct nlattr *tb[CTRL_ATTR_MAX + 1];
memset(tb, 0, sizeof(tb));
struct nlattr *attr = (struct nlattr *) ((char *) gh + GENL_HDRLEN);
int rem = h->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
while (NLA_OK(attr, rem)) {
if (attr->nla_type <= CTRL_ATTR_MAX)
tb[attr->nla_type] = attr;
attr = NLA_NEXT(attr, rem);
}
if (tb[CTRL_ATTR_FAMILY_ID])
return *(uint16_t *) NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
return -1;
}
void send_dev_up(int fd, int family_id, uint32_t dev_idx) {
struct {
struct nlmsghdr n;
struct genlmsghdr g;
char buf[256];
} req;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg;
struct nlattr *na;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
req.n.nlmsg_type = family_id;
req.n.nlmsg_flags = NLM_F_REQUEST;
req.g.cmd = 2; // NFC_CMD_DEV_UP
req.g.version = 1;
na = (struct nlattr *) GENLMSG_DATA(&req);
na->nla_type = 1; // NFC_ATTR_DEVICE_INDEX
na->nla_len = sizeof(uint32_t) + NLA_HDRLEN;
*(uint32_t *) NLA_DATA(na) = dev_idx;
req.n.nlmsg_len += NLA_ALIGN(na->nla_len);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
iov.iov_base = &req;
iov.iov_len = req.n.nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sendmsg(fd, &msg, 0);
}
void *netlink_thread(void *arg) {
int cpu = (int)(intptr_t)arg;
pin_to_cpu(cpu);
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0) {
printf("[-] Failed to create netlink socket: %s\n", strerror(errno));
return NULL;
}
int family_id = -1;
while (family_id < 0 && !stop_threads) {
family_id = resolve_family(fd, "nfc");
usleep(100000);
}
if (family_id >= 0) {
printf("[+] Resolved nfc family id: %d\n", family_id);
}
while (!usb_configured && !stop_threads) {
usleep(10000);
}
while (!stop_threads) {
for (int i = 0; i < 16; i++) {
send_dev_up(fd, family_id, i);
}
}
close(fd);
return NULL;
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
pthread_t t1, t2, t3, t4;
pthread_create(&t1, NULL, usb_thread, NULL);
pthread_create(&t2, NULL, netlink_thread, (void *)1);
pthread_create(&t3, NULL, netlink_thread, (void *)2);
pthread_create(&t4, NULL, netlink_thread, (void *)3);
pthread_join(t1, NULL);
stop_threads = 1;
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
printf("[+] Done\n");
return 0;
}
Reply: The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
|
| 8/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 13811
output: 40
thoughts: 546
Thoughts: **Analyzing a Nasty NFC Driver Crash**
Okay, so I've got a general protection fault, a classic null pointer dereference, right in `nfcmrvl_bulk_complete`. The crash is happening in the `nfcmrvl` NFC driver, specifically in `drivers/nfc/nfcmrvl/usb.c` at line 71. The culprit is a NULL pointer or a pointer very close to NULL (0x20-0x27) – classic memory access violation territory.
The stack trace reveals the completion handler `nfcmrvl_bulk_complete` is where the crash originates. It gets called from `__usb_hcd_giveback_urb`, which, in turn, is called by `dummy_timer`, likely as part of the USB completion process. Nothing too surprising so far.
Now, here's where it gets interesting. I see another thread concurrently allocating a URB and submitting it: `usb_alloc_urb`, `nfcmrvl_submit_bulk_urb`, and all that. It's calling the open function through various layers - `nfcmrvl_usb_nci_open`, `nfcmrvl_nci_open`, etc. Wait a minute...the completion handler is firing *while* another thread is submitting new URBs? Or is the completion handler running for an URB that *just* got submitted but something is not initialized? This sounds like a classic race condition or initialization problem, doesn't it?
Time to dig into the code. I need to take a look at `drivers/nfc/nfcmrvl/usb.c` and specifically around line 71 to see what's happening there. I suspect a synchronization issue or some missing initialization flag is playing a part in this mess. Let's see if we can get to the bottom of this.
|
| 9/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:50 LineCount:50]
Results: map[Contents: 50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
]
|
| 10/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 15553
output: 32
thoughts: 136
Thoughts: **Debugging a Kernel Panic**
Okay, so I've got a crash, and it's pinpointed to line 71: `skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);`. The error seems to be happening when trying to allocate a socket buffer. My immediate thought is to check the validity of the variables being accessed.
Specifically, I need to look at `drv_data->priv->ndev`. Let's see... `drv_data` is actually `urb->context`. That's important to remember. If `drv_data` itself isn't the problem, then maybe `drv_data->priv` is NULL? Or even worse, perhaps `drv_data->priv` is valid, but the `ndev` pointer within it is NULL? I need to know.
To figure this out definitively, I need to check the definitions of the structures involved. I'm going to look at `struct nfcmrvl_usb_drv_data` and `struct nfcmrvl_private`. I need to review these structures to understand their members and potential for invalid pointers. Then, I can trace back where these pointers are initialized and verify their validity at the time of the crash.
|
| 11/2 |
2026/08/26 14:14 |
tool |
codesearch-file-index |
0m
Args: map[SourceFile:drivers/nfc/nfcmrvl/usb.c]
Results: map[Entities:[map[Kind:function Name:nfcmrvl_bulk_complete] map[Kind:function Name:nfcmrvl_disconnect] map[Kind:function Name:nfcmrvl_inc_tx] map[Kind:function Name:nfcmrvl_play_deferred] map[Kind:function Name:nfcmrvl_probe] map[Kind:function Name:nfcmrvl_resume] map[Kind:function Name:nfcmrvl_submit_bulk_urb] map[Kind:function Name:nfcmrvl_suspend] map[Kind:function Name:nfcmrvl_tx_complete] map[Kind:function Name:nfcmrvl_usb_driver_exit] map[Kind:function Name:nfcmrvl_usb_driver_init] map[Kind:function Name:nfcmrvl_usb_nci_close] map[Kind:function Name:nfcmrvl_usb_nci_open] map[Kind:function Name:nfcmrvl_usb_nci_send] map[Kind:function Name:nfcmrvl_usb_stop_traffic] map[Kind:function Name:nfcmrvl_waker] map[Kind:struct Name:nfcmrvl_usb_drv_data] map[Kind:global_variable Name:__UNIQUE_ID_addressable_nfcmrvl_usb_driver_init_1018] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1019] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1020] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1021] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1022] map[Kind:global_variable Name:__exitcall_nfcmrvl_usb_driver_exit] map[Kind:global_variable Name:__mod_device_table__kmod_nfcmrvl_usb__usb__nfcmrvl_table] map[Kind:global_variable Name:nfcmrvl_table] map[Kind:global_variable Name:nfcmrvl_usb_driver] map[Kind:global_variable Name:usb_ops]]]
|
| 12/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 16602
output: 47
thoughts: 31
Thoughts: Okay, here's my interpretation of those thoughts, presented as a first-person expert summary:
**Diving into the nfcmrvl USB Driver**
Alright, time to get a handle on this `nfcmrvl_usb_drv_data` struct and the `nfcmrvl_usb_nci_open` function. Given my experience, I'm expecting `nfcmrvl_usb_drv_data` to be the central data structure holding all the critical context for this USB-based NFC Marvell driver. It'll probably contain the usual suspects: pointers to the USB device structure, endpoint information (IN/OUT pipes), perhaps some state flags indicating initialization status, and potentially buffers for DMA transfers. I'll need to see how they manage interrupt handling within this struct; are they using URBs or something more streamlined?
Then, the `nfcmrvl_usb_nci_open` function. This is most likely the entry point when a user-space application attempts to interface with the NFC controller via the USB port. My guess is it's responsible for:
* **Device Probe and Matching:** Verifying that the USB device attached is indeed a Marvell NFC device. This would likely involve checking vendor/product IDs or perhaps some other unique identifier.
* **Resource Allocation:** Allocating any necessary memory for the driver data structure (`nfcmrvl_usb_drv_data` in this case) and the USB interface.
* **Endpoint Configuration:** Identifying and configuring the USB endpoints used for communication with the NFC controller (control, bulk in, bulk out, interrupt). This might involve setting up URBs for data transfers.
* **Initialization:** Performing any necessary initialization steps on the NFC controller itself – maybe sending a reset command or setting up initial registers.
* **File Operations setup:** Setting up the file operations to allow for communication with user space.
I'll be looking to see how they handle error conditions in `nfcmrvl_usb_nci_open`. Do they return standard error codes, and how robust is the error handling? I will need to understand the sequencing and synchronization within the open routine. Are they protecting the driver data struct with any kind of locking mechanism?
My goal is to understand how this driver interfaces with the underlying USB hardware and, subsequently, how it provides a clean and efficient API for higher-level NFC functionality. Time to start digging!
|
| 13/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c Name:nfcmrvl_usb_drv_data]
Results: map[SourceCode: 26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
30: struct work_struct waker;
31: struct usb_anchor tx_anchor;
32: struct usb_anchor bulk_anchor;
33: struct usb_anchor deferred;
34: int tx_in_flight;
35: /* protects tx_in_flight */
36: spinlock_t txlock;
37: struct usb_endpoint_descriptor *bulk_tx_ep;
38: struct usb_endpoint_descriptor *bulk_rx_ep;
39: int suspend_count;
40: struct nfcmrvl_private *priv;
41: };
SourceFile:drivers/nfc/nfcmrvl/usb.c]
|
| 14/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 17445
output: 48
thoughts: 17
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, and rendered in the first person:
**Diving into `nfcmrvl_usb_nci_open`**
Alright, time to get to it. "Let's check `nfcmrvl_usb_nci_open`." That's the command, the entry point I'm focusing on. It's the starting point, the hook, the function I need to understand at this juncture. I'm going to quickly refresh myself on the context. Why am I looking at this specific function now? What's the bigger picture of the issue I'm trying to resolve? I need to review what calls it, and what it calls. Understanding the control flow around that function will give me crucial clues for debugging. It's probably related to USB and NFC interaction, so I'll keep that perspective front and center. I suspect there might be issues with initialization, resource allocation, or even just general error handling within this function. Time to dig in!
|
| 15/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c Name:nfcmrvl_usb_nci_open]
Results: map[SourceCode: 166: static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
167: {
168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
169: int err;
170:
171: err = usb_autopm_get_interface(drv_data->intf);
172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
188: usb_autopm_put_interface(drv_data->intf);
189: return err;
190: }
SourceFile:drivers/nfc/nfcmrvl/usb.c]
|
| 16/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 18152
output: 47
thoughts: 35
Thoughts: Here's my take on the thought process, as requested:
**Investigating a Double Call**
Okay, hold on a second. I'm seeing `nfcmrvl_submit_bulk_urb` get called *twice*. That immediately raises a flag. We need to figure out why this is happening. Something isn't right if we're submitting the same bulk URB twice in such a rapid succession, especially in a performance critical section like this. Let's dig into the implementation of `nfcmrvl_submit_bulk_urb` and see what's going on. I need to understand its behavior and what upstream side-effects are. This is likely the source of a subtle, but impactful, bug, or a costly inefficiency. I'll need to trace its call stack and pinpoint the code paths leading to this redundant execution.
|
| 17/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c Name:nfcmrvl_submit_bulk_urb]
Results: map[SourceCode: 102: static int
103: nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
104: {
105: struct urb *urb;
106: unsigned char *buf;
107: unsigned int pipe;
108: int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
109:
110: if (!drv_data->bulk_rx_ep)
111: return -ENODEV;
112:
113: urb = usb_alloc_urb(0, mem_flags);
114: if (!urb)
115: return -ENOMEM;
116:
117: buf = kmalloc(size, mem_flags);
118: if (!buf) {
119: usb_free_urb(urb);
120: return -ENOMEM;
121: }
122:
123: pipe = usb_rcvbulkpipe(drv_data->udev,
124: drv_data->bulk_rx_ep->bEndpointAddress);
125:
126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
127: nfcmrvl_bulk_complete, drv_data);
128:
129: urb->transfer_flags |= URB_FREE_BUFFER;
130:
131: usb_mark_last_busy(drv_data->udev);
132: usb_anchor_urb(urb, &drv_data->bulk_anchor);
133:
134: err = usb_submit_urb(urb, mem_flags);
135: if (err) {
136: if (err != -EPERM && err != -ENODEV)
137: nfc_err(&drv_data->udev->dev,
138: "urb %p submission failed (%d)\n", urb, -err);
139: usb_unanchor_urb(urb);
140: }
141:
142: usb_free_urb(urb);
143:
144: return err;
145: }
SourceFile:drivers/nfc/nfcmrvl/usb.c]
|
| 18/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 19098
output: 46
thoughts: 210
Thoughts: **Analyzing the Marvell NFC Driver's Bulk URB Handling**
Okay, so I'm looking at how the Marvell NFC driver handles bulk URB submissions, specifically the `nfcmrvl_submit_bulk_urb` function. It seems pretty straightforward: submit the URB. Now, if the URB completes *immediately* – which makes me think of scenarios like a dummy HCD or some other incredibly fast completion – `nfcmrvl_bulk_complete` gets called.
Now, let's zoom in on `nfcmrvl_bulk_complete`. The code snippet starts with a crucial check: `!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)`. Hold on a second... `NFCMRVL_NCI_RUNNING`. I'm immediately curious: when is this bit set? It's a critical guard, preventing the function from running if a condition isn't met. I need to trace back and figure out where this flag is initialized.
Right, let me check the likely candidates. Given the nature of this driver, it's probably set during the initialization or opening of the NFC device. Let's look at `nfcmrvl_nci_open` in `drivers/nfc/nfcmrvl/main.c`. That seems like the logical place to start. I'll bet I'll find the answer in there.
|
| 19/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/main.c Name:nfcmrvl_nci_open]
Results: map[SourceCode: 17: static int nfcmrvl_nci_open(struct nci_dev *ndev)
18: {
19: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
20: int err;
21:
22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
23: return 0;
24:
25: /* Reset possible fault of previous session */
26: clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
27:
28: err = priv->if_ops->nci_open(priv);
29:
30: if (err)
31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
32:
33: return err;
34: }
SourceFile:drivers/nfc/nfcmrvl/main.c]
|
| 20/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 19949
output: 24
thoughts: 187
Thoughts: **A Potential Synchronization Bug**
Okay, here's what's running through my mind. I've spotted something potentially problematic with this `NFCMRVL_NCI_RUNNING` flag. I see it's being set in `priv->flags` – that's the driver's private data, right? Good, that makes sense for tracking internal state. But, then, in `nfcmrvl_bulk_complete`, it's checking `drv_data->flags`! Alarm bells! Are we supposed to be using *different* flags in these two locations?
I'm starting to get that uneasy feeling you get when you see a potential synchronization issue. Let me double-check `nfcmrvl_bulk_complete`:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
Yes, there it is again. It's looking at `drv_data->flags`. But the _other_ part of the system is setting it in `priv->flags`. This could be a race condition waiting to happen. Is `NFCMRVL_NCI_RUNNING` meant to be in `drv_data->flags`, or is there a simple variable assignment error?
I need to see where `NFCMRVL_NCI_RUNNING` is actually defined. The scope of that macro will tell me everything. Let's find that definition and see what's going on.
|
| 21/2 |
2026/08/26 14:14 |
tool |
grepper |
0m
Args: map[Expression:NFCMRVL_NCI_RUNNING]
Results: map[Output:drivers/nfc/nfcmrvl/main.c=17=static int nfcmrvl_nci_open(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-21-
drivers/nfc/nfcmrvl/main.c:22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-23- return 0;
--
drivers/nfc/nfcmrvl/main.c-30- if (err)
drivers/nfc/nfcmrvl/main.c:31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
drivers/nfc/nfcmrvl/main.c-32-
--
drivers/nfc/nfcmrvl/main.c=36=static int nfcmrvl_nci_close(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-39-
drivers/nfc/nfcmrvl/main.c:40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-41- return 0;
--
drivers/nfc/nfcmrvl/main.c=194=int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
--
drivers/nfc/nfcmrvl/main.c-212-
drivers/nfc/nfcmrvl/main.c:213: if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-214- nci_recv_frame(priv->ndev, skb);
--
drivers/nfc/nfcmrvl/nfcmrvl.h=13=struct gpio_desc;
--
drivers/nfc/nfcmrvl/nfcmrvl.h-15-/* Define private flags: */
drivers/nfc/nfcmrvl/nfcmrvl.h:16:#define NFCMRVL_NCI_RUNNING 1
drivers/nfc/nfcmrvl/nfcmrvl.h-17-#define NFCMRVL_PHY_ERROR 2
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-64-
drivers/nfc/nfcmrvl/usb.c:65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-66- return;
--
drivers/nfc/nfcmrvl/usb.c=400=static int nfcmrvl_resume(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-409-
drivers/nfc/nfcmrvl/usb.c:410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-411- goto done;
]
|
| 22/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 21349
output: 26
thoughts: 123
Thoughts: **Investigating Potential Flag Mismatch**
Okay, so I'm seeing something that immediately sets off alarm bells. I've noticed that the `NFCMRVL_NCI_RUNNING` flag is being checked in `drv_data->flags` within `usb.c`. My initial thought was, "Aha! Is it being set differently elsewhere?" I started digging and found that it's actually being set in `priv->flags` in `main.c`.
Wait a minute... I have to remind myself that `NFCMRVL_NCI_RUNNING` is actually just defined as `1`. Hmm, okay, maybe that isn't the issue.
Now, let me shift gears slightly. I remember seeing a similar issue before. In `usb.c`, I need to double-check that `NFCMRVL_USB_BULK_RUNNING` is also defined as `1`. I need to ensure there isn't some strange clash or bit manipulation going on that I'm overlooking. Let's get into `usb.c` and make sure that's all clear.
|
| 23/2 |
2026/08/26 14:14 |
tool |
grepper |
0m
Args: map[Expression:NFCMRVL_USB_BULK_RUNNING]
Results: map[Output:drivers/nfc/nfcmrvl/usb.c=21=MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
drivers/nfc/nfcmrvl/usb.c-22-
drivers/nfc/nfcmrvl/usb.c:23:#define NFCMRVL_USB_BULK_RUNNING 1
drivers/nfc/nfcmrvl/usb.c-24-#define NFCMRVL_USB_SUSPENDING 2
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-83-
drivers/nfc/nfcmrvl/usb.c:84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-85- return;
--
drivers/nfc/nfcmrvl/usb.c=166=static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/usb.c-180-
drivers/nfc/nfcmrvl/usb.c:181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-182- nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
--
drivers/nfc/nfcmrvl/usb.c=197=static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/usb.c-203-
drivers/nfc/nfcmrvl/usb.c:204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-205-
--
drivers/nfc/nfcmrvl/usb.c=400=static int nfcmrvl_resume(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-412-
drivers/nfc/nfcmrvl/usb.c:413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
drivers/nfc/nfcmrvl/usb.c-414- err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
drivers/nfc/nfcmrvl/usb.c-415- if (err) {
drivers/nfc/nfcmrvl/usb.c:416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-417- goto failed;
]
|
| 24/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 22500
output: 50
thoughts: 946
Thoughts: **Analyzing a Potential Race Condition in the NFC Driver**
Okay, so I'm looking at this NFC driver code, and I've spotted something potentially problematic in how it handles USB bulk transfers. I see that `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` are both defined as 1. My initial thought is that `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)` is actually equivalent to `test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)`! This is because, as it's written, they are referencing the same bit in `drv_data->flags`.
This becomes apparent when I look at `nfcmrvl_bulk_complete`. The code includes this check:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
It seems like it’s checking the `NFCMRVL_USB_BULK_RUNNING` bit, which is 1. Wait, is this a typo? Should it be checking something else? Perhaps it was supposed to be checking `drv_data->priv->flags` for the `NCI_RUNNING` flag? Or, more simply, just check `NFCMRVL_USB_BULK_RUNNING` directly?
If the intent was to check `NFCMRVL_USB_BULK_RUNNING`, then we have a duplicate check later in the code:
```c
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
return;
```
That’s definitely a bug. The check should be something like `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->priv->flags)`.
Now, digging further, I see that in `nfcmrvl_usb_nci_open`:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
```
The key observation is that `nfcmrvl_submit_bulk_urb` is called *before* `NFCMRVL_USB_BULK_RUNNING` is actually set in `drv_data->flags`. This could be a race condition. If the URB completes immediately (e.g., a dummy HCD), `nfcmrvl_bulk_complete` gets called, but that function checks `NFCMRVL_NCI_RUNNING`. Since that's equivalent to `NFCMRVL_USB_BULK_RUNNING`, and it hasn’t been set yet, the driver could return prematurely, and we might miss a bulk completion.
This initial analysis doesn't immediately suggest a crash, but it can cause the driver to fail to process bulk completions.
I need to confirm that `NFCMRVL_NCI_RUNNING` is actually defined as 1, so I’ll check `nfcmrvl.h` to make sure. I've got a potential race condition here, and it's time to investigate how this affects the state machine.
|
| 25/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/nfc/nfcmrvl/nfcmrvl.h Name:NFCMRVL_NCI_RUNNING]
Results: map[SourceCode: SourceFile:]
|
| 26/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 24284
output: 42
thoughts: 24
Thoughts: Okay, let's dive into this. Here's my take on those initial thoughts:
**Analyzing the nfcmrvl.h Header File**
Alright, the logical first step is to get right into the heart of the matter. Instead of trying to reconstruct things from high-level descriptions or indirect references, I'm going to go straight to the source. Let's pull up the `drivers/nfc/nfcmrvl/nfcmrvl.h` file. That's where the fundamental definitions, structures, and possibly even some core function declarations are housed for this Marvell NFC driver. It's the essential blueprint. By understanding what's declared in the header, I can begin to grasp the overall architecture, how the different components are meant to interact, and start to form hypotheses about its internal workings, the data flow, and potential points of interest for deeper investigation. This is the foundation upon which everything else rests, and it will give me the context I need to tackle the more complex interactions.
|
| 27/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:1 LineCount:30]
Results: map[Contents: 1: /* SPDX-License-Identifier: GPL-2.0-only */
2: /*
3: * Marvell NFC driver
4: *
5: * Copyright (C) 2014-2015, Marvell International Ltd.
6: */
7:
8: #ifndef _NFCMRVL_H_
9: #define _NFCMRVL_H_
10:
11: #include "fw_dnld.h"
12:
13: struct gpio_desc;
14:
15: /* Define private flags: */
16: #define NFCMRVL_NCI_RUNNING 1
17: #define NFCMRVL_PHY_ERROR 2
18:
19: #define NFCMRVL_EXT_COEX_ID 0xE0
20: #define NFCMRVL_NOT_ALLOWED_ID 0xE1
21: #define NFCMRVL_ACTIVE_ID 0xE2
22: #define NFCMRVL_EXT_COEX_ENABLE 1
23: #define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA
24: #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB
25: #define NFCMRVL_NCI_MAX_EVENT_SIZE 260
26:
27: /*
28: * NCI FW Parameters
29: */
30:
]
|
| 28/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 24975
output: 40
thoughts: 35
Thoughts: Here's my take on the situation:
**Investigating the NFCMRVL Module**
Okay, so I'm seeing that `NFCMRVL_NCI_RUNNING` is set to 1. That's the starting point. Now, I need to understand what that actually *means* in the context of the Marvell NFC driver. My next logical step is to delve into `drivers/nfc/nfcmrvl/usb.c`. This is where the USB-specific implementation will be, which is likely where this flag is being used, or at least how it's being managed. I'll need to trace its usage, identify the functions that interact with this flag, and figure out the state transitions related to `NFCMRVL_NCI_RUNNING`. Understanding that USB driver's behavior is critical. I'll start with a `grep` or similar command to zero in on how and where the flag is tested and modified, then dig deeper based on those hits. This could be anything from initialization routines to interrupt handlers. Knowing the context will point me towards the solution I'm looking for.
|
| 29/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:20 LineCount:10]
Results: map[Contents: 20:
21: MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
22:
23: #define NFCMRVL_USB_BULK_RUNNING 1
24: #define NFCMRVL_USB_SUSPENDING 2
25:
26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
]
|
| 30/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 25435
output: 34
thoughts: 182
Thoughts: **Debugging a Potential NULL Pointer Dereference**
Okay, so I'm looking at this crash log, and it seems like `NFCMRVL_USB_BULK_RUNNING` is definitely set to 1. But hang on, this bit check, `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)`, doesn't seem to make sense. It should return if the bit isn't set, so how can we get this crash? Something's not lining up. Let me re-examine the crash.
Ah, here it is: `skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);`. I'm immediately suspicious of that `drv_data->priv->ndev` dereference. It's almost certainly a `NULL` pointer dereference, right? If `drv_data->priv` is `NULL`, then accessing `ndev` off of it is going to cause a segmentation fault, precisely what we're seeing.
Now the question is *why* is `drv_data->priv` `NULL`? I need to trace back where that pointer is initialized. Let's find where `drv_data->priv` is actually set and figure out where the root cause lies.
|
| 31/2 |
2026/08/26 14:14 |
tool |
grepper |
0m
Args: map[Expression:priv PathPrefix:drivers/nfc/nfcmrvl/usb.c]
Results: map[Output:drivers/nfc/nfcmrvl/usb.c=26=struct nfcmrvl_usb_drv_data {
--
drivers/nfc/nfcmrvl/usb.c-39- int suspend_count;
drivers/nfc/nfcmrvl/usb.c:40: struct nfcmrvl_private *priv;
drivers/nfc/nfcmrvl/usb.c-41-};
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-70-
drivers/nfc/nfcmrvl/usb.c:71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
drivers/nfc/nfcmrvl/usb.c-72- GFP_ATOMIC);
--
drivers/nfc/nfcmrvl/usb.c-77- urb->actual_length);
drivers/nfc/nfcmrvl/usb.c:78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
drivers/nfc/nfcmrvl/usb.c-79- nfc_err(&drv_data->udev->dev,
--
drivers/nfc/nfcmrvl/usb.c=147=static void nfcmrvl_tx_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-150- struct nci_dev *ndev = (struct nci_dev *)skb->dev;
drivers/nfc/nfcmrvl/usb.c:151: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
drivers/nfc/nfcmrvl/usb.c:152: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-153- unsigned long flags;
drivers/nfc/nfcmrvl/usb.c-154-
drivers/nfc/nfcmrvl/usb.c:155: nfc_info(priv->dev, "urb %p status %d count %d\n",
drivers/nfc/nfcmrvl/usb.c-156- urb, urb->status, urb->actual_length);
--
drivers/nfc/nfcmrvl/usb.c-165-
drivers/nfc/nfcmrvl/usb.c:166:static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
drivers/nfc/nfcmrvl/usb.c-167-{
drivers/nfc/nfcmrvl/usb.c:168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-169- int err;
--
drivers/nfc/nfcmrvl/usb.c=192=static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
--
drivers/nfc/nfcmrvl/usb.c-196-
drivers/nfc/nfcmrvl/usb.c:197:static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
drivers/nfc/nfcmrvl/usb.c-198-{
drivers/nfc/nfcmrvl/usb.c:199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-200- int err;
--
drivers/nfc/nfcmrvl/usb.c-219-
drivers/nfc/nfcmrvl/usb.c:220:static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
drivers/nfc/nfcmrvl/usb.c-221- struct sk_buff *skb)
drivers/nfc/nfcmrvl/usb.c-222-{
drivers/nfc/nfcmrvl/usb.c:223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-224- struct urb *urb;
--
drivers/nfc/nfcmrvl/usb.c=286=static int nfcmrvl_probe(struct usb_interface *intf,
--
drivers/nfc/nfcmrvl/usb.c-289- struct nfcmrvl_usb_drv_data *drv_data;
drivers/nfc/nfcmrvl/usb.c:290: struct nfcmrvl_private *priv;
drivers/nfc/nfcmrvl/usb.c-291- struct usb_device *udev = interface_to_usbdev(intf);
--
drivers/nfc/nfcmrvl/usb.c-319-
drivers/nfc/nfcmrvl/usb.c:320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
drivers/nfc/nfcmrvl/usb.c-321- &intf->dev, &config);
drivers/nfc/nfcmrvl/usb.c:322: if (IS_ERR(priv))
drivers/nfc/nfcmrvl/usb.c:323: return PTR_ERR(priv);
drivers/nfc/nfcmrvl/usb.c-324-
drivers/nfc/nfcmrvl/usb.c:325: drv_data->priv = priv;
drivers/nfc/nfcmrvl/usb.c:326: drv_data->priv->support_fw_dnld = false;
drivers/nfc/nfcmrvl/usb.c-327-
--
drivers/nfc/nfcmrvl/usb.c=333=static void nfcmrvl_disconnect(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-341-
drivers/nfc/nfcmrvl/usb.c:342: nfcmrvl_nci_unregister_dev(drv_data->priv);
drivers/nfc/nfcmrvl/usb.c-343-
]
|
| 32/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 27608
output: 48
thoughts: 222
Thoughts: **Investigating a Potential Race Condition in the NFCMRVL Driver**
Okay, so I'm looking at this `nfcmrvl_probe` function, and something's bugging me. Specifically, it looks like `drv_data->priv` is being assigned *after* the call to `nfcmrvl_nci_register_dev`. This immediately raises a red flag. What exactly *does* `nfcmrvl_nci_register_dev` do? I need to understand if this function exposes the device to userspace, or even if it's interacting with netlink. If it does, then there's a serious possibility of a race condition. Userspace could theoretically call `nci_dev_up` (which, I suspect, ultimately leads to `nfcmrvl_nci_open`) *before* `drv_data->priv` is set. If that's the case, we've got a potential access to uninitialized data, or worse, a double free situation when things are cleaned up. I need to dive into `drivers/nfc/nfcmrvl/main.c` and take a closer look at `nfcmrvl_nci_register_dev` to see what's going on under the hood. Time to dig in!
|
| 33/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/main.c Name:nfcmrvl_nci_register_dev]
Results: map[SourceCode: 92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
101: int tailroom;
102: u32 protocols;
103:
104: priv = kzalloc_obj(*priv);
105: if (!priv)
106: return ERR_PTR(-ENOMEM);
107:
108: priv->drv_data = drv_data;
109: priv->if_ops = ops;
110: priv->dev = dev;
111: priv->phy = phy;
112:
113: memcpy(&priv->config, pdata, sizeof(*pdata));
114:
115: if (!priv->config.reset_gpio) {
116: priv->config.reset_gpio =
117: devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
118: if (IS_ERR(priv->config.reset_gpio)) {
119: priv->config.reset_gpio = NULL;
120: nfc_err(dev, "failed to get reset gpio\n");
121: }
122: }
123:
124: if (phy == NFCMRVL_PHY_SPI) {
125: headroom = NCI_SPI_HDR_LEN;
126: tailroom = 1;
127: } else
128: headroom = tailroom = 0;
129:
130: if (priv->config.hci_muxed)
131: headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
132:
133: protocols = NFC_PROTO_JEWEL_MASK
134: | NFC_PROTO_MIFARE_MASK
135: | NFC_PROTO_FELICA_MASK
136: | NFC_PROTO_ISO14443_MASK
137: | NFC_PROTO_ISO14443_B_MASK
138: | NFC_PROTO_ISO15693_MASK
139: | NFC_PROTO_NFC_DEP_MASK;
140:
141: priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
142: headroom, tailroom);
143: if (!priv->ndev) {
144: nfc_err(dev, "nci_allocate_device failed\n");
145: rc = -ENOMEM;
146: goto error_free;
147: }
148:
149: rc = nfcmrvl_fw_dnld_init(priv);
150: if (rc) {
151: nfc_err(dev, "failed to initialize FW download %d\n", rc);
152: goto error_free_dev;
153: }
154:
155: nci_set_drvdata(priv->ndev, priv);
156:
157: rc = nci_register_device(priv->ndev);
158: if (rc) {
159: nfc_err(dev, "nci_register_device failed %d\n", rc);
160: goto error_fw_dnld_deinit;
161: }
162:
163: /* Ensure that controller is powered off */
164: nfcmrvl_chip_halt(priv);
165:
166: nfc_info(dev, "registered with nci successfully\n");
167: return priv;
168:
169: error_fw_dnld_deinit:
170: nfcmrvl_fw_dnld_deinit(priv);
171: error_free_dev:
172: nci_free_device(priv->ndev);
173: error_free:
174: kfree(priv);
175: return ERR_PTR(rc);
176: }
SourceFile:drivers/nfc/nfcmrvl/main.c]
|
| 34/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 29457
output: 45
thoughts: 1880
Thoughts: **The Race Condition in nfcmrvl USB Driver**
Okay, so here's the issue I've identified in the Marvell NFC USB driver. It boils down to a race condition that could lead to a NULL pointer dereference and, ultimately, a crash. Let me walk you through my thought process.
First, I see that `nci_register_device` is called before `nfcmrvl_nci_register_dev` returns. This is crucial because `nci_register_device` makes the device visible to userspace through netlink. The potential problem begins when userspace can immediately send a netlink message to bring the device up (`nfc_dev_up`). This leads to the call chain: `nci_dev_up` -> `nci_open_device` -> `nfcmrvl_nci_open` -> `nfcmrvl_usb_nci_open`.
Inside `nfcmrvl_usb_nci_open`, a USB URB is submitted. Now, here's where things get interesting. If this URB completes immediately—let's say we're using `dummy_hcd` or an error happens—the callback function `nfcmrvl_bulk_complete` is called immediately.
The problem lies in `nfcmrvl_bulk_complete`. This function dereferences `drv_data->priv->ndev`. *But*, the critical data structure `drv_data->priv` isn't actually initialized until *after* `nfcmrvl_nci_register_dev` returns, in the `nfcmrvl_probe` function.
Let me confirm this. In `nfcmrvl_probe`, I see:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv;
```
So `drv_data->priv` is only set *after* `nfcmrvl_nci_register_dev` returns. If `nci_register_device` is called *inside* `nfcmrvl_nci_register_dev` (as it seems to be), then we have a potential race.
Now, looking at `nfcmrvl_usb_nci_open`:
```c
static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
drv_data->intf->needs_remote_wakeup = 1;
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
...
```
I see that `nfcmrvl_submit_bulk_urb` submits the URB. Now if the URB *completes* immediately, then `nfcmrvl_bulk_complete` is called:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_usb_drv_data *drv_data = urb->context;
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))
return;
if (!urb->status) {
struct sk_buff *skb;
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
GFP_ATOMIC);
```
Here's the problem: `drv_data->priv` is accessed *before* it's set. Because, `nfcmrvl_probe` hasn't reached `drv_data->priv = priv;` yet!
But now I have an idea. Let's see how `NFCMRVL_NCI
|
| 35/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c Name:nfcmrvl_bulk_complete]
Results: map[SourceCode: 57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
100: }
SourceFile:drivers/nfc/nfcmrvl/usb.c]
|
| 36/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 33119
output: 43
thoughts: 1371
Thoughts: **Root Cause Analysis: Race Condition in Marvell NFC Driver**
Okay, let's break this down. Line 65 and 84 are the keys, both checking the same bit. I see the code checks `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` on `drv_data->flags` in two different places. This immediately flags a bug. Wait, both checks are effectively the same; they are checking bit 1 of `drv_data->flags`. My immediate thought is, if it's meant to check `drv_data->priv->flags`, it would read like: `if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->priv->flags))`. But if `drv_data->priv` is null, that would cause a crash also, right?
The core problem, I'm fairly certain, is that `nfcmrvl_nci_register_dev` is exposing the device *before* `drv_data->priv` is set. If `drv_data->priv` is set *after* `nfcmrvl_nci_register_dev`, then any callback from the NCI, like `nfcmrvl_usb_nci_open`, will have `priv` as its argument. Okay, let me confirm. Checking `nfcmrvl_usb_nci_open`: it does, in fact, take `priv` as an argument. And it gets `drv_data` from `priv->drv_data`. So, `priv` should NOT be null. The `priv` pointer is allocated in `nfcmrvl_nci_register_dev` and passed to `nci_allocate_device`, which sets it as the NCI drvdata.
When `nci_dev_up` is called, it calls `ndev->ops->open`, which is `nfcmrvl_nci_open`. And `nfcmrvl_nci_open` gets `priv` from `nci_get_drvdata(ndev)`. That makes sense; `priv` is perfectly valid. But wait, `drv_data->priv` is null! The issue lies in `nfcmrvl_probe`. The problem is that `drv_data->priv` is only set *after* `nfcmrvl_nci_register_dev` returns. But `nfcmrvl_nci_register_dev` calls `nci_register_device`, which makes the device visible to userspace.
Userspace *can* call `nfc_dev_up` immediately, which calls `nfcmrvl_usb_nci_open`, and submits the URB. Now the URB completion handler `nfcmrvl_bulk_complete` gets `drv_data` from `urb->context`. Then it tries to access `drv_data->priv`. But, *crucially*, `drv_data->priv` is still NULL because `nfcmrvl_probe` hasn't reached the line `drv_data->priv = priv;` yet!
Okay, let's map out the exact sequence. 1. `nfcmrvl_probe` allocates `drv_data`. 2. It calls `nfcmrvl_nci_register_dev`. 3. `nfcmrvl_nci_register_dev` allocates `priv`. 4. `nfcmrvl_nci_register_dev` sets `priv->drv_data = drv_data`. 5. `nfcmrvl_nci_register_device` is called. 6. The NCI device registers and becomes visible. 7. Userspace or a tool, like syzkaller, sends a netlink message to bring it up. 8. `nci_dev_up` is called. 9. `nfcmrvl_nci_open` is called with `priv`. 10. `nfcmrvl_usb_nci_open` gets called with `priv`. 11. It gets `drv_data = priv->drv_data`. 12. It submits the URB (with `urb->context = drv_data`). 13. The URB completes *immediately*. 14. `nfcmrvl_bulk_complete` is called with `urb`. 15. It gets `drv_data = urb->context`. 16. It tries to dereference `drv_data->priv`. 17. CRASH! Because `nfcmrvl_probe` is still in progress and hasn't assigned `drv_data->priv`.
This is a
|
| 37/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c Name:nfcmrvl_probe]
Results: map[SourceCode: 286: static int nfcmrvl_probe(struct usb_interface *intf,
287: const struct usb_device_id *id)
288: {
289: struct nfcmrvl_usb_drv_data *drv_data;
290: struct nfcmrvl_private *priv;
291: struct usb_device *udev = interface_to_usbdev(intf);
292: struct nfcmrvl_platform_data config;
293: int ret;
294:
295: /* No configuration for USB */
296: memset(&config, 0, sizeof(config));
297: config.reset_gpio = NULL;
298:
299: nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
300:
301: drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
302: if (!drv_data)
303: return -ENOMEM;
304:
305: ret = usb_find_common_endpoints(intf->cur_altsetting, &drv_data->bulk_rx_ep,
306: &drv_data->bulk_tx_ep, NULL, NULL);
307: if (ret)
308: return -ENODEV;
309:
310: drv_data->udev = udev;
311: drv_data->intf = intf;
312:
313: INIT_WORK(&drv_data->waker, nfcmrvl_waker);
314: spin_lock_init(&drv_data->txlock);
315:
316: init_usb_anchor(&drv_data->tx_anchor);
317: init_usb_anchor(&drv_data->bulk_anchor);
318: init_usb_anchor(&drv_data->deferred);
319:
320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
SourceFile:drivers/nfc/nfcmrvl/usb.c]
|
| 38/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 36333
output: 29
thoughts: 344
Thoughts: **Race Condition Discovery & Analysis**
Okay, so just as I suspected. The assignment `drv_data->priv = priv;` happens *after* `nfcmrvl_nci_register_dev` returns. The problem is that `nfcmrvl_nci_register_dev` calls `nci_register_device`, which effectively exposes the device to userspace. That means we have a nasty race condition.
Alright, how do we fix this? Let's see... could we pass a pointer to `drv_data->priv` to `nfcmrvl_nci_register_dev`? No, that won't work. `nfcmrvl_nci_register_dev` returns `priv`, not the address to which it should be written. Could we just set `drv_data->priv` within `nfcmrvl_nci_register_dev`? Nope, `nfcmrvl_nci_register_dev` doesn't know about `drv_data->priv`, it only deals with a `void *` that's passed in.
Wait a second, if `nfcmrvl_bulk_complete` needs `priv`, it can get it from `drv_data->priv`. But `drv_data->priv` is NULL when `nfcmrvl_bulk_complete` is running in userspace.
Okay, let's back up. Can we set `drv_data->priv` *before* `nci_register_device` is called? The answer is no, because `priv` is allocated inside `nfcmrvl_nci_register_dev`.
This is a real problem. And wait... `nfcmrvl_nci_register_dev` is a common function used across the `usb.c`, `uart.c`, `spi.c`, and `i2c.c` drivers. I need to check if those other PHY drivers are experiencing the same issue.
|
| 39/2 |
2026/08/26 14:14 |
tool |
codesearch-find-references |
0m
Args: map[Name:nfcmrvl_nci_register_dev]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_probe SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:320] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__UNIQUE_ID_addressable_nfcmrvl_nci_register_dev_1014 SourceFile:drivers/nfc/nfcmrvl/main.c SourceLine:177] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_nfcmrvl_nci_register_dev SourceFile:drivers/nfc/nfcmrvl/main.c SourceLine:177] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_nfcmrvl_nci_register_dev SourceFile:drivers/nfc/nfcmrvl/main.c SourceLine:177]] TruncatedOutput:false]
|
| 40/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 37438
output: 25
thoughts: 41
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Debugging Embedded Systems: A Focused Approach**
Alright, before I dive too deep into this, I need to take a systematic approach. I've got this NFC Marvell NCI issue, and my first instinct is to zero in on the low-level communication drivers. So, I'm thinking, "Where would the initialization and device registration of the NFC Marvell NCI component likely occur within the system?" It's a fairly standard pattern, so my gut says those primary interface drivers – `i2c.c`, `uart.c`, and `spi.c` – are prime suspects.
Now, instead of blindly poking around, let's get targeted. I'll use `grep` to quickly search within those driver source files for the function `nfcmrvl_nci_register_dev`. That should directly pinpoint where this device is being initialized and, hopefully, reveal some clues about the context in which it's being done, and where any failures could be occurring. This is the fastest way to get a good understanding of the module's state. Then, I can start looking at call stacks and other debugging information.
|
| 41/2 |
2026/08/26 14:14 |
tool |
grepper |
0m
Args: map[Expression:nfcmrvl_nci_register_dev]
Results: map[Output:drivers/nfc/nfcmrvl/i2c.c=186=static int nfcmrvl_i2c_probe(struct i2c_client *client)
--
drivers/nfc/nfcmrvl/i2c.c-227-
drivers/nfc/nfcmrvl/i2c.c:228: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
drivers/nfc/nfcmrvl/i2c.c-229- drv_data, &i2c_ops,
--
drivers/nfc/nfcmrvl/main.c=84=static const struct nci_ops nfcmrvl_nci_ops = {
--
drivers/nfc/nfcmrvl/main.c-91-
drivers/nfc/nfcmrvl/main.c:92:struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
drivers/nfc/nfcmrvl/main.c-93- void *drv_data,
--
drivers/nfc/nfcmrvl/main.c-176-}
drivers/nfc/nfcmrvl/main.c:177:EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
drivers/nfc/nfcmrvl/main.c-178-
--
drivers/nfc/nfcmrvl/nfcmrvl.h=120=int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
drivers/nfc/nfcmrvl/nfcmrvl.h:121:struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
drivers/nfc/nfcmrvl/nfcmrvl.h-122- void *drv_data,
--
drivers/nfc/nfcmrvl/spi.c=127=static int nfcmrvl_spi_probe(struct spi_device *spi)
--
drivers/nfc/nfcmrvl/spi.c-159-
drivers/nfc/nfcmrvl/spi.c:160: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
drivers/nfc/nfcmrvl/spi.c-161- drv_data, &spi_ops,
--
drivers/nfc/nfcmrvl/uart.c=108=static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
--
drivers/nfc/nfcmrvl/uart.c-133-
drivers/nfc/nfcmrvl/uart.c:134: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
drivers/nfc/nfcmrvl/uart.c-135- dev, pdata);
--
drivers/nfc/nfcmrvl/usb.c=286=static int nfcmrvl_probe(struct usb_interface *intf,
--
drivers/nfc/nfcmrvl/usb.c-319-
drivers/nfc/nfcmrvl/usb.c:320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
drivers/nfc/nfcmrvl/usb.c-321- &intf->dev, &config);
]
|
| 42/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 38685
output: 52
thoughts: 56
Thoughts: **My Concerns Regarding Driver Initialization**
Okay, so I'm looking over this code and something's immediately catching my attention. It seems that across the board, every implementation sets `drv_data->priv` *after* `nfcmrvl_nci_register_dev` has returned. Now, that's not necessarily inherently wrong, but it's making me wonder if it could be a potential issue, particularly with how `i2c.c` or `spi.c` are handling things. I need to dig deeper into those files and figure out what, if any, dependencies are being introduced. Is this a potential race condition? Are there assumptions in the driver registration process that might be violated? Time to get into the details.
|
| 43/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/nfc/nfcmrvl/i2c.c Name:nfcmrvl_i2c_nci_open]
Results: map[SourceCode: SourceFile:]
|
| 44/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 38971
output: 39
thoughts: 18
Thoughts: Here's a summary of my thought process:
**Diving into the `nci_open` Function**
Okay, so I need to understand how `nci_open` is working. Given the context, it's highly likely this is related to I2C communication. My first move is to directly check the `i2c.c` file within the relevant code base. That's usually the most direct path to understanding how an I2C-specific function like `nci_open` is implemented and how it interacts with the lower-level I2C drivers or hardware abstraction layers. I'm going to look for the implementation of `nci_open` and how it handles initialization, device access, and error conditions within the I2C context. This will give me a solid foundation for further analysis.
|
| 45/2 |
2026/08/26 14:14 |
tool |
grepper |
0m
Args: map[Expression:nci_open PathPrefix:drivers/nfc/nfcmrvl/i2c.c]
Results: map[Output:drivers/nfc/nfcmrvl/i2c.c=64=static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
--
drivers/nfc/nfcmrvl/i2c.c-93-
drivers/nfc/nfcmrvl/i2c.c:94:static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
drivers/nfc/nfcmrvl/i2c.c-95-{
--
drivers/nfc/nfcmrvl/i2c.c=153=static const struct nfcmrvl_if_ops i2c_ops = {
drivers/nfc/nfcmrvl/i2c.c:154: .nci_open = nfcmrvl_i2c_nci_open,
drivers/nfc/nfcmrvl/i2c.c-155- .nci_close = nfcmrvl_i2c_nci_close,
]
|
| 46/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 39483
output: 52
thoughts: 19
Thoughts: Okay, here's my interpretation of that.
**Analyzing the Function: `nfcmrvl_i2c_nci_open`**
Alright, let's dive into this `nfcmrvl_i2c_nci_open` function. Given the naming convention, I'm already making some educated guesses, assuming I know the existing architecture. "nfcmrvl" likely refers to the NFC controller from Marvell, obviously. The "i2c" indicates that the communication interface used between the host and the NFC controller is I2C. And finally, "nci" is almost certainly referring to the NFC Controller Interface, or NCI, the standard protocol for NFC communication.
Therefore, this function is responsible for opening or initializing the communication channel between the system and the Marvell NFC controller that utilizes the I2C interface, probably to establish an NCI context. I need to understand what this specific function is doing regarding error handling, resource allocation (like I2C bus acquisition and potential memory allocation), and possibly what state it's setting the NFC controller to initially. I'll need to look deeper into it to see what actions this function is taking. I expect it'll involve configuring the I2C bus parameters (speed, addressing), checking for the presence of the NFC controller, and potentially sending initial commands to bring it online. This is probably a crucial function for the whole NFC stack to work properly, so I better understand it completely!
|
| 47/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/nfc/nfcmrvl/i2c.c Name:nfcmrvl_i2c_nci_open]
Results: map[SourceCode: SourceFile:]
|
| 48/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 39883
output: 42
thoughts: 27
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the Function Call**
Okay, so I see the function in question is `nfcmrvl_i2c_nci_open`. Immediately, the naming convention tells me this is likely related to NFC (Near Field Communication), specifically a Marvell chipset, using I2C for communication, and it's an "open" function, so likely initializing a connection or resource. My first instinct, given the context, is to reach for a tool or approach that allows for a read operation.
|
| 49/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:90 LineCount:20]
Results: map[Contents: 90: }
91: return IRQ_HANDLED;
92: }
93:
94: static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
95: {
96: struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
97:
98: if (!drv_data)
99: return -ENODEV;
100:
101: return 0;
102: }
103:
104: static int nfcmrvl_i2c_nci_close(struct nfcmrvl_private *priv)
105: {
106: return 0;
107: }
108:
109: static int nfcmrvl_i2c_nci_send(struct nfcmrvl_private *priv,
]
|
| 50/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 40353
output: 42
thoughts: 44
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating the NFC Marvell Driver**
Right, so I'm looking into the NFC Marvell driver and my first impression is that `nfcmrvl_i2c_nci_open` is, well, pretty underwhelming. It doesn't seem to be doing much heavy lifting. Okay, let's keep that in mind as a potential optimization target later. Now, my focus shifts. I need to understand the critical path here. Given that `nfcmrvl_i2c_nci_open` is light on action, I need to know *where* the real work is happening. My gut tells me that the interrupt handling and thread function is going to be the key, so I am going to delve deeply into `nfcmrvl_i2c_int_irq_thread_fn`. That's where I bet the real meat of the driver's operation, the state machine, the communication with the hardware and associated data processing, must be. That's the function to really understand.
|
| 51/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:60 LineCount:30]
Results: map[Contents: 60:
61: return 0;
62: }
63:
64: static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
65: {
66: struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
67: struct sk_buff *skb = NULL;
68: int ret;
69:
70: if (!drv_data->priv)
71: return IRQ_HANDLED;
72:
73: if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
74: return IRQ_HANDLED;
75:
76: ret = nfcmrvl_i2c_read(drv_data, &skb);
77:
78: switch (ret) {
79: case -EREMOTEIO:
80: set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
81: break;
82: case -ENOMEM:
83: case -EBADMSG:
84: nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
85: break;
86: default:
87: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
88: nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
89: break;
]
|
| 52/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 41146
output: 40
thoughts: 71
Thoughts: **Checking for Null Pointer Vulnerabilities**
Okay, so in `i2c.c`, specifically within the `nfcmrvl_i2c_int_irq_thread_fn` function, there's a crucial check right at the beginning: `if (!drv_data->priv) return IRQ_HANDLED;`. That's good. It appears they're proactively guarding against `drv_data->priv` being a null pointer. This prevents a potential dereference, which is excellent. We want to avoid those at all costs. Now, I'm going to shift gears and see if similar precautions have been taken in `spi.c`. Let's see what the code's doing there.
|
| 53/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:60 LineCount:30]
Results: map[Contents: 60: static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
61: {
62: return 0;
63: }
64:
65: static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
66: struct sk_buff *skb)
67: {
68: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
69: int err;
70:
71: /* Reinit completion for slave handshake */
72: reinit_completion(&drv_data->handshake_completion);
73: set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
74:
75: /*
76: * Append a dummy byte at the end of SPI frame. This is due to a
77: * specific DMA implementation in the controller
78: */
79: skb_put(skb, 1);
80:
81: /* Send the SPI packet */
82: err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
83: skb);
84: if (err)
85: nfc_err(priv->dev, "spi_send failed %d", err);
86:
87: return err;
88: }
89:
]
|
| 54/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 41834
output: 35
thoughts: 23
Thoughts: Here's my thought process, summarized as you requested:
**Diving into the `spi.c` IRQ Handler**
Okay, so I see `spi.c` is using `nci_spi`. That tells me where to start digging deeper. Now, let's get right to the core of the problem and investigate its Interrupt Request (IRQ) handler. That's where the real action, the stuff that's probably causing the issues, is likely happening. Gotta see what's triggering interrupts and how they're being managed. This is where I'll get the key insights on flow control and potential bottlenecks.
|
| 55/2 |
2026/08/26 14:14 |
tool |
grepper |
0m
Args: map[Expression:irq PathPrefix:drivers/nfc/nfcmrvl/spi.c]
Results: map[Output:drivers/nfc/nfcmrvl/spi.c-10-#include <linux/nfc.h>
drivers/nfc/nfcmrvl/spi.c:11:#include <linux/of_irq.h>
drivers/nfc/nfcmrvl/spi.c-12-#include <net/nfc/nci.h>
--
drivers/nfc/nfcmrvl/spi.c=19=struct nfcmrvl_spi_drv_data {
--
drivers/nfc/nfcmrvl/spi.c-26-
drivers/nfc/nfcmrvl/spi.c:27:static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
drivers/nfc/nfcmrvl/spi.c-28-{
--
drivers/nfc/nfcmrvl/spi.c=106=static int nfcmrvl_spi_parse_dt(struct device_node *node,
--
drivers/nfc/nfcmrvl/spi.c-116-
drivers/nfc/nfcmrvl/spi.c:117: ret = irq_of_parse_and_map(node, 0);
drivers/nfc/nfcmrvl/spi.c-118- if (!ret) {
drivers/nfc/nfcmrvl/spi.c:119: pr_err("Unable to get irq\n");
drivers/nfc/nfcmrvl/spi.c-120- return -EINVAL;
drivers/nfc/nfcmrvl/spi.c-121- }
drivers/nfc/nfcmrvl/spi.c:122: pdata->irq = ret;
drivers/nfc/nfcmrvl/spi.c-123-
--
drivers/nfc/nfcmrvl/spi.c=127=static int nfcmrvl_spi_probe(struct spi_device *spi)
--
drivers/nfc/nfcmrvl/spi.c-150-
drivers/nfc/nfcmrvl/spi.c:151: ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
drivers/nfc/nfcmrvl/spi.c:152: NULL, nfcmrvl_spi_int_irq_thread_fn,
drivers/nfc/nfcmrvl/spi.c-153- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
]
|
| 56/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 42674
output: 40
thoughts: 21
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Analyzing the nfcmrvl_spi_int_irq_thread_fn Function**
Alright, let's dive into the `nfcmrvl_spi_int_irq_thread_fn`. My initial expectation is this is likely the interrupt service thread function for the NFC Marvell SPI driver, judging by the naming. "nfcmrvl" clearly points to the NFC Marvell implementation, "spi" indicates the Serial Peripheral Interface, and "int_irq" suggests it's related to interrupt handling. The "thread_fn" suffix means it runs as a kernel thread, allowing it to perform potentially blocking operations without holding up the main interrupt handler context.
Now, I'm thinking about the typical flow. This thread probably gets woken up by a hardware interrupt, specifically the NFC controller on the SPI bus. I'll be looking for how the interrupt is configured and enabled in the initialization code, and how that relates to the interrupt pin on the SPI controller itself. My guess is this thread will read status registers from the NFC chip, determine the cause of the interrupt (likely data ready, error, or some other event), and then call appropriate handlers or workqueues to process that event. It needs to be carefully designed to avoid race conditions and ensure data integrity between the interrupt context and this thread.
I'll be paying close attention to any locking mechanisms used within this function. Since it's a thread, it can potentially conflict with other parts of the driver, especially the SPI transfer code. This function needs to synchronize access to shared data structures like the transmit/receive buffers, status flags, and the NFC chip's registers. I expect to see spinlocks or mutexes here, and I'll need to assess their correct use (or misuse).
I'll also want to understand the error handling strategy. What happens if the NFC chip returns an error? How is the driver able to recover? Are there retry mechanisms? Any watchdog timers? This part is critical for robust operation, especially in embedded systems.
Finally, I'll consider performance. Is the thread priority appropriate? Are there any unnecessary delays or blocking calls? Is there efficient use of DMA for data transfers, or is it doing too much busy-waiting? The goal is to maximize throughput and minimize latency, given the real-time constraints imposed by NFC interaction. Looking for ways it could be optimized will also be a priority.
|
| 57/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:27 LineCount:30]
Results: map[Contents: 27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
45: nfc_err(&drv_data->spi->dev, "failed to read spi packet");
46: return IRQ_HANDLED;
47: }
48:
49: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
50: nfc_err(&drv_data->spi->dev, "corrupted RX packet");
51:
52: return IRQ_HANDLED;
53: }
54:
55: static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
56: {
]
|
| 58/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 43734
output: 40
thoughts: 223
Thoughts: **Vulnerability Discovered: Potential Race Condition in NFC Driver**
Okay, so I've been digging into this `spi.c` driver, specifically the `nfcmrvl_spi_int_irq_thread_fn` function. And I've found something that's really concerning. It looks like it directly calls `nfcmrvl_nci_recv_frame` using `drv_data->priv` *without* checking if `drv_data->priv` is actually initialized. That's a huge red flag! It means if an interrupt arrives before that `drv_data->priv` pointer gets properly set, the whole thing will crash. Classic race condition, right?
Then I went and looked back at when the interrupt request actually happens. Boom! The `devm_request_threaded_irq` call is made in `nfcmrvl_spi_probe`, and *that* happens BEFORE `nfcmrvl_nci_register_dev` is called! Which means the window for that race condition is wide open. It’s definitely vulnerable, no doubt about it. I need to make sure this is taken care of ASAP.
Now, I need to check the `uart.c` driver to see if a similar issue exists.
|
| 59/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/uart.c FirstLine:40 LineCount:30]
Results: map[Contents: 40: struct sk_buff *skb)
41: {
42: struct nci_uart *nu = priv->drv_data;
43:
44: return nu->ops.send(nu, skb);
45: }
46:
47: static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
48: const void *param)
49: {
50: struct nci_uart *nu = priv->drv_data;
51: const struct nfcmrvl_fw_uart_config *config = param;
52:
53: nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
54: config->flow_control);
55: }
56:
57: static const struct nfcmrvl_if_ops uart_ops = {
58: .nci_open = nfcmrvl_uart_nci_open,
59: .nci_close = nfcmrvl_uart_nci_close,
60: .nci_send = nfcmrvl_uart_nci_send,
61: .nci_update_config = nfcmrvl_uart_nci_update_config
62: };
63:
64: static int nfcmrvl_uart_parse_dt(struct device_node *node,
65: struct nfcmrvl_platform_data *pdata,
66: struct device *dev)
67: {
68: struct device_node *matched_node;
69: struct gpio_desc *reset_gpio;
]
|
| 60/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 44741
output: 41
thoughts: 43
Thoughts: Here's my take on the thought process, as if I were the one thinking it:
**Deconstructing the UART Implementation**
Okay, so I'm digging into this `uart.c` file and immediately see that it's leaning on `nci_uart`. That's a good starting point. The crucial part here is figuring out how the UART is actually initialized and managed. My next step is to trace the initialization process. Hmm, let's see, `nci_uart_register`... that sounds like a registration function. It's a key function in UART implementation. Let me immediately check where it's called and, more importantly, what happens within it. Going straight to the `nfcmrvl_nci_uart_open` function will give the best insight into the low-level UART driver setup, pin assignments, baud rate, and device configuration. That will provide insight into the UART driver setup.
|
| 61/2 |
2026/08/26 14:14 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/uart.c FirstLine:100 LineCount:40]
Results: map[Contents: 100:
101: return 0;
102: }
103:
104: /*
105: * NCI UART OPS
106: */
107:
108: static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
109: {
110: struct nfcmrvl_private *priv;
111: struct nfcmrvl_platform_data config;
112: const struct nfcmrvl_platform_data *pdata = NULL;
113: struct device *dev = nu->tty->dev;
114:
115: /*
116: * Platform data cannot be used here since usually it is already used
117: * by low level serial driver. We can try to retrieve serial device
118: * and check if DT entries were added.
119: */
120:
121: if (dev && dev->parent && dev->parent->of_node)
122: if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config, dev) == 0)
123: pdata = &config;
124:
125: if (!pdata) {
126: pr_info("No platform data / DT -> fallback to module params\n");
127: config.hci_muxed = hci_muxed;
128: config.reset_gpio = NULL;
129: config.flow_control = flow_control;
130: config.break_control = break_control;
131: pdata = &config;
132: }
133:
134: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
135: dev, pdata);
136: if (IS_ERR(priv))
137: return PTR_ERR(priv);
138:
139: priv->support_fw_dnld = true;
]
|
| 62/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 45619
output: 48
thoughts: 977
Thoughts: **Analyzing the NCI Driver Null Pointer Dereference**
Okay, so here's the situation. I've been tracing the execution path and it looks like we have a null pointer dereference, and it's most likely in `usb.c` (and possibly `spi.c` too). Let me break it down.
Essentially, `nfcmrvl_probe` calls `nfcmrvl_nci_register_dev`, registering the NCI device, which then exposes it to userspace. Userspace can then immediately try to open this newly registered device, which triggers `nfcmrvl_usb_nci_open`. Within `nfcmrvl_usb_nci_open`, a USB URB is submitted. If that URB completes immediately, which is entirely possible, `nfcmrvl_bulk_complete` gets called. Now, here's the problem: inside `nfcmrvl_bulk_complete`, we dereference `drv_data->priv->ndev`. But `drv_data->priv` is *not* set until after `nfcmrvl_nci_register_dev` returns, which happens *within* `nfcmrvl_probe`. Thus, `drv_data->priv` is NULL when the URB completes right away, and a null pointer dereference occurs.
Let me confirm if there are any other similar potential issues. Looking at `nfcmrvl_tx_complete`, things look okay. We get `priv` from `ndev`, so that's fine. However, checking `nfcmrvl_bulk_complete` again:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_usb_drv_data *drv_data = urb->context;
...
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
GFP_ATOMIC);
```
Okay, so we get `drv_data` from `urb->context` and then use `drv_data->priv`, which is a likely source of the bug.
Now, how do we fix this? We can't easily modify `nfcmrvl_nci_register_dev` to set `drv_data->priv` directly because all it knows is the `void *drv_data`. However, and this is the key, `nfcmrvl_nci_register_dev` *does* take `drv_data` as an argument. Furthermore, `nfcmrvl_usb_nci_open` is called from `nfcmrvl_probe`, and importantly, it also *receives* the `priv` pointer. So, in `nfcmrvl_usb_nci_open` we could simply assign `drv_data->priv = priv;`. Since `nfcmrvl_usb_nci_open` is the very first function called from userspace, and the only place where URBs are submitted, we can be certain that by the time `nfcmrvl_bulk_complete` is called, `drv_data->priv` will already have been initialized.
Checking `nfcmrvl_usb_nci_send` shows that URBs are *not* submitted in any other function so our fix is, indeed, safe.
|
| 63/2 |
2026/08/26 14:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c Name:nfcmrvl_usb_nci_send]
Results: map[SourceCode: 220: static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
221: struct sk_buff *skb)
222: {
223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
224: struct urb *urb;
225: unsigned int pipe;
226: int err;
227:
228: if (!drv_data->bulk_tx_ep)
229: return -ENODEV;
230:
231: urb = usb_alloc_urb(0, GFP_ATOMIC);
232: if (!urb)
233: return -ENOMEM;
234:
235: pipe = usb_sndbulkpipe(drv_data->udev,
236: drv_data->bulk_tx_ep->bEndpointAddress);
237:
238: usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
239: nfcmrvl_tx_complete, skb);
240:
241: err = nfcmrvl_inc_tx(drv_data);
242: if (err) {
243: usb_anchor_urb(urb, &drv_data->deferred);
244: schedule_work(&drv_data->waker);
245: err = 0;
246: goto done;
247: }
248:
249: usb_anchor_urb(urb, &drv_data->tx_anchor);
250:
251: err = usb_submit_urb(urb, GFP_ATOMIC);
252: if (err) {
253: if (err != -EPERM && err != -ENODEV)
254: nfc_err(&drv_data->udev->dev,
255: "urb %p submission failed (%d)\n", urb, -err);
256: kfree(urb->setup_packet);
257: usb_unanchor_urb(urb);
258: } else {
259: usb_mark_last_busy(drv_data->udev);
260: }
261:
262: done:
263: usb_free_urb(urb);
264: return err;
265: }
SourceFile:drivers/nfc/nfcmrvl/usb.c]
|
| 64/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 48095
output: 25
thoughts: 249
Thoughts: **My Analysis of the `nfcmrvl` Driver Initialization**
Okay, so I'm looking at this `nfcmrvl` driver and trying to figure out the cleanest way to handle the `priv` data within the `nfcmrvl_usb_nci_send` function. I see that `nfcmrvl_usb_nci_send` *does* take `priv`, and it *does* retrieve `drv_data`. My initial thought was, since `nfcmrvl_usb_nci_send` is called only after `nfcmrvl_usb_nci_open` has completed successfully, that I could simply assign `drv_data->priv = priv;` in the `nfcmrvl_usb_nci_open` function. That seems like it would work.
But, you know, it feels a bit sloppy. I'm always striving for elegance here, and that felt... less than ideal.
Then I had a different idea: wouldn't it be cleaner to just tweak `nfcmrvl_nci_register_dev`? Specifically, if I could modify it to prevent the device from being "exposed" until after the caller has completed its own initialization, that might be better. Wait a minute! The last thing `nfcmrvl_nci_register_dev` does is call `nci_register_device`.
Hmm... what if I changed `nfcmrvl_nci_register_dev` to return the `priv` data *without* calling `nci_register_device` itself? Then, the caller could call `nci_register_device` later, after it's fully initialized. That would allow for more flexibility. The downside, of course, is that I'd need to modify *all* the drivers – USB, I2C, SPI, and UART – since they all use `nfcmrvl_nci_register_dev`. It's a trade-off: a cleaner design versus broader impact. Let's start by figuring out precisely how many drivers are using `nfcmrvl_nci_register_dev`.
|
| 65/2 |
2026/08/26 14:14 |
tool |
grepper |
0m
Args: map[Expression:nfcmrvl_nci_register_dev]
Results: map[Output:drivers/nfc/nfcmrvl/i2c.c=186=static int nfcmrvl_i2c_probe(struct i2c_client *client)
--
drivers/nfc/nfcmrvl/i2c.c-227-
drivers/nfc/nfcmrvl/i2c.c:228: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
drivers/nfc/nfcmrvl/i2c.c-229- drv_data, &i2c_ops,
--
drivers/nfc/nfcmrvl/main.c=84=static const struct nci_ops nfcmrvl_nci_ops = {
--
drivers/nfc/nfcmrvl/main.c-91-
drivers/nfc/nfcmrvl/main.c:92:struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
drivers/nfc/nfcmrvl/main.c-93- void *drv_data,
--
drivers/nfc/nfcmrvl/main.c-176-}
drivers/nfc/nfcmrvl/main.c:177:EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
drivers/nfc/nfcmrvl/main.c-178-
--
drivers/nfc/nfcmrvl/nfcmrvl.h=120=int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
drivers/nfc/nfcmrvl/nfcmrvl.h:121:struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
drivers/nfc/nfcmrvl/nfcmrvl.h-122- void *drv_data,
--
drivers/nfc/nfcmrvl/spi.c=127=static int nfcmrvl_spi_probe(struct spi_device *spi)
--
drivers/nfc/nfcmrvl/spi.c-159-
drivers/nfc/nfcmrvl/spi.c:160: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
drivers/nfc/nfcmrvl/spi.c-161- drv_data, &spi_ops,
--
drivers/nfc/nfcmrvl/uart.c=108=static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
--
drivers/nfc/nfcmrvl/uart.c-133-
drivers/nfc/nfcmrvl/uart.c:134: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
drivers/nfc/nfcmrvl/uart.c-135- dev, pdata);
--
drivers/nfc/nfcmrvl/usb.c=286=static int nfcmrvl_probe(struct usb_interface *intf,
--
drivers/nfc/nfcmrvl/usb.c-319-
drivers/nfc/nfcmrvl/usb.c:320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
drivers/nfc/nfcmrvl/usb.c-321- &intf->dev, &config);
]
|
| 66/2 |
2026/08/26 14:14 |
llm |
debugger |
0m
Model:
Tokens:
input: 49751
output: 51
thoughts: 5206
Thoughts: **Analysis of the NFC Driver Initialization and Potential Race Conditions**
Okay, here's my breakdown of this. We've got a classic problem where initialization order creates a race condition, leading to a null pointer dereference. The core issue is the timing of `nci_register_device` and the interaction with the hardware.
Initially, I see the code calls `nfcmrvl_nci_register_dev`, which essentially registers the NFC device with the NCI core. The challenge arises because this registration exposes the device to userspace *before* the driver has fully finished its setup. Specifically, the interrupt request in `spi.c` might fire immediately after `devm_request_threaded_irq` but *before* `nfcmrvl_nci_register_dev` is called.
My first thought was to pass a pointer to `priv`'s pointer to `nfcmrvl_nci_register_dev` to set `priv`. This looks neat and clean, but there's a problem: it doesn't solve the issue. In fact, if `nfcmrvl_nci_register_dev` is meant to be exported, it could be called even *before* it gets a chance to even *initialize* `priv`.
In the case of `usb.c`, the completion handler for URBs (`nfcmrvl_bulk_complete`) is called *after* `nci_register_device`. This means userspace can trigger data transfer via `nci_dev_up` which calls `nfcmrvl_usb_nci_open` which then submits the URB. This calls the completion handler, and we are vulnerable to a null pointer dereference if `drv_data->priv` is null because it isn't set before submission.
In my analysis, `i2c.c` cleverly checks for this null pointer in its IRQ handler, which prevents the crash. `spi.c` must do the same.
In `usb.c`, the crash happens in `nfcmrvl_bulk_complete` because the code assumes `drv_data->priv` is already set. But that's not always true if there's a race between device registration and userspace interaction. The simplest fix here would be to set `drv_data->priv = priv;` *at the very beginning* of `nfcmrvl_usb_nci_open`. This ensures `drv_data->priv` is valid when the completion handler runs. This is the safest method. I verified that setting `drv_data->priv = priv` at the beginning of `nfcmrvl_usb_nci_open` will fix the problem. I checked that no other functions in `usb.c` use `drv_data->priv` before `nfcmrvl_usb_nci_open` sets it.
Wait, looking at `nfcmrvl_usb_nci_open` again: If we set `drv_data->priv = priv;` before the USB autopm code, we *guarantee* that `drv_data->priv` is set before we try to use it in `nfcmrvl_bulk_complete`. This solves the issue.
Also, the code submits *two* URBs. If the first completes instantly, the completion handler returns *without resubmitting* (because a flag isn't set yet). This might be a separate bug, leading to only one active URB instead of two. However, the root cause for the crash is always addressed if we fix the NULL check in `nfcmrvl_bulk_complete`.
Finally, the code in `nfcmrvl_resume` has a potential bug. It mistakenly uses `drv_data->flags` (a local variable) to check whether `NFCMRVL_NCI_RUNNING` is set. This should use a bit on `priv->flags` instead. This does not cause a crash.
So, setting `drv_data->priv = priv;` at the beginning of `nfcmrvl_usb_nci_open` is the cleanest and most effective solution to this race condition and fixes the null pointer dereference.
|
| 67/2 |
2026/08/26 14:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/fw_dnld.c Name:nfcmrvl_fw_dnld_init]
Results: map[SourceCode: 444: int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
445: {
446: char name[32];
447:
448: INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
449: snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
450: dev_name(&priv->ndev->nfc_dev->dev));
451: priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
452: if (!priv->fw_dnld.rx_wq)
453: return -ENOMEM;
454: skb_queue_head_init(&priv->fw_dnld.rx_q);
455: return 0;
456: }
SourceFile:drivers/nfc/nfcmrvl/fw_dnld.c]
|
| 68/2 |
2026/08/26 14:15 |
llm |
debugger |
0m
Model:
Tokens:
input: 56202
output: 20
thoughts: 34
Thoughts: Okay, here's my interpretation of those thoughts, presented as a first-person expert summary:
**Initializing the Download Process**
Alright, let me just double-check what's going on here. The code just called `nfcmrvl_fw_dnld_init`. Okay, yeah, that just sets up the workqueue, nothing too exciting. That's a pretty standard initialization step for asynchronous tasks. Now, the real question is, where does this get *used*? I need to trace this further to figure out where the actual firmware download logic is triggered and how it's connected to the workqueue. `support_fw_dnld`… is that another function or a variable? I need to find its calls or references to understand its role in this whole process. Where's this going?
|
| 69/2 |
2026/08/26 14:16 |
tool |
grepper |
0m
Args: map[Expression:support_fw_dnld]
Results: map[Output:drivers/nfc/nfcmrvl/fw_dnld.c=483=int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
--
drivers/nfc/nfcmrvl/fw_dnld.c-488-
drivers/nfc/nfcmrvl/fw_dnld.c:489: if (!priv->support_fw_dnld)
drivers/nfc/nfcmrvl/fw_dnld.c-490- return -ENOTSUPP;
--
drivers/nfc/nfcmrvl/i2c.c=186=static int nfcmrvl_i2c_probe(struct i2c_client *client)
--
drivers/nfc/nfcmrvl/i2c.c-234-
drivers/nfc/nfcmrvl/i2c.c:235: drv_data->priv->support_fw_dnld = true;
drivers/nfc/nfcmrvl/i2c.c-236-
--
drivers/nfc/nfcmrvl/nfcmrvl.h=81=struct nfcmrvl_private {
--
drivers/nfc/nfcmrvl/nfcmrvl.h-94- /* FW download support */
drivers/nfc/nfcmrvl/nfcmrvl.h:95: bool support_fw_dnld;
drivers/nfc/nfcmrvl/nfcmrvl.h-96-
--
drivers/nfc/nfcmrvl/spi.c=127=static int nfcmrvl_spi_probe(struct spi_device *spi)
--
drivers/nfc/nfcmrvl/spi.c-166-
drivers/nfc/nfcmrvl/spi.c:167: drv_data->priv->support_fw_dnld = true;
drivers/nfc/nfcmrvl/spi.c-168-
--
drivers/nfc/nfcmrvl/uart.c=108=static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
--
drivers/nfc/nfcmrvl/uart.c-138-
drivers/nfc/nfcmrvl/uart.c:139: priv->support_fw_dnld = true;
drivers/nfc/nfcmrvl/uart.c-140-
--
drivers/nfc/nfcmrvl/usb.c=286=static int nfcmrvl_probe(struct usb_interface *intf,
--
drivers/nfc/nfcmrvl/usb.c-325- drv_data->priv = priv;
drivers/nfc/nfcmrvl/usb.c:326: drv_data->priv->support_fw_dnld = false;
drivers/nfc/nfcmrvl/usb.c-327-
]
|
| 70/2 |
2026/08/26 14:16 |
llm |
debugger |
0m
Model:
Tokens:
input: 57167
output: 46
thoughts: 609
Thoughts: **Analyzing a Potential Race Condition in NFC Firmware Download**
Okay, so I'm digging into this firmware download logic, and it seems like there's a possible race condition here. It's a bit subtle, but here's what I'm seeing: `support_fw_dnld` is initially `false` because `priv` is allocated using `kzalloc`. That's good, because it's safe if userspace *tries* to download the firmware before things are fully set up. However, looking closely at the `i2c.c`, `spi.c`, and `uart.c` drivers, I see that `support_fw_dnld` is set to `true` *after* `nfcmrvl_nci_register_dev` is called!
So, if userspace is too eager and tries a firmware download immediately after the device initialization, it might get a `-ENOTSUPP` error. Because, at that point, `support_fw_dnld` is still `false`. I can see this happening because `nfc_fw_download` ultimately calls `ndev->ops->fw_download`, which then calls `nfcmrvl_fw_dnld_start`. This function, of course, checks `priv->support_fw_dnld`. If it's false, `-ENOTSUPP` is returned. Yep, that confirms it: userspace could be getting a premature rejection.
This reinforces my understanding that `nci_register_device` should really be called *last*, after all the critical initialization is completed. Alright, how do we fix this? I see a couple of ways to approach this. I could split `nfcmrvl_nci_register_dev` into two functions: one for allocation and another for registration. Alternatively, perhaps I could just pass `support_fw_dnld` and `priv_ptr` as arguments to `nfcmrvl_nci_register_dev`?
Hmm, but changing the function signature means updating all four drivers! That could be a lot of work. Is there a simpler approach? What if I decouple the `nci_register_device` call from `nfcmrvl_nci_register_dev`? I could rename the function to something like `nfcmrvl_nci_setup_dev`, but that means making callers responsible for calling `nci_register_device`. But wait, `nci_register_device` requires `priv->ndev`, but is that available to callers? Let's check... Aha! `priv` is returned, and `priv->ndev` is a public field in `struct nfcmrvl_private`. Yes, that makes it accessible. I'll need to double-check `struct nfcmrvl_private`, but this might be the cleanest solution.
|
| 71/2 |
2026/08/26 14:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/nfcmrvl.h Name:nfcmrvl_private]
Results: map[SourceCode: 81: struct nfcmrvl_private {
82:
83: unsigned long flags;
84:
85: /* Platform configuration */
86: struct nfcmrvl_platform_data config;
87:
88: /* Parent dev */
89: struct nci_dev *ndev;
90:
91: /* FW download context */
92: struct nfcmrvl_fw_dnld fw_dnld;
93:
94: /* FW download support */
95: bool support_fw_dnld;
96:
97: /*
98: * PHY related information
99: */
100:
101: /* PHY driver context */
102: void *drv_data;
103: /* PHY device */
104: struct device *dev;
105: /* PHY type */
106: enum nfcmrvl_phy phy;
107: /* Low level driver ops */
108: const struct nfcmrvl_if_ops *if_ops;
109: };
SourceFile:drivers/nfc/nfcmrvl/nfcmrvl.h]
|
| 72/2 |
2026/08/26 14:16 |
llm |
debugger |
0m
Model:
Tokens:
input: 58786
output: 48
thoughts: 123
Thoughts: Here's my thinking:
**Refactoring the NFC Driver**
Okay, so I see that `ndev` is accessible, which is a good starting point. My initial thought is that we could simplify the `nfcmrvl_nci_register_dev` function by simply removing the call to `nci_register_device`. Perhaps, we could even rename it to something like `nfcmrvl_nci_allocate_dev`, better reflecting its new primary function.
However, hold on a second. If we strip out that `nci_register_device` call, then we'll also have to address the consequence. Wait a moment, I think we have to remove the `nfcmrvl_chip_halt(priv);` call. Let me revisit the original function and make sure I haven't missed anything.
|
| 73/2 |
2026/08/26 14:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/main.c Name:nfcmrvl_nci_register_dev]
Results: map[SourceCode: 92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
101: int tailroom;
102: u32 protocols;
103:
104: priv = kzalloc_obj(*priv);
105: if (!priv)
106: return ERR_PTR(-ENOMEM);
107:
108: priv->drv_data = drv_data;
109: priv->if_ops = ops;
110: priv->dev = dev;
111: priv->phy = phy;
112:
113: memcpy(&priv->config, pdata, sizeof(*pdata));
114:
115: if (!priv->config.reset_gpio) {
116: priv->config.reset_gpio =
117: devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
118: if (IS_ERR(priv->config.reset_gpio)) {
119: priv->config.reset_gpio = NULL;
120: nfc_err(dev, "failed to get reset gpio\n");
121: }
122: }
123:
124: if (phy == NFCMRVL_PHY_SPI) {
125: headroom = NCI_SPI_HDR_LEN;
126: tailroom = 1;
127: } else
128: headroom = tailroom = 0;
129:
130: if (priv->config.hci_muxed)
131: headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
132:
133: protocols = NFC_PROTO_JEWEL_MASK
134: | NFC_PROTO_MIFARE_MASK
135: | NFC_PROTO_FELICA_MASK
136: | NFC_PROTO_ISO14443_MASK
137: | NFC_PROTO_ISO14443_B_MASK
138: | NFC_PROTO_ISO15693_MASK
139: | NFC_PROTO_NFC_DEP_MASK;
140:
141: priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
142: headroom, tailroom);
143: if (!priv->ndev) {
144: nfc_err(dev, "nci_allocate_device failed\n");
145: rc = -ENOMEM;
146: goto error_free;
147: }
148:
149: rc = nfcmrvl_fw_dnld_init(priv);
150: if (rc) {
151: nfc_err(dev, "failed to initialize FW download %d\n", rc);
152: goto error_free_dev;
153: }
154:
155: nci_set_drvdata(priv->ndev, priv);
156:
157: rc = nci_register_device(priv->ndev);
158: if (rc) {
159: nfc_err(dev, "nci_register_device failed %d\n", rc);
160: goto error_fw_dnld_deinit;
161: }
162:
163: /* Ensure that controller is powered off */
164: nfcmrvl_chip_halt(priv);
165:
166: nfc_info(dev, "registered with nci successfully\n");
167: return priv;
168:
169: error_fw_dnld_deinit:
170: nfcmrvl_fw_dnld_deinit(priv);
171: error_free_dev:
172: nci_free_device(priv->ndev);
173: error_free:
174: kfree(priv);
175: return ERR_PTR(rc);
176: }
SourceFile:drivers/nfc/nfcmrvl/main.c]
|
| 74/2 |
2026/08/26 14:16 |
llm |
debugger |
1m
Model:
Tokens:
input: 60437
output: 1393
thoughts: 10140
Thoughts: **Root Cause Analysis and Proposed Fix for NULL Pointer Dereference in `nfcmrvl_bulk_complete` (drivers/nfc/nfcmrvl/usb.c)**
The crash is a null-pointer dereference in the `nfcmrvl_bulk_complete` function, specifically when accessing `drv_data->priv->ndev`. This occurs because of a race condition between device initialization in `nfcmrvl_probe` and userspace interaction (device open). The `priv` structure (containing device-specific data) is allocated and registered in `nfcmrvl_nci_register_dev`, which makes the NCI device available to userspace. However, `drv_data->priv = priv;` (where `drv_data` is part of the USB-specific driver data) is only set later in `nfcmrvl_probe`, *after* the device has been registered, creating a race condition.
If userspace attempts to open the device (e.g., using `nfc_dev_up`) *before* `nfcmrvl_probe` has fully completed (i.e., before `drv_data->priv` is set), the USB interface open function (`nfcmrvl_usb_nci_open`) is triggered. This function submits a bulk URB (USB Request Block). If this URB completes immediately (which is possible in scenarios like the use of a "dummy" HCD or other edge cases), `nfcmrvl_bulk_complete` is called *before* `drv_data->priv` is initialized. Because `drv_data->priv` is NULL, any access to it within `nfcmrvl_bulk_complete` (such as `drv_data->priv->ndev`) leads to a null-pointer dereference and a kernel crash.
This issue is exacerbated by a secondary bug (effectively a typo) in the code. In `nfcmrvl_bulk_complete` and `nfcmrvl_resume`, there's a check `if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))`. However, `NFCMRVL_NCI_RUNNING` is defined as `1` and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1`. Due to this unfortunate coincidence, the code *accidentally* checks `&drv_data->flags` (for the bit representing `NFCMRVL_USB_BULK_RUNNING`) instead of `&priv->flags` for `NFCMRVL_NCI_RUNNING`. The result is the data in the URB is not processed until the `NFCMRVL_USB_BULK_RUNNING` flag is set (the second URB submission), which triggers the crash. The initial URB completion is skipped due to the mis-identified check, *and* the crash only occurs when the second URB submission is processed, precisely because `drv_data->priv` is still NULL.
To resolve both the race condition and this logic/typo bug, the following changes are proposed:
1. **Modify `nfcmrvl_submit_bulk_urb()`**: Change this function to accept `struct nfcmrvl_private *priv` as its primary argument, instead of `struct nfcmrvl_usb_drv_data *drv_data`. Inside this function, derive the `drv_data` from `priv->drv_data`. The bulk URB's context is set to `priv`.
2. **`nfcmrvl_bulk_complete()` Updates**: Inside `nfcmrvl_bulk_complete`, retrieve `struct nfcmrvl_private *priv = urb->context`. Then derive `drv_data` using `priv->drv_data`. By using `priv` (which is already valid when `nfcmrvl_usb_nci_open` is called), the null-pointer dereference is completely avoided.
3. **Correct the Flag Checks**: Change the checks inside `nfcmrvl_bulk_complete` and `nfcmrvl_resume` to check `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`. This ensures that processing is handled correctly.
These changes make the code more robust and correct by ensuring that required device information is always available, independent of timing issues. This also resolves the unintended conditional early returns. Other PHYs like `spi.c` might experience similar issues, and the same root causes will apply. The recommended solution should address these issues.
|
| 75/1 |
2026/08/26 14:18 |
agent |
history-explorer |
27m
Model:
Instruction:
You are an experienced Linux kernel developer researching prior art for fixing a kernel bug.
You are given a bug explanation. This explanation details the root cause of the bug resulting
from debugging, but does not provide the final fix strategy. Your goal is to explore how
similar bugs were fixed in the past in the same subsystem or files.
CRITICAL: Do NOT attempt to debug the issue further or write a patch for it yourself.
Your ONLY objective is to research and provide the necessary historical context.
Use the git-log tool with the Since parameter set to "3 years" to focus on recent history.
Search for commits that address issues with similar root causes (e.g. similar missing locks,
incorrect refcounting, or similar error path bugs) in the affected files.
Your final reply must summarize your findings: what idioms, locking rules, or common patterns
should be followed when writing a fix for this bug based on how previous similar bugs were addressed.
If you find no relevant past fixes, clearly state that.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The explanation of the root cause of the bug is:
The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
Reply: Based on the historical context and similar bugs in the NFC subsystem, the root cause of this bug is a classic **"register before initialize"** race condition. The device is exposed to userspace (via `nci_register_device()`) before its driver data structures are fully initialized (`drv_data->priv = priv`).
Here is a summary of how similar bugs were addressed in the past and the idioms that should be followed for the fix:
### 1. Reordering Initialization vs. Passing Valid Context
In the past, similar race conditions in the NFC subsystem were fixed by strictly reordering the initialization sequence so that the device or IRQ is only registered *after* all data structures are fully set up.
* For example, in commit `32ecc75ded72` ("NFC: pn533: change order operations in dev registation"), a NULL pointer dereference in `pn533_i2c` was fixed by moving `request_threaded_irq()` to happen *after* `phy->priv` was assigned.
* Similarly, commit `d270453a0d9e` ("nfc: nfcmrvl: main: reorder destructive operations...") fixed race conditions during device unregistration by strictly reordering operations relative to `nci_unregister_device()`.
However, in `nfcmrvl/usb.c`, reordering is difficult because `nci_register_device()` is called deep inside `nfcmrvl_nci_register_dev()`, which allocates and returns `priv`. Therefore, the proposed fix of **passing the fully initialized `priv` structure as the URB context** (instead of the partially initialized `drv_data`) is the correct and robust idiom here. It ensures the asynchronous callback (`nfcmrvl_bulk_complete`) always operates on valid data, completely bypassing the race window.
### 2. Early IRQ Handler Checks (SPI/I2C)
For interfaces where hardware interrupts might fire before the driver is fully bound (like SPI or I2C), the established pattern in this subsystem is to add an early return check at the top of the IRQ handler.
* If you look at `drivers/nfc/nfcmrvl/i2c.c`, this exact pattern is already implemented in `nfcmrvl_i2c_int_irq_thread_fn()`:
```c
if (!drv_data->priv)
return IRQ_HANDLED;
```
* As noted in the bug explanation, applying this exact same check to `nfcmrvl_spi_int_irq_thread_fn()` in `drivers/nfc/nfcmrvl/spi.c` is the correct historical idiom to prevent the identical race condition on the SPI PHY.
### 3. Fixing the Macro Collision Typo
The accidental check of `NFCMRVL_NCI_RUNNING` against `drv_data->flags` (instead of `priv->flags`) is a logic error that masked the bug on the first URB. Fixing this typo to correctly check `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)` is necessary and aligns with how the flags were originally intended to be separated between the PHY layer (`drv_data->flags`) and the core layer (`priv->flags`).
### Conclusion
The proposed fix strategy is perfectly aligned with prior art in the subsystem. You should proceed with:
1. Changing `nfcmrvl_submit_bulk_urb()` to take `priv` and pass it as the URB context to `usb_fill_bulk_urb()`.
2. Retrieving `priv` from `urb->context` in `nfcmrvl_bulk_complete()`.
3. Fixing the `NFCMRVL_NCI_RUNNING` typo to check `priv->flags`.
4. Adding the `if (!drv_data->priv) return IRQ_HANDLED;` safeguard to the SPI IRQ handler, mirroring the existing code in `i2c.c`.
|
| 76/2 |
2026/08/26 14:18 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 10188
output: 33
thoughts: 393
Thoughts: **Analyzing a Race Condition in the NFC Marvell Driver**
Okay, so I've got a tricky bug to tackle – a race condition in the `nfcmrvl_probe()` function within the `drivers/nfc/nfcmrvl/usb.c` file. The core problem is that `nfcmrvl_nci_register_dev()` registers the device, exposing it to userspace before we're sure our driver data is fully initialized. Specifically, `drv_data->priv = priv;` happens *after* the device is visible to userspace. If userspace gets to the device first via something like `nfcmrvl_usb_nci_open()`, it can submit URBs, and the completion handler `nfcmrvl_bulk_complete()` subsequently tries to use `drv_data->priv` which hasn't been set yet, leading to a NULL pointer dereference. The suggested fix is to pass `priv` as the URB context instead of `drv_data`, which makes perfect sense. I also spotted a typo in the explanation that checks `NFCMRVL_NCI_RUNNING` against `drv_data->flags` instead of `priv->flags`, and a similar check on `drv_data->priv` in `spi.c` that needs to be reviewed as well.
My immediate next step is to get familiar with how similar issues have been addressed in the past within this subsystem. I need to understand the idioms, locking strategies, and common patterns that the developers here have used to prevent these kinds of race conditions. I'm going to leverage `git-log` with a "Since=3 years" filter to pinpoint relevant commits in the `drivers/nfc/nfcmrvl/` and `drivers/nfc/` directories. I'll be searching for commits that address bugs with similar root causes: missing locks, incorrect reference counting, or issues related to data initialization in the face of possible concurrent access. This historical analysis should provide a solid foundation for crafting a robust and idiomatic solution for this current bug. The goal is not just to fix the problem, but to do so in a way that aligns with the established practices of this code base, so I don't introduce new problems or deviate from the project's standards.
|
| 77/2 |
2026/08/26 14:18 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/nfc/nfcmrvl/ Since:3 years]
Results: map[Output:567ab7727adb nfc: Use named initializers for struct i2c_device_id
dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
c49a9eb650d5 NFC: nfcmrvl: Replace strcpy() with strscpy()
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
41cb08555c41 treewide, timers: Rename from_timer() to timer_container_of()
8fa7292fee5c treewide: Switch/rename to timer_delete[_sync]()
a26892ee1297 nfc: mrvl: Don't use "proxy" headers
5f60d5f6bbc1 move asm/unaligned.h to linux/unaligned.h
]
|
| 78/2 |
2026/08/26 14:18 |
llm |
history-explorer |
1m
Model:
Tokens:
input: 11373
output: 36
thoughts: 54
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Investigating Potential NFC Race Conditions**
Alright, so the first thing I'm doing is checking a specific area, `drivers/nfc/nfcmrvl/`, for any recent commits related to race conditions or actual bugs. Three years is a pretty good window. Nothing. Okay, that's good news on the surface. Now, I'm thinking, this doesn't mean we're completely in the clear. I need to broaden the search. I'll expand it to the broader NFC driver space, focusing on `drivers/nfc/` generally, and also to the networking-specific NFC code in `net/nfc/`. This seems like a reasonable way to cover a wider potential issue in the subsystem. I'm going to look for any similar problems that *might* exist in other, related parts of the NFC stack, even if the Marvell-specific driver is clean. Gotta be thorough.
|
| 79/2 |
2026/08/26 14:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race] PathPrefix:drivers/nfc/ Since:3 years]
Results: map[Output:febccb39255f nfc: pn533: Add poll mod list filling check
]
|
| 80/2 |
2026/08/26 14:19 |
llm |
history-explorer |
2m
Model:
Tokens:
input: 11711
output: 36
thoughts: 15
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Race Condition Hunting in net/nfc/**
Okay, time to dive in. My immediate thought is, "Let's scrutinize `net/nfc/` for any lurking race conditions." I know this area can be a hotbed for these types of bugs, especially given the interactions with hardware and potential multi-threaded access patterns. I'll need to go beyond the usual static analysis; I'll be thinking about concurrent access to shared data structures within the NFC subsystem. This could involve driver code, core NFC functionality, or even interactions with other kernel modules that might touch the same resources. I'll mentally map out the key data structures and function calls, looking for areas where a read/write conflict is possible, or where a missing lock or a flawed locking scheme might lead to unpredictable behavior. I'll need to consider interrupt context, workqueues, and any asynchronous operations that might be in play. I'll be looking for subtle timing issues that might only surface under specific load conditions or with particular hardware configurations. I'll keep in mind any known patterns or common race conditions I've encountered in similar areas of the kernel, and I'll be mentally filing this under "high priority."
|
| 81/2 |
2026/08/26 14:21 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race] PathPrefix:net/nfc/ Since:3 years]
Results: map[Output:b493ea2765cc nfc: llcp: Fix use-after-free race in nfc_llcp_recv_cc()
d793458c45df nfc: rawsock: cancel tx_work before socket teardown
66083581945b nfc: nci: complete pending data exchange on device close
7bd4b0c4779f nfc: nci: free skb on nci_transceive early error paths
c9efde1e537b nfc: hci: shdlc: Stop timers and work before freeing context
d2492688bb9f nfc: nci: Fix race between rfkill and nci_unregister_device().
165c34fb6068 nfc: llcp: Fix memleak in nfc_llcp_send_ui_frame().
7a87441c9651 nfc: llcp: fix nfc_llcp_setsockopt() unsafe copies
31c07dffafce net: nfc: fix races in nfc_llcp_sock_get() and nfc_llcp_sock_get_sn()
]
|
| 82/2 |
2026/08/26 14:21 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 12327
output: 36
thoughts: 25
Thoughts: Here's a summary of my thought process, tailored for an expert like you:
**Diving into the NFC Driver Code**
Alright, time to dig into the `drivers/nfc/` directory. Given my experience, I'm immediately thinking about potential vulnerabilities within this NFC driver code. My focus is going to be laser-sharp on pinpointing any instances of use-after-free conditions or null pointer dereferences. These are classic and often devastating bugs, especially in kernel-level code, and I want to make sure this driver is rock solid. I'll be meticulously examining the memory management, pointer assignments, and any asynchronous operations to root them out. I'm going to be looking for race conditions and any code paths that might lead to unexpected behavior. Let's see what we can find.
|
| 83/2 |
2026/08/26 14:22 |
tool |
git-log |
0m
Args: map[MessageRegexps:[null] PathPrefix:drivers/nfc/ Since:3 years]
Results: map[Output:c71ba669b570 nfc: pn533: allocate rx skb before consuming bytes
5c14a19d5b16 nfc: s3fwrn5: allocate rx skb before consuming bytes
21a8a77abb4c nfc: st21nfca: Drop unneeded null check in st21nfca_tx_work()
]
|
| 84/2 |
2026/08/26 14:22 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 12663
output: 40
thoughts: 24
Thoughts: Here's a summary of my thought process:
**Hunting for a Use-After-Free Vulnerability in NFC Drivers**
Okay, time to dig in. I need to find potential vulnerabilities, and since I'm focusing on memory corruption, "use-after-free" (UAF) is a good target. I'll start with the obvious and check the `drivers/nfc/` directory. This seems like a reasonable place to start, as NFC drivers can be complex and often interface with external hardware, increasing the likelihood of subtle bugs. I'm going to look specifically for places where memory might be freed and then later accessed. Time to get to work.
|
| 85/2 |
2026/08/26 14:23 |
tool |
git-log |
0m
Args: map[MessageRegexps:[use-after-free] PathPrefix:drivers/nfc/ Since:3 years]
Results:
|
| 86/2 |
2026/08/26 14:23 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 12881
output: 36
thoughts: 15
Thoughts: Here's my thought process, as if I were considering this investigation:
**Investigating a Potential NFC Driver Crash**
Okay, so we've got a crash, and given my expertise, the NFC subsystem is a likely candidate. My first instinct, as always, is to start with the most relevant code and data. A "crash" usually indicates a problem at the driver level, a hardware interaction issue, or a resource management failure. Considering the complexity of NFC, with its hardware dependencies and potential race conditions, the `drivers/nfc/` directory is the logical place to begin my investigation. I'll need to meticulously examine the code within that directory for any recent changes that might correlate with the reported crash, paying close attention to error handling, interrupt routines, memory allocation/deallocation, and potential locking issues. I'll be particularly vigilant for any potential null pointer dereferences, buffer overflows, or improper use of atomic operations. Essentially, I'm expecting to find the most directly involved and relevant code in that directory.
|
| 87/2 |
2026/08/26 14:24 |
tool |
git-log |
0m
Args: map[MessageRegexps:[crash] PathPrefix:drivers/nfc/ Since:3 years]
Results:
|
| 88/2 |
2026/08/26 14:24 |
llm |
history-explorer |
1m
Model:
Tokens:
input: 13158
output: 37
thoughts: 16
Thoughts: Here's my thought process, as if I were considering this issue:
**Investigating Potential Dereference Issues in NFC Drivers**
Okay, so I need to hunt down potential dereference bugs within the NFC driver code. My first instinct is to dive into the `drivers/nfc/` directory. That's the obvious place to start, given the problem domain. I need to be methodical here. I'll need to use a tool like grep or ripgrep, or maybe even my IDE's search, to look specifically for instances of "deref". That term, "deref", is a good starting point because it's a generally accepted shorthand when talking about dereferencing pointers, and thus a good trigger phrase for finding potential pointer-related errors. From there, I'll need to carefully review the results, examining each instance in its context to determine if it's a legitimate risk of a null pointer dereference, a use-after-free, or some other memory safety issue. I'll have to pay attention to how pointers are being initialized, checked, and used. This could take a while, but it's crucial to ensure the NFC drivers are robust and secure.
|
| 89/2 |
2026/08/26 14:25 |
tool |
git-log |
0m
Args: map[MessageRegexps:[deref] PathPrefix:drivers/nfc/ Since:3 years]
Results: map[Output:c71ba669b570 nfc: pn533: allocate rx skb before consuming bytes
5c14a19d5b16 nfc: s3fwrn5: allocate rx skb before consuming bytes
]
|
| 90/2 |
2026/08/26 14:25 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 13531
output: 36
thoughts: 15
Thoughts: Here's my thought process, as if I were considering this issue:
**Investigating NFC Driver Unregistration**
Okay, so I need to figure out where the NFC drivers are being unregistered. Given what I know about kernel structure, the logical starting point is probably the `drivers/nfc/` directory. That's where I'd expect to find the core NFC driver code, and therefore, where the unregistration function or functions related to that process would most likely reside. I'll need to do a thorough search within that directory and its subdirectories, focusing on the instances of "unregister" - that's my keyword. This should narrow down the search and point me towards the relevant source files. Once I've found those files, I can start analyzing the unregistration logic and figure out why it's happening, or whether there's a problem there.
|
| 91/2 |
2026/08/26 14:25 |
tool |
git-log |
0m
Args: map[MessageRegexps:[unregister] PathPrefix:drivers/nfc/ Since:3 years]
Results:
|
| 92/2 |
2026/08/26 14:25 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 13777
output: 36
thoughts: 15
Thoughts: Here's my thought process, as if I were considering this:
**Investigating NFC Driver Registration**
Okay, so I need to find where the NFC drivers are being registered within the kernel. Let's start by looking in the `drivers/nfc/` directory. That's the logical place to begin, since it's the dedicated location for NFC-related code. I'm specifically looking for any kind of registration mechanisms, because that's how a driver makes itself known to the system. The word "register" is probably a good search term to use. It’ll help me quickly identify functions, macros, or structures that handle the driver's initialization and attachment to the NFC subsystem. Let's see what we find in there.
|
| 93/2 |
2026/08/26 14:26 |
tool |
git-log |
0m
Args: map[MessageRegexps:[register] PathPrefix:drivers/nfc/ Since:3 years]
Results: map[Output:5d69351820ea NFC: trf7970a: Create device-tree parameter for RX gain reduction
e93af7228605 nfc: st95hf: drop driver owner assignment
e3c95d56190c nfc: mrvl: spi: drop driver owner assignment
]
|
| 94/2 |
2026/08/26 14:26 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 14080
output: 31
thoughts: 121
Thoughts: **Debugging a Register-Before-Initialize Bug**
Okay, so here's the situation: `nci_register_device()` is being called prematurely. The driver data isn't fully set up yet – specifically, `drv_data->priv` is still null. Classic "register before initialize" issue. My gut tells me this is going to be a fun one to track down.
First instinct is to do a keyword search through the kernel source. I'll target "register" paired with either "initialize," "init," or "before" to see what comes up. That should give me a good starting point. But wait, before I dive into a generic search, I should probably check if there are any similar bugs fixed in the `drivers/nfc/` or `net/nfc/` directories. Maybe I can find a relevant precedent there, and it will give me a clue of how to approach this. It would save me a lot of time if I could leverage someone else's solution.
Let's start by zeroing in on `nci_register_device` within `drivers/nfc/`. That's the most likely area to find a pre-existing fix. Hopefully, I'll find something there to point me in the right direction.
|
| 95/2 |
2026/08/26 14:27 |
tool |
grepper |
0m
Args: map[Expression:nci_register_device PathPrefix:drivers/nfc/]
Results: map[Output:drivers/nfc/fdp/fdp.c=699=int fdp_nci_probe(struct fdp_i2c_phy *phy, const struct nfc_phy_ops *phy_ops,
--
drivers/nfc/fdp/fdp.c-737-
drivers/nfc/fdp/fdp.c:738: r = nci_register_device(ndev);
drivers/nfc/fdp/fdp.c-739- if (r)
--
drivers/nfc/nfcmrvl/main.c=92=struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
--
drivers/nfc/nfcmrvl/main.c-156-
drivers/nfc/nfcmrvl/main.c:157: rc = nci_register_device(priv->ndev);
drivers/nfc/nfcmrvl/main.c-158- if (rc) {
drivers/nfc/nfcmrvl/main.c:159: nfc_err(dev, "nci_register_device failed %d\n", rc);
drivers/nfc/nfcmrvl/main.c-160- goto error_fw_dnld_deinit;
--
drivers/nfc/nxp-nci/core.c=135=int nxp_nci_probe(void *phy_id, struct device *pdev,
--
drivers/nfc/nxp-nci/core.c-169- nci_set_drvdata(info->ndev, info);
drivers/nfc/nxp-nci/core.c:170: r = nci_register_device(info->ndev);
drivers/nfc/nxp-nci/core.c-171- if (r < 0) {
--
drivers/nfc/s3fwrn5/core.c=160=int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
--
drivers/nfc/s3fwrn5/core.c-184-
drivers/nfc/s3fwrn5/core.c:185: ret = nci_register_device(info->ndev);
drivers/nfc/s3fwrn5/core.c-186- if (ret < 0) {
--
drivers/nfc/st-nci/core.c=112=int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
--
drivers/nfc/st-nci/core.c-148-
drivers/nfc/st-nci/core.c:149: r = nci_register_device(ndlc->ndev);
drivers/nfc/st-nci/core.c-150- if (r) {
--
drivers/nfc/virtual_ncidev.c=133=static int virtual_ncidev_open(struct inode *inode, struct file *file)
--
drivers/nfc/virtual_ncidev.c-152-
drivers/nfc/virtual_ncidev.c:153: ret = nci_register_device(vdev->ndev);
drivers/nfc/virtual_ncidev.c-154- if (ret < 0) {
]
|
| 96/2 |
2026/08/26 14:27 |
llm |
history-explorer |
1m
Model:
Tokens:
input: 15371
output: 40
thoughts: 19
Thoughts: Here's a summary of my thought process, as if I were the one thinking it:
**Diving into the NFC Driver**
Okay, time to get to the heart of this. Let's start by zeroing in on the NFC driver implementation – specifically, the USB aspect, as that's likely where the interesting stuff is happening. I need to understand the interaction between the NFC controller and the USB bus. Let's see what's in `drivers/nfc/nfcmrvl/usb.c`. Hopefully, the naming conventions are logical, and I can quickly spot the relevant functions for device enumeration, data transfer, and error handling. I'm expecting to find USB descriptors, endpoint initialization, and the low-level communication logic. This is where the magic happens, so I'm keen to dissect this code. I need to get a solid grasp of how this Marvell NFC controller interacts with the system through USB.
|
| 97/2 |
2026/08/26 14:28 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-USB driver: USB interface related functions
4: *
5: * Copyright (C) 2014, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/usb.h>
10: #include <linux/nfc.h>
11: #include <net/nfc/nci.h>
12: #include <net/nfc/nci_core.h>
13: #include "nfcmrvl.h"
14:
15: static struct usb_device_id nfcmrvl_table[] = {
16: { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
17: USB_CLASS_VENDOR_SPEC, 4, 1) },
18: { } /* Terminating entry */
19: };
20:
21: MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
22:
23: #define NFCMRVL_USB_BULK_RUNNING 1
24: #define NFCMRVL_USB_SUSPENDING 2
25:
26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
30: struct work_struct waker;
31: struct usb_anchor tx_anchor;
32: struct usb_anchor bulk_anchor;
33: struct usb_anchor deferred;
34: int tx_in_flight;
35: /* protects tx_in_flight */
36: spinlock_t txlock;
37: struct usb_endpoint_descriptor *bulk_tx_ep;
38: struct usb_endpoint_descriptor *bulk_rx_ep;
39: int suspend_count;
40: struct nfcmrvl_private *priv;
41: };
42:
43: static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
44: {
45: unsigned long flags;
46: int rv;
47:
48: spin_lock_irqsave(&drv_data->txlock, flags);
49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
100: }
]
|
| 98/2 |
2026/08/26 14:28 |
llm |
history-explorer |
2m
Model:
Tokens:
input: 17098
output: 43
thoughts: 80
Thoughts: **Debugging the NFC Driver**
Okay, here's what's running through my mind. I've tracked this bug down to the `nfcmrvl_bulk_complete()` function. It seems the problem might be stemming from that `!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)` check. That's a classic suspect, a condition that could easily lead to an issue. I'm going to double-check my understanding of how this flag is supposed to work and where its defined; `NFCMRVL_NCI_RUNNING` is defined in `nfcmrvl.h`, so I'll go take a look at the header file to verify how the bit is set and cleared, as well as ensure the logic around it makes sense. I need to understand this flag's state to figure out if this conditional check is behaving as expected.
|
| 99/2 |
2026/08/26 14:31 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:1 LineCount:100]
Results: map[Contents: 1: /* SPDX-License-Identifier: GPL-2.0-only */
2: /*
3: * Marvell NFC driver
4: *
5: * Copyright (C) 2014-2015, Marvell International Ltd.
6: */
7:
8: #ifndef _NFCMRVL_H_
9: #define _NFCMRVL_H_
10:
11: #include "fw_dnld.h"
12:
13: struct gpio_desc;
14:
15: /* Define private flags: */
16: #define NFCMRVL_NCI_RUNNING 1
17: #define NFCMRVL_PHY_ERROR 2
18:
19: #define NFCMRVL_EXT_COEX_ID 0xE0
20: #define NFCMRVL_NOT_ALLOWED_ID 0xE1
21: #define NFCMRVL_ACTIVE_ID 0xE2
22: #define NFCMRVL_EXT_COEX_ENABLE 1
23: #define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA
24: #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB
25: #define NFCMRVL_NCI_MAX_EVENT_SIZE 260
26:
27: /*
28: * NCI FW Parameters
29: */
30:
31: #define NFCMRVL_PB_BAIL_OUT 0x11
32: #define NFCMRVL_PROP_REF_CLOCK 0xF0
33: #define NFCMRVL_PROP_SET_HI_CONFIG 0xF1
34:
35: /*
36: * HCI defines
37: */
38:
39: #define NFCMRVL_HCI_EVENT_HEADER_SIZE 0x04
40: #define NFCMRVL_HCI_EVENT_CODE 0x04
41: #define NFCMRVL_HCI_NFC_EVENT_CODE 0xFF
42: #define NFCMRVL_HCI_COMMAND_CODE 0x01
43: #define NFCMRVL_HCI_OGF 0x81
44: #define NFCMRVL_HCI_OCF 0xFE
45:
46: enum nfcmrvl_phy {
47: NFCMRVL_PHY_USB = 0,
48: NFCMRVL_PHY_UART = 1,
49: NFCMRVL_PHY_I2C = 2,
50: NFCMRVL_PHY_SPI = 3,
51: };
52:
53: struct nfcmrvl_platform_data {
54: /*
55: * Generic
56: */
57:
58: /* GPIO that is wired to RESET_N signal */
59: struct gpio_desc *reset_gpio;
60: /* Tell if transport is muxed in HCI one */
61: bool hci_muxed;
62:
63: /*
64: * UART specific
65: */
66:
67: /* Tell if UART needs flow control at init */
68: bool flow_control;
69: /* Tell if firmware supports break control for power management */
70: bool break_control;
71:
72:
73: /*
74: * I2C specific
75: */
76:
77: unsigned int irq;
78: unsigned int irq_polarity;
79: };
80:
81: struct nfcmrvl_private {
82:
83: unsigned long flags;
84:
85: /* Platform configuration */
86: struct nfcmrvl_platform_data config;
87:
88: /* Parent dev */
89: struct nci_dev *ndev;
90:
91: /* FW download context */
92: struct nfcmrvl_fw_dnld fw_dnld;
93:
94: /* FW download support */
95: bool support_fw_dnld;
96:
97: /*
98: * PHY related information
99: */
100:
]
|
| 100/2 |
2026/08/26 14:31 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18653
output: 42
thoughts: 123
Thoughts: **Analyzing the NFC Driver's USB Bulk Transfer Logic**
Okay, so I'm looking at this NFC driver code, and there's a potential bug popping out at me. It looks like the `NFCMRVL_NCI_RUNNING` flag is set to 1, and the `NFCMRVL_USB_BULK_RUNNING` flag is also 1 within `usb.c`. This is leading the `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)` call to effectively be checking `NFCMRVL_USB_BULK_RUNNING` instead, which is not necessarily what the original intent was. The proposed fix, which seems reasonable at first glance, is to pass the `priv` pointer as the URB context instead of `drv_data`. This makes sense because it changes the context that flag is checked in. I'll need to check how the context is used, so let's navigate to the `nfcmrvl_probe()` function in `usb.c` to see how the driver is set up to begin with.
|
| 101/2 |
2026/08/26 14:32 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:300 LineCount:100]
Results: map[Contents: 300:
301: drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
302: if (!drv_data)
303: return -ENOMEM;
304:
305: ret = usb_find_common_endpoints(intf->cur_altsetting, &drv_data->bulk_rx_ep,
306: &drv_data->bulk_tx_ep, NULL, NULL);
307: if (ret)
308: return -ENODEV;
309:
310: drv_data->udev = udev;
311: drv_data->intf = intf;
312:
313: INIT_WORK(&drv_data->waker, nfcmrvl_waker);
314: spin_lock_init(&drv_data->txlock);
315:
316: init_usb_anchor(&drv_data->tx_anchor);
317: init_usb_anchor(&drv_data->bulk_anchor);
318: init_usb_anchor(&drv_data->deferred);
319:
320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
332:
333: static void nfcmrvl_disconnect(struct usb_interface *intf)
334: {
335: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
336:
337: if (!drv_data)
338: return;
339:
340: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
341:
342: nfcmrvl_nci_unregister_dev(drv_data->priv);
343:
344: usb_set_intfdata(drv_data->intf, NULL);
345: }
346:
347: #ifdef CONFIG_PM
348: static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
349: {
350: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
351:
352: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
353:
354: if (drv_data->suspend_count++)
355: return 0;
356:
357: spin_lock_irq(&drv_data->txlock);
358: if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
359: set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
360: spin_unlock_irq(&drv_data->txlock);
361: } else {
362: spin_unlock_irq(&drv_data->txlock);
363: drv_data->suspend_count--;
364: return -EBUSY;
365: }
366:
367: nfcmrvl_usb_stop_traffic(drv_data);
368: usb_kill_anchored_urbs(&drv_data->tx_anchor);
369:
370: return 0;
371: }
372:
373: static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
374: {
375: struct urb *urb;
376: int err;
377:
378: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
379: usb_anchor_urb(urb, &drv_data->tx_anchor);
380:
381: err = usb_submit_urb(urb, GFP_ATOMIC);
382: if (err) {
383: kfree(urb->setup_packet);
384: usb_unanchor_urb(urb);
385: usb_free_urb(urb);
386: break;
387: }
388:
389: drv_data->tx_in_flight++;
390: usb_free_urb(urb);
391: }
392:
393: /* Cleanup the rest deferred urbs. */
394: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
395: kfree(urb->setup_packet);
396: usb_free_urb(urb);
397: }
398: }
399:
]
|
| 102/2 |
2026/08/26 14:32 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 20589
output: 40
thoughts: 122
Thoughts: **Race Condition Diagnosis and Mitigation**
Okay, so I've pinpointed a race condition, and it's a classic one. The problem boils down to `nci_register_device()` being called before `drv_data->priv = priv;` is properly initialized. I see this all the time in kernel drivers, right? We're registering the device, making it instantly available to userspace, but the driver's not fully ready to go, the pointers aren't set, and bam – crash. Happens more often than you'd think.
Time to find other instances of this in the wild. I'm going to dig through the code, focusing on areas like `drivers/nfc/` and `net/nfc/`, and other related subsystems. I'll need to use search terms like "race", "register", and "probe" to zero in on relevant commits that have addressed similar issues. Hopefully, this will give me some ideas for a clean fix, or at least help me understand the prevalence of this kind of bug. It's all about pattern recognition at this point!
|
| 103/2 |
2026/08/26 14:32 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race register] PathPrefix:drivers/nfc/ Since:3 years]
Results:
|
| 104/2 |
2026/08/26 14:32 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 20997
output: 37
thoughts: 56
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Kernel Investigation Focus**
Right, I need to pinpoint some potential concurrency issues or specific driver problems. My initial thought was to cast a wide net across the entire kernel source tree, looking for commits containing terms like "race," "register," and "probe" within the last three years. That would've been a brute-force approach, but it quickly struck me as far too general. The noise-to-signal ratio would be horrendous. I'd be wading through unrelated code for ages.
So, I refined the strategy. Given my area of focus, the NFC subsystem seems like a good starting point. I'll narrow the scope significantly. Let's see what's in `drivers/nfc/` for any commit messages that include the term "race". That's a much more targeted approach. It makes far more sense to start within a known problematic area. This helps to reduce the search complexity and increases the likelihood of finding something relevant to my current goals.
|
| 105/2 |
2026/08/26 14:33 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race] PathPrefix:drivers/nfc/ Since:10 years]
Results: map[Output:febccb39255f nfc: pn533: Add poll mod list filling check
5000fe6c2782 nfc: st-nci: Fix use after free bug in ndlc_remove due to race condition
484b7059796e nfc: pn533: initialize struct pn533_out_arg properly
9dab880d675b nfc: pn533: Wait for out_urb's completion in pn533_usb_send_frame()
9f28157778ed nfc: pn533: Clear nfc_target before being used
e840d8f4a1b3 nfc: virtual_ncidev: Fix memory leak in virtual_nci_send()
b8cedb7093b2 nfc: pn533: Fix buggy cleanup order
d270453a0d9e nfc: nfcmrvl: main: reorder destructive operations in nfcmrvl_nci_unregister_dev to avoid bugs
f80cfe2f2658 NFC: port100: fix use-after-free in port100_send_complete
1b9dadba5022 NFC: st21nfca: Fix memory leak in device probe and remove
]
|
| 106/2 |
2026/08/26 14:33 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 21643
output: 27
thoughts: 47
Thoughts: Okay, let's dive into this patch.
**Analyzing a Potential Driver Bug Fix**
Right, so I'm looking at this commit: `d270453a0d9e nfc: nfcmrvl: main: reorder destructive operations in nfcmrvl_nci_unregister_dev to avoid bugs`. The title itself is a big hint. "Reorder destructive operations" screams "concurrency issue" or "resource management problem." Specifically, it's in the `nfcmrvl_nci_unregister_dev` function, which implies this is related to how the NFC Marvell driver handles device unregistration, that's my area, and this looks to be about fixing the sequence of operations when the NFC Marvell device is being unregistered. My experience tells me that destructive operations - freeing memory, releasing locks, etc. - in the wrong order can lead to all sorts of nasty problems: use-after-free bugs, double frees, race conditions... the usual suspects. I'll need to look closely at the before-and-after of this function to see exactly which operations were reordered and why. I'm expecting to see a change in the sequence of memory freeing, lock release, device state changes... any of those. I'm especially interested to know what the specific bug was, how it was triggered, and why the reordering fixed it. Hopefully, there's a good commit message explaining the logic. Let's dig in and see.
|
| 107/2 |
2026/08/26 14:33 |
tool |
git-show |
0m
Args:
Results: map[Output:commit d270453a0d9ec10bb8a802a142fb1b3601a83098
Author: Duoming Zhou <duoming@zju.edu.cn>
Date: Fri Apr 29 20:45:51 2022 +0800
nfc: nfcmrvl: main: reorder destructive operations in nfcmrvl_nci_unregister_dev to avoid bugs
There are destructive operations such as nfcmrvl_fw_dnld_abort and
gpio_free in nfcmrvl_nci_unregister_dev. The resources such as firmware,
gpio and so on could be destructed while the upper layer functions such as
nfcmrvl_fw_dnld_start and nfcmrvl_nci_recv_frame is executing, which leads
to double-free, use-after-free and null-ptr-deref bugs.
There are three situations that could lead to double-free bugs.
The first situation is shown below:
(Thread 1) | (Thread 2)
nfcmrvl_fw_dnld_start |
... | nfcmrvl_nci_unregister_dev
release_firmware() | nfcmrvl_fw_dnld_abort
kfree(fw) //(1) | fw_dnld_over
| release_firmware
... | kfree(fw) //(2)
| ...
The second situation is shown below:
(Thread 1) | (Thread 2)
nfcmrvl_fw_dnld_start |
... |
mod_timer |
(wait a time) |
fw_dnld_timeout | nfcmrvl_nci_unregister_dev
fw_dnld_over | nfcmrvl_fw_dnld_abort
release_firmware | fw_dnld_over
kfree(fw) //(1) | release_firmware
... | kfree(fw) //(2)
The third situation is shown below:
(Thread 1) | (Thread 2)
nfcmrvl_nci_recv_frame |
if(..->fw_download_in_progress)|
nfcmrvl_fw_dnld_recv_frame |
queue_work |
|
fw_dnld_rx_work | nfcmrvl_nci_unregister_dev
fw_dnld_over | nfcmrvl_fw_dnld_abort
release_firmware | fw_dnld_over
kfree(fw) //(1) | release_firmware
| kfree(fw) //(2)
The firmware struct is deallocated in position (1) and deallocated
in position (2) again.
The crash trace triggered by POC is like below:
BUG: KASAN: double-free or invalid-free in fw_dnld_over
Call Trace:
kfree
fw_dnld_over
nfcmrvl_nci_unregister_dev
nci_uart_tty_close
tty_ldisc_kill
tty_ldisc_hangup
__tty_hangup.part.0
tty_release
...
What's more, there are also use-after-free and null-ptr-deref bugs
in nfcmrvl_fw_dnld_start. If we deallocate firmware struct, gpio or
set null to the members of priv->fw_dnld in nfcmrvl_nci_unregister_dev,
then, we dereference firmware, gpio or the members of priv->fw_dnld in
nfcmrvl_fw_dnld_start, the UAF or NPD bugs will happen.
This patch reorders destructive operations after nci_unregister_device
in order to synchronize between cleanup routine and firmware download
routine.
The nci_unregister_device is well synchronized. If the device is
detaching, the firmware download routine will goto error. If firmware
download routine is executing, nci_unregister_device will wait until
firmware download routine is finished.
Fixes: 3194c6870158 ("NFC: nfcmrvl: add firmware download support")
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
index 2fcf545012b1..1a5284de4341 100644
--- a/drivers/nfc/nfcmrvl/main.c
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -183,6 +183,7 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
{
struct nci_dev *ndev = priv->ndev;
+ nci_unregister_device(ndev);
if (priv->ndev->nfc_dev->fw_download_in_progress)
nfcmrvl_fw_dnld_abort(priv);
@@ -191,7 +192,6 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
if (gpio_is_valid(priv->config.reset_n_io))
gpio_free(priv->config.reset_n_io);
- nci_unregister_device(ndev);
nci_free_device(ndev);
kfree(priv);
}
]
|
| 108/2 |
2026/08/26 14:33 |
llm |
history-explorer |
1m
Model:
Tokens:
input: 23517
output: 40
thoughts: 94
Thoughts: Here's my analysis of this code change:
**Analyzing the Order of Operations in NFC Driver Registration**
Okay, so this commit's about reordering `nci_unregister_device()` to be *before* destructive operations. That's a reasonable defensive measure, generally, but it doesn't address the core problem. The root cause of the bug, as I understand it, seems to lie in either `nfcmrvl_probe()` or, more specifically, in `nfcmrvl_nci_register_dev()`. The issue is that `nci_register_device()` is being called prematurely—*before* the driver's own internal data structures are fully set up and initialized. This is a classic race condition scenario, I'd wager.
To confirm this, I need to verify how `nci_register_device` is implemented and when it's being called, so I'll need to check the definition. Let me go look in `net/nfc/nci/core.c` for `nci_register_device`.
|
| 109/2 |
2026/08/26 14:35 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race] PathPrefix:net/nfc/nci/ Since:10 years]
Results: map[Output:66083581945b nfc: nci: complete pending data exchange on device close
7bd4b0c4779f nfc: nci: free skb on nci_transceive early error paths
d2492688bb9f nfc: nci: Fix race between rfkill and nci_unregister_device().
53270fb0fd77 NFC: nci: fix memory leak in nci_rx_data_packet()
0ad6bded175e nfc/nci: fix race with opening and closing
ef27324e2cb7 nfc: nci: add flush_workqueue to prevent uaf
48b71a9e66c2 NFC: add NCI_UNREG flag to eliminate the race
86cdf8e38792 NFC: reorganize the functions in nci_request
5b25a5bf5e04 nfc: drop unneeded debug prints
b9c28286d8f1 nfc: constify nci_ops
]
|
| 110/2 |
2026/08/26 14:35 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 24146
output: 24
thoughts: 27
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Analyzing the NFC/NCI Race Condition Fix**
Alright, let's dive into that commit, `0ad6bded175e nfc/nci: fix race with opening and closing`. I'm immediately thinking, "Race conditions in NFC/NCI? That's always a fun one." I've seen my fair share of those in driver implementations, especially around resource management and state transitions. My gut tells me this likely centers around asynchronous operations. It could be something like a double-free, data corruption, or even a deadlock scenario, all stemming from concurrent access to shared resources during the NFC controller's initialization, connection, or de-initialization sequences. I want to quickly understand how the fix addresses the core issue. What locks are introduced? Are atomic operations being used? Did they employ some sort of state machine logic with proper synchronization? Knowing the specifics of the hardware abstraction layer is also crucial here. Did they introduce a guard? I'm hoping the commit message is clear and concise, detailing the precise steps and any potential implications regarding performance. I'm keen to see if this affects only certain NFC controller chipsets or if it's more of a general safeguard within the NCI framework. Understanding the scope is important to determine if this has ripple effects into my projects, or if I can breathe easy for now.
|
| 111/2 |
2026/08/26 14:36 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 0ad6bded175e829c2ca261529c9dce39a32a042d
Author: Lin Ma <linma@zju.edu.cn>
Date: Wed Nov 16 21:02:49 2022 +0800
nfc/nci: fix race with opening and closing
Previously we leverage NCI_UNREG and the lock inside nci_close_device to
prevent the race condition between opening a device and closing a
device. However, it still has problem because a failed opening command
will erase the NCI_UNREG flag and allow another opening command to
bypass the status checking.
This fix corrects that by making sure the NCI_UNREG is held.
Reported-by: syzbot+43475bf3cfbd6e41f5b7@syzkaller.appspotmail.com
Fixes: 48b71a9e66c2 ("NFC: add NCI_UNREG flag to eliminate the race")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 6a193cce2a75..4ffdf2f45c44 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -542,7 +542,7 @@ static int nci_open_device(struct nci_dev *ndev)
skb_queue_purge(&ndev->tx_q);
ndev->ops->close(ndev);
- ndev->flags = 0;
+ ndev->flags &= BIT(NCI_UNREG);
}
done:
]
|
| 112/2 |
2026/08/26 14:36 |
llm |
history-explorer |
1m
Model:
Tokens:
input: 25009
output: 32
thoughts: 415
Thoughts: **Analyzing a Potential Race Condition in NFC Driver Initialization**
Okay, so I'm looking into a potential race condition related to `nci_open_device` and `nci_close_device`. My initial instinct is to search the driver code for keywords like "register" and "race," specifically within the `drivers/net/`, `drivers/usb/`, and `drivers/nfc/` directories. I need to be methodical here.
The core of the issue appears to stem from how `nci_register_device()` makes the device accessible to userspace. Userspace can then invoke `nci_dev_up()`, leading to a call to `ndev->ops->open()`. In the specific case of `nfcmrvl`, `ndev->ops->open` maps to `nfcmrvl_nci_open()`, which, for USB implementations, calls `nfcmrvl_usb_nci_open()` and that submits URBs.
Now, here's where it gets interesting: the URB completion handler, `nfcmrvl_bulk_complete()`, relies on `drv_data->priv`. But... and this is crucial... `drv_data->priv` is set *after* `nfcmrvl_nci_register_dev()` returns.
Wait, let me unpack this... `nfcmrvl_nci_register_dev()` allocates `priv`, sets up the `priv->ndev`, and *then* calls `nci_register_device(priv->ndev)`. That call to `nci_register_device()` makes the device available to userspace, and it's also *before* `nfcmrvl_probe()` assigns `drv_data->priv = priv`. So, essentially, we have a window of vulnerability where userspace can open the device before `drv_data->priv` is fully initialized. This leads to a race.
The provided solution proposes passing `priv` as the URB context instead of `drv_data`, since `priv` is always valid when `nfcmrvl_usb_nci_open()` is called. This sounds like the cleaner approach. I need to see if there are similar fixes elsewhere in the `drivers/nfc/` subtree that I can draw inspiration from. This is definitely a race condition that needs to be addressed.
|
| 113/2 |
2026/08/26 14:37 |
tool |
grepper |
0m
Args: map[Expression:usb_fill_bulk_urb PathPrefix:drivers/nfc/]
Results: map[Output:drivers/nfc/nfcmrvl/usb.c=103=nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
--
drivers/nfc/nfcmrvl/usb.c-125-
drivers/nfc/nfcmrvl/usb.c:126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
drivers/nfc/nfcmrvl/usb.c-127- nfcmrvl_bulk_complete, drv_data);
--
drivers/nfc/nfcmrvl/usb.c=220=static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
--
drivers/nfc/nfcmrvl/usb.c-237-
drivers/nfc/nfcmrvl/usb.c:238: usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
drivers/nfc/nfcmrvl/usb.c-239- nfcmrvl_tx_complete, skb);
--
drivers/nfc/pn533/usb.c=476=static int pn533_usb_probe(struct usb_interface *interface,
--
drivers/nfc/pn533/usb.c-518-
drivers/nfc/pn533/usb.c:519: usb_fill_bulk_urb(phy->in_urb, phy->udev,
drivers/nfc/pn533/usb.c-520- usb_rcvbulkpipe(phy->udev, usb_endpoint_num(ep_in)),
--
drivers/nfc/pn533/usb.c-522-
drivers/nfc/pn533/usb.c:523: usb_fill_bulk_urb(phy->out_urb, phy->udev,
drivers/nfc/pn533/usb.c-524- usb_sndbulkpipe(phy->udev, usb_endpoint_num(ep_out)),
drivers/nfc/pn533/usb.c-525- NULL, 0, pn533_out_complete, phy);
drivers/nfc/pn533/usb.c:526: usb_fill_bulk_urb(phy->ack_urb, phy->udev,
drivers/nfc/pn533/usb.c-527- usb_sndbulkpipe(phy->udev, usb_endpoint_num(ep_out)),
--
drivers/nfc/port100.c=1489=static int port100_probe(struct usb_interface *interface,
--
drivers/nfc/port100.c-1523-
drivers/nfc/port100.c:1524: usb_fill_bulk_urb(dev->in_urb, dev->udev,
drivers/nfc/port100.c-1525- usb_rcvbulkpipe(dev->udev, usb_endpoint_num(ep_in)),
drivers/nfc/port100.c-1526- NULL, 0, NULL, dev);
drivers/nfc/port100.c:1527: usb_fill_bulk_urb(dev->out_urb, dev->udev,
drivers/nfc/port100.c-1528- usb_sndbulkpipe(dev->udev, usb_endpoint_num(ep_out)),
]
|
| 114/2 |
2026/08/26 14:37 |
llm |
history-explorer |
1m
Model:
Tokens:
input: 26869
output: 42
thoughts: 42
Thoughts: Okay, let's dive into this. I need to get a handle on how these NFC drivers work, specifically the initialization and registration processes. My first stop should be the code itself. I'm going to take a look at `drivers/nfc/pn533/usb.c` and `drivers/nfc/port100.c`. I expect these to be good examples since they likely handle different connection types, and I need a holistic view. I'm hoping to quickly identify the key functions involved in getting these devices up and running within the kernel, understanding the registration mechanisms, and how these drivers integrate with the NFC subsystem. By examining these two, I should be able to spot any common patterns or distinct approaches to these core operations. This should provide the foundation for what I need to see.
|
| 115/2 |
2026/08/26 14:38 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/pn533/usb.c FirstLine:476 LineCount:100]
Results: map[Contents: 476: static int pn533_usb_probe(struct usb_interface *interface,
477: const struct usb_device_id *id)
478: {
479: struct usb_endpoint_descriptor *ep_in, *ep_out;
480: struct pn533 *priv;
481: struct pn533_usb_phy *phy;
482: u32 protocols;
483: enum pn533_protocol_type protocol_type = PN533_PROTO_REQ_ACK_RESP;
484: struct pn533_frame_ops *fops = NULL;
485: unsigned char *in_buf;
486: int in_buf_len = PN533_EXT_FRAME_HEADER_LEN +
487: PN533_STD_FRAME_MAX_PAYLOAD_LEN +
488: PN533_STD_FRAME_TAIL_LEN;
489: int rc;
490:
491: phy = devm_kzalloc(&interface->dev, sizeof(*phy), GFP_KERNEL);
492: if (!phy)
493: return -ENOMEM;
494:
495: in_buf = kzalloc(in_buf_len, GFP_KERNEL);
496: if (!in_buf)
497: return -ENOMEM;
498:
499: phy->udev = interface_to_usbdev(interface);
500: phy->interface = interface;
501:
502: rc = usb_find_common_endpoints(interface->cur_altsetting, &ep_in,
503: &ep_out, NULL, NULL);
504: if (rc) {
505: nfc_err(&interface->dev,
506: "Could not find bulk-in or bulk-out endpoint\n");
507: goto error;
508: }
509:
510: phy->in_urb = usb_alloc_urb(0, GFP_KERNEL);
511: phy->out_urb = usb_alloc_urb(0, GFP_KERNEL);
512: phy->ack_urb = usb_alloc_urb(0, GFP_KERNEL);
513:
514: if (!phy->in_urb || !phy->out_urb || !phy->ack_urb) {
515: rc = -ENOMEM;
516: goto error;
517: }
518:
519: usb_fill_bulk_urb(phy->in_urb, phy->udev,
520: usb_rcvbulkpipe(phy->udev, usb_endpoint_num(ep_in)),
521: in_buf, in_buf_len, NULL, phy);
522:
523: usb_fill_bulk_urb(phy->out_urb, phy->udev,
524: usb_sndbulkpipe(phy->udev, usb_endpoint_num(ep_out)),
525: NULL, 0, pn533_out_complete, phy);
526: usb_fill_bulk_urb(phy->ack_urb, phy->udev,
527: usb_sndbulkpipe(phy->udev, usb_endpoint_num(ep_out)),
528: NULL, 0, pn533_ack_complete, phy);
529:
530: switch (id->driver_info) {
531: case PN533_DEVICE_STD:
532: protocols = PN533_ALL_PROTOCOLS;
533: break;
534:
535: case PN533_DEVICE_PASORI:
536: protocols = PN533_NO_TYPE_B_PROTOCOLS;
537: break;
538:
539: case PN533_DEVICE_ACR122U:
540: protocols = PN533_NO_TYPE_B_PROTOCOLS;
541: fops = &pn533_acr122_frame_ops;
542: protocol_type = PN533_PROTO_REQ_RESP;
543:
544: rc = pn533_acr122_poweron_rdr(phy);
545: if (rc < 0) {
546: nfc_err(&interface->dev,
547: "Couldn't poweron the reader (error %d)\n", rc);
548: goto error;
549: }
550: break;
551:
552: default:
553: nfc_err(&interface->dev, "Unknown device type %lu\n",
554: id->driver_info);
555: rc = -EINVAL;
556: goto error;
557: }
558:
559: priv = pn53x_common_init(id->driver_info, protocol_type,
560: phy, &usb_phy_ops, fops,
561: &phy->udev->dev);
562:
563: if (IS_ERR(priv)) {
564: rc = PTR_ERR(priv);
565: goto error;
566: }
567:
568: phy->priv = priv;
569:
570: rc = pn533_finalize_setup(priv);
571: if (rc)
572: goto err_clean;
573:
574: usb_set_intfdata(interface, phy);
575: rc = pn53x_register_nfc(priv, protocols, &interface->dev);
]
|
| 116/2 |
2026/08/26 14:38 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 28751
output: 41
thoughts: 92
Thoughts: **My Line of Reasoning**
Okay, so I'm looking at `pn533_usb_probe` and I see the crucial calls. The key sequence here is that `pn53x_register_nfc` is called *after* `phy->priv = priv;` and `usb_set_intfdata(interface, phy);`. Perfect, that's the correct order. I need the private data to be set up first, and the USB interface data initialized before registering the NFC device. Makes total sense. Wait a minute... I need to check something. `pn53x_register_nfc` *itself* calls `nfc_register_device`. I want to make sure I am covering all the bases so I should check `port100.c` next. Just a quick double-check for the sake of thoroughness.
|
| 117/2 |
2026/08/26 14:39 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/port100.c FirstLine:1489 LineCount:100]
Results: map[Contents:1489: static int port100_probe(struct usb_interface *interface,
1490: const struct usb_device_id *id)
1491: {
1492: struct usb_endpoint_descriptor *ep_in, *ep_out;
1493: struct port100 *dev;
1494: int rc;
1495: u16 fw_version;
1496: u64 cmd_type_mask;
1497:
1498: dev = devm_kzalloc(&interface->dev, sizeof(struct port100), GFP_KERNEL);
1499: if (!dev)
1500: return -ENOMEM;
1501:
1502: mutex_init(&dev->out_urb_lock);
1503: dev->udev = interface_to_usbdev(interface);
1504: dev->interface = interface;
1505: usb_set_intfdata(interface, dev);
1506:
1507: rc = usb_find_common_endpoints(interface->cur_altsetting, &ep_in,
1508: &ep_out, NULL, NULL);
1509: if (rc) {
1510: nfc_err(&interface->dev,
1511: "Could not find bulk-in or bulk-out endpoint\n");
1512: goto error;
1513: }
1514:
1515: dev->in_urb = usb_alloc_urb(0, GFP_KERNEL);
1516: dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
1517:
1518: if (!dev->in_urb || !dev->out_urb) {
1519: nfc_err(&interface->dev, "Could not allocate USB URBs\n");
1520: rc = -ENOMEM;
1521: goto error;
1522: }
1523:
1524: usb_fill_bulk_urb(dev->in_urb, dev->udev,
1525: usb_rcvbulkpipe(dev->udev, usb_endpoint_num(ep_in)),
1526: NULL, 0, NULL, dev);
1527: usb_fill_bulk_urb(dev->out_urb, dev->udev,
1528: usb_sndbulkpipe(dev->udev, usb_endpoint_num(ep_out)),
1529: NULL, 0, port100_send_complete, dev);
1530: dev->out_urb->transfer_flags = URB_ZERO_PACKET;
1531:
1532: dev->skb_headroom = PORT100_FRAME_HEADER_LEN +
1533: PORT100_COMM_RF_HEAD_MAX_LEN;
1534: dev->skb_tailroom = PORT100_FRAME_TAIL_LEN;
1535:
1536: init_completion(&dev->cmd_cancel_done);
1537: INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete);
1538:
1539: /* The first thing to do with the Port-100 is to set the command type
1540: * to be used. If supported we use command type 1. 0 otherwise.
1541: */
1542: cmd_type_mask = port100_get_command_type_mask(dev);
1543: if (!cmd_type_mask) {
1544: nfc_err(&interface->dev,
1545: "Could not get supported command types\n");
1546: rc = -ENODEV;
1547: goto error;
1548: }
1549:
1550: if (PORT100_CMD_TYPE_IS_SUPPORTED(cmd_type_mask, PORT100_CMD_TYPE_1))
1551: dev->cmd_type = PORT100_CMD_TYPE_1;
1552: else
1553: dev->cmd_type = PORT100_CMD_TYPE_0;
1554:
1555: rc = port100_set_command_type(dev, dev->cmd_type);
1556: if (rc) {
1557: nfc_err(&interface->dev,
1558: "The device does not support command type %u\n",
1559: dev->cmd_type);
1560: goto error;
1561: }
1562:
1563: fw_version = port100_get_firmware_version(dev);
1564: if (!fw_version)
1565: nfc_err(&interface->dev,
1566: "Could not get device firmware version\n");
1567:
1568: nfc_info(&interface->dev,
1569: "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n",
1570: (fw_version & 0xFF00) >> 8, fw_version & 0xFF);
1571:
1572: dev->nfc_digital_dev = nfc_digital_allocate_device(&port100_digital_ops,
1573: PORT100_PROTOCOLS,
1574: PORT100_CAPABILITIES,
1575: dev->skb_headroom,
1576: dev->skb_tailroom);
1577: if (!dev->nfc_digital_dev) {
1578: nfc_err(&interface->dev,
1579: "Could not allocate nfc_digital_dev\n");
1580: rc = -ENOMEM;
1581: goto error;
1582: }
1583:
1584: nfc_digital_set_parent_dev(dev->nfc_digital_dev, &interface->dev);
1585: nfc_digital_set_drvdata(dev->nfc_digital_dev, dev);
1586:
1587: rc = nfc_digital_register_device(dev->nfc_digital_dev);
1588: if (rc) {
]
|
| 118/2 |
2026/08/26 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 30792
output: 41
thoughts: 169
Thoughts: **Analyzing NFC Device Registration Sequences**
Okay, so I'm looking at how NFC devices get registered within the system, specifically focusing on the timing and order of operations. In `port100.c`, I see that `nfc_digital_register_device` is called right at the end of the `port100_probe` function. That seems correct, following the expected initialization sequence.
Now, I'm cross-referencing this with the Marvell USB implementation in `nfcmrvl/usb.c`. Here, `nfcmrvl_nci_register_dev` is called. That function, in turn, calls `nci_register_device`, which is a key difference and source for potential discrepancies. I also note that `nfcmrvl_nci_register_dev` allocates the `priv` data structure and returns it. Crucially, `drv_data->priv = priv;` is *subsequently* assigned. This means that the driver-specific private data is set *after* the generic NCI device registration, which is something I need to keep in mind.
To get a complete picture, I need to check `nfcmrvl_nci_register_dev` itself, so I'll check its implementation in `nfcmrvl/main.c`. This might highlight differences in implementation.
|
| 119/2 |
2026/08/26 14:39 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:92 LineCount:100]
Results: map[Contents: 92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
101: int tailroom;
102: u32 protocols;
103:
104: priv = kzalloc_obj(*priv);
105: if (!priv)
106: return ERR_PTR(-ENOMEM);
107:
108: priv->drv_data = drv_data;
109: priv->if_ops = ops;
110: priv->dev = dev;
111: priv->phy = phy;
112:
113: memcpy(&priv->config, pdata, sizeof(*pdata));
114:
115: if (!priv->config.reset_gpio) {
116: priv->config.reset_gpio =
117: devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
118: if (IS_ERR(priv->config.reset_gpio)) {
119: priv->config.reset_gpio = NULL;
120: nfc_err(dev, "failed to get reset gpio\n");
121: }
122: }
123:
124: if (phy == NFCMRVL_PHY_SPI) {
125: headroom = NCI_SPI_HDR_LEN;
126: tailroom = 1;
127: } else
128: headroom = tailroom = 0;
129:
130: if (priv->config.hci_muxed)
131: headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
132:
133: protocols = NFC_PROTO_JEWEL_MASK
134: | NFC_PROTO_MIFARE_MASK
135: | NFC_PROTO_FELICA_MASK
136: | NFC_PROTO_ISO14443_MASK
137: | NFC_PROTO_ISO14443_B_MASK
138: | NFC_PROTO_ISO15693_MASK
139: | NFC_PROTO_NFC_DEP_MASK;
140:
141: priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
142: headroom, tailroom);
143: if (!priv->ndev) {
144: nfc_err(dev, "nci_allocate_device failed\n");
145: rc = -ENOMEM;
146: goto error_free;
147: }
148:
149: rc = nfcmrvl_fw_dnld_init(priv);
150: if (rc) {
151: nfc_err(dev, "failed to initialize FW download %d\n", rc);
152: goto error_free_dev;
153: }
154:
155: nci_set_drvdata(priv->ndev, priv);
156:
157: rc = nci_register_device(priv->ndev);
158: if (rc) {
159: nfc_err(dev, "nci_register_device failed %d\n", rc);
160: goto error_fw_dnld_deinit;
161: }
162:
163: /* Ensure that controller is powered off */
164: nfcmrvl_chip_halt(priv);
165:
166: nfc_info(dev, "registered with nci successfully\n");
167: return priv;
168:
169: error_fw_dnld_deinit:
170: nfcmrvl_fw_dnld_deinit(priv);
171: error_free_dev:
172: nci_free_device(priv->ndev);
173: error_free:
174: kfree(priv);
175: return ERR_PTR(rc);
176: }
177: EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
178:
179: void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
180: {
181: struct nci_dev *ndev = priv->ndev;
182:
183: nci_unregister_device(ndev);
184: if (priv->ndev->nfc_dev->fw_download_in_progress)
185: nfcmrvl_fw_dnld_abort(priv);
186:
187: nfcmrvl_fw_dnld_deinit(priv);
188:
189: nci_free_device(ndev);
190: kfree(priv);
191: }
]
|
| 120/2 |
2026/08/26 14:39 |
llm |
history-explorer |
4m
Model:
Tokens:
input: 32805
output: 42
thoughts: 333
Thoughts: **Debugging a Race Condition in the NFC Marvell Driver**
Okay, so I'm digging into this NFC Marvell driver and I've stumbled upon what looks like a classic race condition, which is a bit frustrating but not uncommon in these kinds of driver contexts. Let me walk through the sequence of events and where I think the problem lies.
The function `nfcmrvl_nci_register_dev` is responsible for allocating and setting up the private data structure, which I'll call `priv`. It sets `priv->drv_data` and then calls `nci_register_device`. Seems straightforward enough. Then, in the `nfcmrvl_probe` function (specifically in `usb.c`), the `nfcmrvl_nci_register_dev` function is called and the `priv` is received from it, and assigned to `drv_data->priv`.
Now, the crucial point is that `nci_register_device` exposes the device to userspace. Once exposed, userspace is able to call `nci_dev_up` which triggers `nfcmrvl_nci_open`. Within `nfcmrvl_nci_open` function `nfcmrvl_usb_nci_open` is called. It's inside `nfcmrvl_usb_nci_open` where the trouble begins. This function is where the URBs (USB Request Blocks) get submitted. And the completion handler for these URBs, `nfcmrvl_bulk_complete`, relies on `drv_data->priv`.
Here's the problem: The `drv_data->priv` isn't necessarily set *before* `nfcmrvl_bulk_complete` might run. It's a race! Userspace might call `nci_dev_up` *before* the `priv` assignment in `nfcmrvl_probe` has completed. Meaning that the URB completion handler might try to access an uninitialized `priv` pointer! This can lead to all sorts of undefined behavior, memory corruption, crashes, etc.
So, the proposed fix involves passing `priv` directly to `usb_fill_bulk_urb` instead of `drv_data`. Which makes me pause for a second, then I remember something... wait a second! I think I see what's going on... `nfcmrvl_usb_nci_open` is called with `priv` as an argument! Now I have to go double-check the arguments passed in to that function... Let's review the code, and make sure that `priv` is passed into `usb_fill_bulk_urb` correctly. I'll need to confirm that `nfcmrvl_usb_nci_open` is indeed getting the correct `priv` pointer to use, so I don't introduce a regression.
|
| 121/2 |
2026/08/26 14:44 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:160 LineCount:100]
Results: map[Contents: 160: spin_unlock_irqrestore(&drv_data->txlock, flags);
161:
162: kfree(urb->setup_packet);
163: kfree_skb(skb);
164: }
165:
166: static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
167: {
168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
169: int err;
170:
171: err = usb_autopm_get_interface(drv_data->intf);
172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
188: usb_autopm_put_interface(drv_data->intf);
189: return err;
190: }
191:
192: static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
193: {
194: usb_kill_anchored_urbs(&drv_data->bulk_anchor);
195: }
196:
197: static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
198: {
199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
200: int err;
201:
202: cancel_work_sync(&drv_data->waker);
203:
204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
205:
206: nfcmrvl_usb_stop_traffic(drv_data);
207: usb_kill_anchored_urbs(&drv_data->tx_anchor);
208: err = usb_autopm_get_interface(drv_data->intf);
209: if (err)
210: goto failed;
211:
212: drv_data->intf->needs_remote_wakeup = 0;
213: usb_autopm_put_interface(drv_data->intf);
214:
215: failed:
216: usb_scuttle_anchored_urbs(&drv_data->deferred);
217: return 0;
218: }
219:
220: static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
221: struct sk_buff *skb)
222: {
223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
224: struct urb *urb;
225: unsigned int pipe;
226: int err;
227:
228: if (!drv_data->bulk_tx_ep)
229: return -ENODEV;
230:
231: urb = usb_alloc_urb(0, GFP_ATOMIC);
232: if (!urb)
233: return -ENOMEM;
234:
235: pipe = usb_sndbulkpipe(drv_data->udev,
236: drv_data->bulk_tx_ep->bEndpointAddress);
237:
238: usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
239: nfcmrvl_tx_complete, skb);
240:
241: err = nfcmrvl_inc_tx(drv_data);
242: if (err) {
243: usb_anchor_urb(urb, &drv_data->deferred);
244: schedule_work(&drv_data->waker);
245: err = 0;
246: goto done;
247: }
248:
249: usb_anchor_urb(urb, &drv_data->tx_anchor);
250:
251: err = usb_submit_urb(urb, GFP_ATOMIC);
252: if (err) {
253: if (err != -EPERM && err != -ENODEV)
254: nfc_err(&drv_data->udev->dev,
255: "urb %p submission failed (%d)\n", urb, -err);
256: kfree(urb->setup_packet);
257: usb_unanchor_urb(urb);
258: } else {
259: usb_mark_last_busy(drv_data->udev);
]
|
| 122/2 |
2026/08/26 14:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 35344
output: 40
thoughts: 171
Thoughts: **Refactoring the NCI USB Driver**
Okay, so I'm looking at this `nfcmrvl_usb_nci_open` function, and it's clear it receives the `priv` structure. That's good; it's the key to everything. Currently, it calls `nfcmrvl_submit_bulk_urb` with `drv_data`, which seems a bit indirect. The immediate thought is, if we refactor `nfcmrvl_submit_bulk_urb` to accept `priv` directly, we can then pass `priv` as the `context` argument to `usb_fill_bulk_urb`.
This is the clever bit. Then, in the completion routine, `nfcmrvl_bulk_complete`, we can grab `priv` directly from `urb->context`. We can recover the original `drv_data` using `priv->drv_data`, since that's initialized during device registration in `nfcmrvl_nci_register_dev`. This will eliminate a layer of indirection and provide a more direct access path to the necessary data within the USB interrupt context.
The text also mentions a potentially similar race condition in `spi.c`. That's worth a look to see if this same pattern of refactoring can be applied there as well. Let's shift gears and check that `spi.c` file.
|
| 123/2 |
2026/08/26 14:44 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-SPI driver: SPI interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/nfc.h>
11: #include <linux/of_irq.h>
12: #include <net/nfc/nci.h>
13: #include <net/nfc/nci_core.h>
14: #include <linux/spi/spi.h>
15: #include "nfcmrvl.h"
16:
17: #define SPI_WAIT_HANDSHAKE 1
18:
19: struct nfcmrvl_spi_drv_data {
20: unsigned long flags;
21: struct spi_device *spi;
22: struct nci_spi *nci_spi;
23: struct completion handshake_completion;
24: struct nfcmrvl_private *priv;
25: };
26:
27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
45: nfc_err(&drv_data->spi->dev, "failed to read spi packet");
46: return IRQ_HANDLED;
47: }
48:
49: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
50: nfc_err(&drv_data->spi->dev, "corrupted RX packet");
51:
52: return IRQ_HANDLED;
53: }
54:
55: static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
56: {
57: return 0;
58: }
59:
60: static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
61: {
62: return 0;
63: }
64:
65: static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
66: struct sk_buff *skb)
67: {
68: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
69: int err;
70:
71: /* Reinit completion for slave handshake */
72: reinit_completion(&drv_data->handshake_completion);
73: set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
74:
75: /*
76: * Append a dummy byte at the end of SPI frame. This is due to a
77: * specific DMA implementation in the controller
78: */
79: skb_put(skb, 1);
80:
81: /* Send the SPI packet */
82: err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
83: skb);
84: if (err)
85: nfc_err(priv->dev, "spi_send failed %d", err);
86:
87: return err;
88: }
89:
90: static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv,
91: const void *param)
92: {
93: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
94: const struct nfcmrvl_fw_spi_config *config = param;
95:
96: drv_data->nci_spi->xfer_speed_hz = config->clk;
97: }
98:
99: static const struct nfcmrvl_if_ops spi_ops = {
100: .nci_open = nfcmrvl_spi_nci_open,
]
|
| 124/2 |
2026/08/26 14:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 37298
output: 42
thoughts: 125
Thoughts: **Analyzing a Potential Driver Bug**
Okay, so I'm looking at this `spi.c` driver, specifically the `nfcmrvl_spi_int_irq_thread_fn` function. It seems like it's using `drv_data->priv`, and the concern is that if an interrupt fires before `drv_data->priv` is properly initialized, we're going to get a nasty NULL pointer dereference. The explanation suggests a fix: simply adding a check for `!drv_data->priv` at the beginning of the interrupt handler, just like they did in `i2c.c`.
That sounds straightforward enough, but I always prefer to be thorough. Before blindly applying a fix, I want to confirm that the proposed solution is indeed the standard practice in this code base. So, let me take a quick look at `i2c.c` and see if they *actually* implement that null check in their interrupt handler. It's good to see how others handle similar situations. Plus, confirming the existing pattern provides additional confidence in the recommended solution. Let's see...
|
| 125/2 |
2026/08/26 14:44 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-I2C driver: I2C interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/i2c.h>
11: #include <linux/nfc.h>
12: #include <linux/delay.h>
13: #include <linux/of_irq.h>
14: #include <net/nfc/nci.h>
15: #include <net/nfc/nci_core.h>
16: #include "nfcmrvl.h"
17:
18: struct nfcmrvl_i2c_drv_data {
19: unsigned long flags;
20: struct device *dev;
21: struct i2c_client *i2c;
22: struct nfcmrvl_private *priv;
23: };
24:
25: static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data,
26: struct sk_buff **skb)
27: {
28: int ret;
29: struct nci_ctrl_hdr nci_hdr;
30:
31: /* Read NCI header to know the payload size */
32: ret = i2c_master_recv(drv_data->i2c, (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE);
33: if (ret != NCI_CTRL_HDR_SIZE) {
34: nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n");
35: return -EBADMSG;
36: }
37:
38: *skb = nci_skb_alloc(drv_data->priv->ndev,
39: nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL);
40: if (!*skb)
41: return -ENOMEM;
42:
43: /* Copy NCI header into the SKB */
44: skb_put_data(*skb, &nci_hdr, NCI_CTRL_HDR_SIZE);
45:
46: if (nci_hdr.plen) {
47: /* Read the NCI payload */
48: ret = i2c_master_recv(drv_data->i2c,
49: skb_put(*skb, nci_hdr.plen),
50: nci_hdr.plen);
51:
52: if (ret != nci_hdr.plen) {
53: nfc_err(&drv_data->i2c->dev,
54: "Invalid frame payload length: %u (expected %u)\n",
55: ret, nci_hdr.plen);
56: kfree_skb(*skb);
57: return -EBADMSG;
58: }
59: }
60:
61: return 0;
62: }
63:
64: static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
65: {
66: struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
67: struct sk_buff *skb = NULL;
68: int ret;
69:
70: if (!drv_data->priv)
71: return IRQ_HANDLED;
72:
73: if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
74: return IRQ_HANDLED;
75:
76: ret = nfcmrvl_i2c_read(drv_data, &skb);
77:
78: switch (ret) {
79: case -EREMOTEIO:
80: set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
81: break;
82: case -ENOMEM:
83: case -EBADMSG:
84: nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
85: break;
86: default:
87: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
88: nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
89: break;
90: }
91: return IRQ_HANDLED;
92: }
93:
94: static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
95: {
96: struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
97:
98: if (!drv_data)
99: return -ENODEV;
100:
]
|
| 126/2 |
2026/08/26 14:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 39216
output: 41
thoughts: 162
Thoughts: **Race Condition Resolution: I2C and Beyond**
Okay, so looking at `i2c.c`, that `if (!drv_data->priv)` check at the beginning of the IRQ handler is *exactly* what was described in the explanation. It mirrors the issue we saw with `spi.c` and is a clear indicator of how to handle these race conditions. Good to see the principle is holding true across different driver implementations.
The pattern is pretty straightforward, and it makes complete sense from a concurrency perspective. Essentially:
1. **Fully Initialized Context:** Pass a *complete* `priv` structure to those asynchronous callbacks – like URB handlers. No partial initialization, which is a recipe for disaster in concurrent code.
2. **Early IRQ Handler Protection:** For IRQ handlers, especially those in SPI/I2C where we might encounter an early firing, we need that initial `!drv_data->priv` check. It's a fundamental guardrail against accessing uninitialized data.
Now, let's see if this pattern has been applied in other potential problem areas. `drivers/nfc/` seems like a likely candidate. Let's see if we can find similar issues and fixes there.
|
| 127/2 |
2026/08/26 14:44 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race NULL] PathPrefix:drivers/nfc/ Since:10 years]
Results: map[Output:484b7059796e nfc: pn533: initialize struct pn533_out_arg properly
d270453a0d9e nfc: nfcmrvl: main: reorder destructive operations in nfcmrvl_nci_unregister_dev to avoid bugs
a71a29f50de1 NFC: nxp-nci: Fix NULL pointer dereference after I2C communication error
32ecc75ded72 NFC: pn533: change order operations in dev registation
b6355fb3f5f4 nfc: fdp: fix NULL pointer dereference
]
|
| 128/2 |
2026/08/26 14:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 39843
output: 23
thoughts: 28
Thoughts: Here's a summary of my thought process, as if I were analyzing that commit:
**Analyzing NFC Driver Registration Order**
Okay, let's dive into commit `32ecc75ded72`. The title already hints at a potential optimization or fix related to the NFC driver for the PN533 chip. Specifically, it's about altering the order of operations during device registration. This is immediately interesting because driver initialization and registration sequences are often critical for proper hardware interaction, especially in complex systems like NFC controllers. I need to understand *why* they're changing the order. Are they addressing a race condition? Is this related to power-up sequencing, timing constraints, or perhaps dependency issues with other parts of the NFC stack or the platform? My initial hypothesis is that they've found a more robust or efficient way to handle device discovery, initialization, and enabling the PN533. I need to look closely at the code diffs to determine if this change affects the overall registration flow, and if so, how. I'll be looking for signs of potential regressions. If the changes seem substantial, I might want to trace through the existing driver code to understand the existing logic and any rationale for the previous ordering.
|
| 129/2 |
2026/08/26 14:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 32ecc75ded72e0425713a7ffe2050fef6e54e564
Author: Andrey Rusalin <arusalin@dev.rtsoft.ru>
Date: Wed Dec 28 20:10:59 2016 +0300
NFC: pn533: change order operations in dev registation
Sometimes during probing and registration of pn533_i2c
NULL pointer dereference happens.
Reproduced in cycle of inserting and removing pn533_i2c
and pn533 modules.
Backtrace:
[<8004205c>] (__queue_work) from [<80042324>] (queue_work_on+0x50/0x5c)
r10:acdc7c80 r9:8006b330 r8:ac0dfb40 r7:ac50c600 r6:00000004 r5:acbbee40 r4:600f0113
[<800422d4>] (queue_work_on) from [<7f7d5b6c>] (pn533_recv_frame+0x158/0x1fc [pn533])
r7:ffffff87 r6:00000000 r5:acbbee40 r4:acbbee00
[<7f7d5a14>] (pn533_recv_frame [pn533]) from [<7f7df4b8>] (pn533_i2c_irq_thread_fn+0x184/0x)
r6:acb2a000 r5:00000000 r4:acdc7b90
[<7f7df334>] (pn533_i2c_irq_thread_fn [pn533_i2c]) from [<8006b354>] (irq_thread_fn+0x24/0x)
r7:00000000 r6:accde000 r5:ac0dfb40 r4:acdc7c80
...
Seems there is some race condition due registration of
irq handler until all data stuctures that could be needed
are ready. So I re-ordered some ops. After this, problem has gone.
Changes in USB part was not tested, but it should not break
anything.
Signed-off-by: Andrey Rusalin <arusalin@dev.rtsoft.ru>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c
index 11d78b43cf76..2c2fb9cfe10a 100644
--- a/drivers/nfc/pn533/i2c.c
+++ b/drivers/nfc/pn533/i2c.c
@@ -206,14 +206,6 @@ static int pn533_i2c_probe(struct i2c_client *client,
phy->i2c_dev = client;
i2c_set_clientdata(client, phy);
- r = request_threaded_irq(client->irq, NULL, pn533_i2c_irq_thread_fn,
- IRQF_TRIGGER_FALLING |
- IRQF_SHARED | IRQF_ONESHOT,
- PN533_I2C_DRIVER_NAME, phy);
-
- if (r < 0)
- nfc_err(&client->dev, "Unable to register IRQ handler\n");
-
priv = pn533_register_device(PN533_DEVICE_PN532,
PN533_NO_TYPE_B_PROTOCOLS,
PN533_PROTO_REQ_ACK_RESP,
@@ -223,16 +215,32 @@ static int pn533_i2c_probe(struct i2c_client *client,
if (IS_ERR(priv)) {
r = PTR_ERR(priv);
- goto err_register;
+ return r;
}
phy->priv = priv;
+ r = request_threaded_irq(client->irq, NULL, pn533_i2c_irq_thread_fn,
+ IRQF_TRIGGER_FALLING |
+ IRQF_SHARED | IRQF_ONESHOT,
+ PN533_I2C_DRIVER_NAME, phy);
+ if (r < 0) {
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+ goto irq_rqst_err;
+ }
+
+ r = pn533_finalize_setup(priv);
+ if (r)
+ goto fn_setup_err;
+
return 0;
-err_register:
+fn_setup_err:
free_irq(client->irq, phy);
+irq_rqst_err:
+ pn533_unregister_device(phy->priv);
+
return r;
}
diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c
index 712aa67e1770..65bbaa5fcdda 100644
--- a/drivers/nfc/pn533/pn533.c
+++ b/drivers/nfc/pn533/pn533.c
@@ -2570,6 +2570,31 @@ static int pn533_setup(struct pn533 *dev)
return 0;
}
+int pn533_finalize_setup(struct pn533 *dev)
+{
+
+ struct pn533_fw_version fw_ver;
+ int rc;
+
+ memset(&fw_ver, 0, sizeof(fw_ver));
+
+ rc = pn533_get_firmware_version(dev, &fw_ver);
+ if (rc) {
+ nfc_err(dev->dev, "Unable to get FW version\n");
+ return rc;
+ }
+
+ nfc_info(dev->dev, "NXP PN5%02X firmware ver %d.%d now attached\n",
+ fw_ver.ic, fw_ver.ver, fw_ver.rev);
+
+ rc = pn533_setup(dev);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pn533_finalize_setup);
+
struct pn533 *pn533_register_device(u32 device_type,
u32 protocols,
enum pn533_protocol_type protocol_type,
@@ -2579,7 +2604,6 @@ struct pn533 *pn533_register_device(u32 device_type,
struct device *dev,
struct device *parent)
{
- struct pn533_fw_version fw_ver;
struct pn533 *priv;
int rc = -ENOMEM;
@@ -2622,15 +2646,6 @@ struct pn533 *pn533_register_device(u32 device_type,
INIT_LIST_HEAD(&priv->cmd_queue);
- memset(&fw_ver, 0, sizeof(fw_ver));
- rc = pn533_get_firmware_version(priv, &fw_ver);
- if (rc < 0)
- goto destroy_wq;
-
- nfc_info(dev, "NXP PN5%02X firmware ver %d.%d now attached\n",
- fw_ver.ic, fw_ver.ver, fw_ver.rev);
-
-
priv->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
priv->ops->tx_header_len +
PN533_CMD_DATAEXCH_HEAD_LEN,
@@ -2647,15 +2662,8 @@ struct pn533 *pn533_register_device(u32 device_type,
if (rc)
goto free_nfc_dev;
- rc = pn533_setup(priv);
- if (rc)
- goto unregister_nfc_dev;
-
return priv;
-unregister_nfc_dev:
- nfc_unregister_device(priv->nfc_dev);
-
free_nfc_dev:
nfc_free_device(priv->nfc_dev);
diff --git a/drivers/nfc/pn533/pn533.h b/drivers/nfc/pn533/pn533.h
index 553c7d171fd1..88d569666c51 100644
--- a/drivers/nfc/pn533/pn533.h
+++ b/drivers/nfc/pn533/pn533.h
@@ -231,6 +231,7 @@ struct pn533 *pn533_register_device(u32 device_type,
struct device *dev,
struct device *parent);
+int pn533_finalize_setup(struct pn533 *dev);
void pn533_unregister_device(struct pn533 *priv);
void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status);
diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c
index 33ed78be2750..000159ea9c5f 100644
--- a/drivers/nfc/pn533/usb.c
+++ b/drivers/nfc/pn533/usb.c
@@ -543,6 +543,10 @@ static int pn533_usb_probe(struct usb_interface *interface,
phy->priv = priv;
+ rc = pn533_finalize_setup(priv);
+ if (rc)
+ goto error;
+
usb_set_intfdata(interface, phy);
return 0;
]
|
| 130/2 |
2026/08/26 14:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 42923
output: 26
thoughts: 466
Thoughts: **Analyzing a Race Condition Fix**
Okay, so commit `32ecc75ded72` rings a bell – it's all about squashing that nasty race condition during device registration. I remember the core issue: `request_threaded_irq` being called *before* the driver's private data (`phy->priv = priv;`) was fully initialized within `pn533_i2c_probe`. The dreaded immediate IRQ firing, running `pn533_i2c_irq_thread_fn`, which then tries to dereference that uninitialized `phy->priv`, resulting in a NULL pointer dereference. The solution? Reorder the operations. Make sure `phy->priv` is set *before* calling `request_threaded_irq`. Smart move splitting `pn533_register_device` too. It was refactored into `pn533_register_device` and `pn533_finalize_setup` to ensure nothing that required an interrupt was done before the interrupt was set up.
Now, my gears are turning. We have a similar scenario with `nci_register_device` called inside `nfcmrvl_nci_register_dev`. This is being called *before* `drv_data->priv = priv;`. The device is exposed to userspace, and thus open to URBs, *before* the private data is set. Upon URB completion, the handler's going to use the still-NULL `drv_data->priv`, boom, dereference and crash.
So, the pattern is solidifying:
1. **Don't expose the device until everything is ready**. That means delaying `nci_register_device` or `request_irq` until the driver data structures are fully initialized.
2. **Pass full context**. If delaying is too disruptive, pass the fully initialized `priv` structure to the asynchronous callbacks (URB completion handlers), rather than the partially initialized `drv_data`.
3. **Defensive Checks**. For IRQ handlers that might fire early, add a check for `!drv_data->priv` at the start.
Time to apply this knowledge. Let's dig into `b6355fb3f5f4 nfc: fdp: fix NULL pointer dereference`. This should be a good example to see how the pattern was handled in another case.
|
| 131/2 |
2026/08/26 14:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit b6355fb3f5f40bbce165847d277e64896cab8f95
Author: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
Date: Tue Dec 20 21:09:04 2016 +0000
nfc: fdp: fix NULL pointer dereference
We are checking phy after dereferencing it. We can print the debug
information after checking it. If phy is NULL then we will get a good
stack trace to tell us that we are in this irq handler.
Signed-off-by: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index 5e797d5c38ed..712936f5d2d6 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -210,14 +210,14 @@ static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
struct sk_buff *skb;
int r;
- client = phy->i2c_dev;
- dev_dbg(&client->dev, "%s\n", __func__);
-
if (!phy || irq != phy->i2c_dev->irq) {
WARN_ON_ONCE(1);
return IRQ_NONE;
}
+ client = phy->i2c_dev;
+ dev_dbg(&client->dev, "%s\n", __func__);
+
r = fdp_nci_i2c_read(phy, &skb);
if (r == -EREMOTEIO)
]
|
| 132/2 |
2026/08/26 14:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 44465
output: 26
thoughts: 53
Thoughts: Here's my take on the thought process, as if it were my own:
**Quick NULL Pointer Fix in an IRQ Handler**
Okay, so it looks like we've got a straightforward NULL pointer dereference fix on our hands, specifically in an Interrupt Request (IRQ) handler. That's usually a critical spot, as those can be really hairy to debug once they start. I need to make sure this is rock solid.
Let's dive into that commit, `a71a29f50de1 NFC: nxp-nci: Fix NULL pointer dereference after I2C communication error`, and see what the original issue was and what the fix entailed. Hopefully, it's well-documented with a clear explanation of the root cause, or at least a good test case attached. The context will probably matter - what's the broader NFC driver doing, and what's the interaction with the I2C bus? Gotta check for any potential side effects too, you know how these things can sometimes cascade. And most importantly, can it break something.
|
| 133/2 |
2026/08/26 14:45 |
tool |
git-show |
0m
Args:
Results: map[Output:commit a71a29f50de1ef97ab55c151a1598eb12dde379d
Author: Stephan Gerhold <stephan@gerhold.net>
Date: Sun Nov 10 17:19:15 2019 +0100
NFC: nxp-nci: Fix NULL pointer dereference after I2C communication error
I2C communication errors (-EREMOTEIO) during the IRQ handler of nxp-nci
result in a NULL pointer dereference at the moment:
BUG: kernel NULL pointer dereference, address: 0000000000000000
Oops: 0002 [#1] PREEMPT SMP NOPTI
CPU: 1 PID: 355 Comm: irq/137-nxp-nci Not tainted 5.4.0-rc6 #1
RIP: 0010:skb_queue_tail+0x25/0x50
Call Trace:
nci_recv_frame+0x36/0x90 [nci]
nxp_nci_i2c_irq_thread_fn+0xd1/0x285 [nxp_nci_i2c]
? preempt_count_add+0x68/0xa0
? irq_forced_thread_fn+0x80/0x80
irq_thread_fn+0x20/0x60
irq_thread+0xee/0x180
? wake_threads_waitq+0x30/0x30
kthread+0xfb/0x130
? irq_thread_check_affinity+0xd0/0xd0
? kthread_park+0x90/0x90
ret_from_fork+0x1f/0x40
Afterward the kernel must be rebooted to work properly again.
This happens because it attempts to call nci_recv_frame() with skb == NULL.
However, unlike nxp_nci_fw_recv_frame(), nci_recv_frame() does not have any
NULL checks for skb, causing the NULL pointer dereference.
Change the code to call only nxp_nci_fw_recv_frame() in case of an error.
Make sure to log it so it is obvious that a communication error occurred.
The error above then becomes:
nxp-nci_i2c i2c-NXP1001:00: NFC: Read failed with error -121
nci: __nci_request: wait_for_completion_interruptible_timeout failed 0
nxp-nci_i2c i2c-NXP1001:00: NFC: Read failed with error -121
Fixes: 6be88670fc59 ("NFC: nxp-nci_i2c: Add I2C support to NXP NCI driver")
Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
index 307bd2afbe05..4d1909aecd6c 100644
--- a/drivers/nfc/nxp-nci/i2c.c
+++ b/drivers/nfc/nxp-nci/i2c.c
@@ -220,8 +220,10 @@ static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
if (r == -EREMOTEIO) {
phy->hard_fault = r;
- skb = NULL;
- } else if (r < 0) {
+ if (info->mode == NXP_NCI_MODE_FW)
+ nxp_nci_fw_recv_frame(phy->ndev, NULL);
+ }
+ if (r < 0) {
nfc_err(&client->dev, "Read failed with error %d\n", r);
goto exit_irq_handled;
}
]
|
| 134/2 |
2026/08/26 14:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 45909
output: 877
thoughts: 880
Thoughts: **My Analysis of the NULL Pointer Dereference Bug**
Okay, so here's what I've got. This boils down to a pretty straightforward NULL pointer dereference, a classic race condition in device driver initialization. The problem is that `nci_register_device()` is exposing the device to userspace *before* the driver's private data structure (`drv_data->priv`) is fully set up.
Essentially, we have a "register before initialize" scenario. Userspace can bring the device up immediately, triggering the URB completion handler (`nfcmrvl_bulk_complete`) *before* `drv_data->priv` is ready, resulting in the dreaded NULL pointer dereference.
I've seen this before, and it's strikingly similar to a fix in `pn533_i2c` (commit `32ecc75ded72`), where a reordering of initialization fixed an analogous issue. The key was ensuring that `request_threaded_irq()` wasn't called until `phy->priv` was completely initialized. Ideally, we'd just reorder the initialization here as well, but because `nfcmrvl_nci_register_dev()` calls `nci_register_device()` internally, it might be more challenging.
The solution I'm leaning towards, and what I see is the most robust, is to pass the fully initialized `priv` structure to the URB context, instead of the potentially uninitialized `drv_data`. `priv` is guaranteed to be valid when `nfcmrvl_usb_nci_open()` is called, eliminating the race. That seems pretty clean.
Now, for SPI/I2C PHYs where early IRQs are a concern, the standard practice (as we see in `i2c.c`) is to add a check at the beginning of the IRQ handler: `if (!drv_data->priv) return IRQ_HANDLED;`. That's a good defensive measure, and we should definitely apply that to `spi.c` as well to prevent similar vulnerabilities.
And finally, there's that macro collision (`NFCMRVL_NCI_RUNNING` vs `NFCMRVL_USB_BULK_RUNNING`). Seems like a simple typo masked the issue on the first URB but exposed it on the second. Correcting that to check the right flag (`priv->flags`) is crucial to avoid a recurrence.
So, in short, my remediation plan includes, (1) ensuring complete initialization before exposure to userspace; (2) robust context passing for asynchronous operations and (3) a defensive check for all early-firing IRQ handlers. The macro fix is the cherry on top. This should be a fairly straightforward fix overall.
|
| 135/1 |
2026/08/26 14:45 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp2426655286]
|
| 136/1 |
2026/08/26 14:45 |
loop |
|
36m
|
| 137/2 |
2026/08/26 14:45 |
iteration |
0 |
25m
|
| 138/3 |
2026/08/26 14:45 |
agent |
patch-generator |
2m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The explanation of the root cause of the bug is:
The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
Historical context on how similar bugs were fixed in the past:
Based on the historical context and similar bugs in the NFC subsystem, the root cause of this bug is a classic **"register before initialize"** race condition. The device is exposed to userspace (via `nci_register_device()`) before its driver data structures are fully initialized (`drv_data->priv = priv`).
Here is a summary of how similar bugs were addressed in the past and the idioms that should be followed for the fix:
### 1. Reordering Initialization vs. Passing Valid Context
In the past, similar race conditions in the NFC subsystem were fixed by strictly reordering the initialization sequence so that the device or IRQ is only registered *after* all data structures are fully set up.
* For example, in commit `32ecc75ded72` ("NFC: pn533: change order operations in dev registation"), a NULL pointer dereference in `pn533_i2c` was fixed by moving `request_threaded_irq()` to happen *after* `phy->priv` was assigned.
* Similarly, commit `d270453a0d9e` ("nfc: nfcmrvl: main: reorder destructive operations...") fixed race conditions during device unregistration by strictly reordering operations relative to `nci_unregister_device()`.
However, in `nfcmrvl/usb.c`, reordering is difficult because `nci_register_device()` is called deep inside `nfcmrvl_nci_register_dev()`, which allocates and returns `priv`. Therefore, the proposed fix of **passing the fully initialized `priv` structure as the URB context** (instead of the partially initialized `drv_data`) is the correct and robust idiom here. It ensures the asynchronous callback (`nfcmrvl_bulk_complete`) always operates on valid data, completely bypassing the race window.
### 2. Early IRQ Handler Checks (SPI/I2C)
For interfaces where hardware interrupts might fire before the driver is fully bound (like SPI or I2C), the established pattern in this subsystem is to add an early return check at the top of the IRQ handler.
* If you look at `drivers/nfc/nfcmrvl/i2c.c`, this exact pattern is already implemented in `nfcmrvl_i2c_int_irq_thread_fn()`:
```c
if (!drv_data->priv)
return IRQ_HANDLED;
```
* As noted in the bug explanation, applying this exact same check to `nfcmrvl_spi_int_irq_thread_fn()` in `drivers/nfc/nfcmrvl/spi.c` is the correct historical idiom to prevent the identical race condition on the SPI PHY.
### 3. Fixing the Macro Collision Typo
The accidental check of `NFCMRVL_NCI_RUNNING` against `drv_data->flags` (instead of `priv->flags`) is a logic error that masked the bug on the first URB. Fixing this typo to correctly check `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)` is necessary and aligns with how the flags were originally intended to be separated between the PHY layer (`drv_data->flags`) and the core layer (`priv->flags`).
### Conclusion
The proposed fix strategy is perfectly aligned with prior art in the subsystem. You should proceed with:
1. Changing `nfcmrvl_submit_bulk_urb()` to take `priv` and pass it as the URB context to `usb_fill_bulk_urb()`.
2. Retrieving `priv` from `urb->context` in `nfcmrvl_bulk_complete()`.
3. Fixing the `NFCMRVL_NCI_RUNNING` typo to check `priv->flags`.
4. Adding the `if (!drv_data->priv) return IRQ_HANDLED;` safeguard to the SPI IRQ handler, mirroring the existing code in `i2c.c`.
Reply: ### Root Cause Analysis
The crash occurs due to a race condition during device registration in `nfcmrvl_probe()`, compounded by a macro collision typo in flag checking:
1. **Race Window during Initialization**:
In `nfcmrvl_probe()` (`drivers/nfc/nfcmrvl/usb.c`), the driver calls `nfcmrvl_nci_register_dev()`, which creates the `struct nfcmrvl_private` object, sets up device driver data, and calls `nci_register_device()`. Registering with NCI immediately exposes the device to userspace via Netlink.
However, `drv_data->priv = priv;` in `nfcmrvl_probe()` is executed only *after* `nfcmrvl_nci_register_dev()` returns. If userspace brings the interface up in between, `nfcmrvl_usb_nci_open()` submits bulk URBs with `drv_data` as the URB context. When an URB completes before `drv_data->priv` is assigned, `nfcmrvl_bulk_complete()` dereferences `drv_data->priv` (`NULL`), leading to a NULL pointer dereference.
2. **Flag Typo Masking the First URB Completion**:
In `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()`, the check:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
checked `drv_data->flags` instead of `priv->flags`. Because `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` are both defined as bit `1`, this erroneously tested whether `NFCMRVL_USB_BULK_RUNNING` was set on `drv_data->flags`. When opening the device, the first URB was submitted before setting `NFCMRVL_USB_BULK_RUNNING`, so the first URB completion exited early, masking the bug until the second URB was submitted.
3. **Similar Issue in SPI Driver**:
In `drivers/nfc/nfcmrvl/spi.c`, `devm_request_threaded_irq()` is called before `drv_data->priv` is assigned by `nfcmrvl_nci_register_dev()`. Unlike `drivers/nfc/nfcmrvl/i2c.c`, `nfcmrvl_spi_int_irq_thread_fn()` lacked a check for `!drv_data->priv` and could crash if an interrupt fired during registration.
---
### Solution Details
1. **Pass `priv` as URB Context**:
Modified `nfcmrvl_submit_bulk_urb()` in `drivers/nfc/nfcmrvl/usb.c` to accept `struct nfcmrvl_private *priv` rather than `drv_data`. The URB context is set to `priv`, ensuring that the completion callback `nfcmrvl_bulk_complete()` always receives the fully initialized `priv` pointer directly from `urb->context` without depending on `drv_data->priv`.
2. **Fix `NFCMRVL_NCI_RUNNING` Flag Checks**:
- In `nfcmrvl_bulk_complete()`, updated the check to `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`.
- In `nfcmrvl_resume()`, updated the check to verify `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`.
3. **Add Early Check in SPI IRQ Handler**:
In `drivers/nfc/nfcmrvl/spi.c`, added `if (!drv_data->priv) return IRQ_HANDLED;` to `nfcmrvl_spi_int_irq_thread_fn()`, mirroring the implementation in `drivers/nfc/nfcmrvl/i2c.c`.
|
| 139/4 |
2026/08/26 14:45 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 11525
output: 167
thoughts: 129
Thoughts: **Initial Assessment & Code Exploration**
Okay, let's dive into this. My first move is always to methodically approach the problem by examining the likely areas. I'll start by reading the relevant source files. Given the context, I'm expecting to find the key implementation details in `drivers/nfc/nfcmrvl/usb.c`, `drivers/nfc/nfcmrvl/nfcmrvl.h`, `drivers/nfc/nfcmrvl/main.c`, `drivers/nfc/nfcmrvl/i2c.c`, and `drivers/nfc/nfcmrvl/spi.c` — these are the obvious candidates for handling NFC communication, particularly with the Marvell chipset, and encompass different transport mechanisms.
I'll start with `drivers/nfc/nfcmrvl/usb.c` and use `read-file` to get a handle on the USB-specific implementation. I'll be looking for things like initialization, data transfer routines, endpoint configuration, error handling specific to USB, and interrupt handling.
|
| 140/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-USB driver: USB interface related functions
4: *
5: * Copyright (C) 2014, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/usb.h>
10: #include <linux/nfc.h>
11: #include <net/nfc/nci.h>
12: #include <net/nfc/nci_core.h>
13: #include "nfcmrvl.h"
14:
15: static struct usb_device_id nfcmrvl_table[] = {
16: { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
17: USB_CLASS_VENDOR_SPEC, 4, 1) },
18: { } /* Terminating entry */
19: };
20:
21: MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
22:
23: #define NFCMRVL_USB_BULK_RUNNING 1
24: #define NFCMRVL_USB_SUSPENDING 2
25:
26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
30: struct work_struct waker;
31: struct usb_anchor tx_anchor;
32: struct usb_anchor bulk_anchor;
33: struct usb_anchor deferred;
34: int tx_in_flight;
35: /* protects tx_in_flight */
36: spinlock_t txlock;
37: struct usb_endpoint_descriptor *bulk_tx_ep;
38: struct usb_endpoint_descriptor *bulk_rx_ep;
39: int suspend_count;
40: struct nfcmrvl_private *priv;
41: };
42:
43: static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
44: {
45: unsigned long flags;
46: int rv;
47:
48: spin_lock_irqsave(&drv_data->txlock, flags);
49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
100: }
]
|
| 141/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:101 LineCount:100]
Results: map[Contents: 101:
102: static int
103: nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
104: {
105: struct urb *urb;
106: unsigned char *buf;
107: unsigned int pipe;
108: int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
109:
110: if (!drv_data->bulk_rx_ep)
111: return -ENODEV;
112:
113: urb = usb_alloc_urb(0, mem_flags);
114: if (!urb)
115: return -ENOMEM;
116:
117: buf = kmalloc(size, mem_flags);
118: if (!buf) {
119: usb_free_urb(urb);
120: return -ENOMEM;
121: }
122:
123: pipe = usb_rcvbulkpipe(drv_data->udev,
124: drv_data->bulk_rx_ep->bEndpointAddress);
125:
126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
127: nfcmrvl_bulk_complete, drv_data);
128:
129: urb->transfer_flags |= URB_FREE_BUFFER;
130:
131: usb_mark_last_busy(drv_data->udev);
132: usb_anchor_urb(urb, &drv_data->bulk_anchor);
133:
134: err = usb_submit_urb(urb, mem_flags);
135: if (err) {
136: if (err != -EPERM && err != -ENODEV)
137: nfc_err(&drv_data->udev->dev,
138: "urb %p submission failed (%d)\n", urb, -err);
139: usb_unanchor_urb(urb);
140: }
141:
142: usb_free_urb(urb);
143:
144: return err;
145: }
146:
147: static void nfcmrvl_tx_complete(struct urb *urb)
148: {
149: struct sk_buff *skb = urb->context;
150: struct nci_dev *ndev = (struct nci_dev *)skb->dev;
151: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
152: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
153: unsigned long flags;
154:
155: nfc_info(priv->dev, "urb %p status %d count %d\n",
156: urb, urb->status, urb->actual_length);
157:
158: spin_lock_irqsave(&drv_data->txlock, flags);
159: drv_data->tx_in_flight--;
160: spin_unlock_irqrestore(&drv_data->txlock, flags);
161:
162: kfree(urb->setup_packet);
163: kfree_skb(skb);
164: }
165:
166: static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
167: {
168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
169: int err;
170:
171: err = usb_autopm_get_interface(drv_data->intf);
172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
188: usb_autopm_put_interface(drv_data->intf);
189: return err;
190: }
191:
192: static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
193: {
194: usb_kill_anchored_urbs(&drv_data->bulk_anchor);
195: }
196:
197: static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
198: {
199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
200: int err;
]
|
| 142/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:201 LineCount:100]
Results: map[Contents: 201:
202: cancel_work_sync(&drv_data->waker);
203:
204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
205:
206: nfcmrvl_usb_stop_traffic(drv_data);
207: usb_kill_anchored_urbs(&drv_data->tx_anchor);
208: err = usb_autopm_get_interface(drv_data->intf);
209: if (err)
210: goto failed;
211:
212: drv_data->intf->needs_remote_wakeup = 0;
213: usb_autopm_put_interface(drv_data->intf);
214:
215: failed:
216: usb_scuttle_anchored_urbs(&drv_data->deferred);
217: return 0;
218: }
219:
220: static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
221: struct sk_buff *skb)
222: {
223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
224: struct urb *urb;
225: unsigned int pipe;
226: int err;
227:
228: if (!drv_data->bulk_tx_ep)
229: return -ENODEV;
230:
231: urb = usb_alloc_urb(0, GFP_ATOMIC);
232: if (!urb)
233: return -ENOMEM;
234:
235: pipe = usb_sndbulkpipe(drv_data->udev,
236: drv_data->bulk_tx_ep->bEndpointAddress);
237:
238: usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
239: nfcmrvl_tx_complete, skb);
240:
241: err = nfcmrvl_inc_tx(drv_data);
242: if (err) {
243: usb_anchor_urb(urb, &drv_data->deferred);
244: schedule_work(&drv_data->waker);
245: err = 0;
246: goto done;
247: }
248:
249: usb_anchor_urb(urb, &drv_data->tx_anchor);
250:
251: err = usb_submit_urb(urb, GFP_ATOMIC);
252: if (err) {
253: if (err != -EPERM && err != -ENODEV)
254: nfc_err(&drv_data->udev->dev,
255: "urb %p submission failed (%d)\n", urb, -err);
256: kfree(urb->setup_packet);
257: usb_unanchor_urb(urb);
258: } else {
259: usb_mark_last_busy(drv_data->udev);
260: }
261:
262: done:
263: usb_free_urb(urb);
264: return err;
265: }
266:
267: static const struct nfcmrvl_if_ops usb_ops = {
268: .nci_open = nfcmrvl_usb_nci_open,
269: .nci_close = nfcmrvl_usb_nci_close,
270: .nci_send = nfcmrvl_usb_nci_send,
271: };
272:
273: static void nfcmrvl_waker(struct work_struct *work)
274: {
275: struct nfcmrvl_usb_drv_data *drv_data =
276: container_of(work, struct nfcmrvl_usb_drv_data, waker);
277: int err;
278:
279: err = usb_autopm_get_interface(drv_data->intf);
280: if (err)
281: return;
282:
283: usb_autopm_put_interface(drv_data->intf);
284: }
285:
286: static int nfcmrvl_probe(struct usb_interface *intf,
287: const struct usb_device_id *id)
288: {
289: struct nfcmrvl_usb_drv_data *drv_data;
290: struct nfcmrvl_private *priv;
291: struct usb_device *udev = interface_to_usbdev(intf);
292: struct nfcmrvl_platform_data config;
293: int ret;
294:
295: /* No configuration for USB */
296: memset(&config, 0, sizeof(config));
297: config.reset_gpio = NULL;
298:
299: nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
300:
]
|
| 143/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:1 LineCount:100]
Results: map[Contents: 1: /* SPDX-License-Identifier: GPL-2.0-only */
2: /*
3: * Marvell NFC driver
4: *
5: * Copyright (C) 2014-2015, Marvell International Ltd.
6: */
7:
8: #ifndef _NFCMRVL_H_
9: #define _NFCMRVL_H_
10:
11: #include "fw_dnld.h"
12:
13: struct gpio_desc;
14:
15: /* Define private flags: */
16: #define NFCMRVL_NCI_RUNNING 1
17: #define NFCMRVL_PHY_ERROR 2
18:
19: #define NFCMRVL_EXT_COEX_ID 0xE0
20: #define NFCMRVL_NOT_ALLOWED_ID 0xE1
21: #define NFCMRVL_ACTIVE_ID 0xE2
22: #define NFCMRVL_EXT_COEX_ENABLE 1
23: #define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA
24: #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB
25: #define NFCMRVL_NCI_MAX_EVENT_SIZE 260
26:
27: /*
28: * NCI FW Parameters
29: */
30:
31: #define NFCMRVL_PB_BAIL_OUT 0x11
32: #define NFCMRVL_PROP_REF_CLOCK 0xF0
33: #define NFCMRVL_PROP_SET_HI_CONFIG 0xF1
34:
35: /*
36: * HCI defines
37: */
38:
39: #define NFCMRVL_HCI_EVENT_HEADER_SIZE 0x04
40: #define NFCMRVL_HCI_EVENT_CODE 0x04
41: #define NFCMRVL_HCI_NFC_EVENT_CODE 0xFF
42: #define NFCMRVL_HCI_COMMAND_CODE 0x01
43: #define NFCMRVL_HCI_OGF 0x81
44: #define NFCMRVL_HCI_OCF 0xFE
45:
46: enum nfcmrvl_phy {
47: NFCMRVL_PHY_USB = 0,
48: NFCMRVL_PHY_UART = 1,
49: NFCMRVL_PHY_I2C = 2,
50: NFCMRVL_PHY_SPI = 3,
51: };
52:
53: struct nfcmrvl_platform_data {
54: /*
55: * Generic
56: */
57:
58: /* GPIO that is wired to RESET_N signal */
59: struct gpio_desc *reset_gpio;
60: /* Tell if transport is muxed in HCI one */
61: bool hci_muxed;
62:
63: /*
64: * UART specific
65: */
66:
67: /* Tell if UART needs flow control at init */
68: bool flow_control;
69: /* Tell if firmware supports break control for power management */
70: bool break_control;
71:
72:
73: /*
74: * I2C specific
75: */
76:
77: unsigned int irq;
78: unsigned int irq_polarity;
79: };
80:
81: struct nfcmrvl_private {
82:
83: unsigned long flags;
84:
85: /* Platform configuration */
86: struct nfcmrvl_platform_data config;
87:
88: /* Parent dev */
89: struct nci_dev *ndev;
90:
91: /* FW download context */
92: struct nfcmrvl_fw_dnld fw_dnld;
93:
94: /* FW download support */
95: bool support_fw_dnld;
96:
97: /*
98: * PHY related information
99: */
100:
]
|
| 144/4 |
2026/08/26 14:45 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 17841
output: 168
thoughts: 88
Thoughts: Okay, here's my take on those files as an expert in the field.
**Deep Dive into the Marvell NFC Driver Implementation**
Alright, time to get my hands dirty. I need to take a look at the core of this Marvell NFC driver implementation. My immediate focus is going to be spread across a few files to get the full picture. I want to see how this thing interacts with the host system, focusing on the various physical interfaces.
I'll start with `usb.c`. I'm going to be looking for the standard USB descriptor parsing, initialization routines, endpoint management, and the control transfer handling. I'll be digging in to understand the nuances of this specific Marvell chip's USB protocol. The main goal here is to grasp the data flow and how the host interacts with the NFC controller over USB.
Then, I'll need to reference `nfcmrvl.h`. This is where the core definitions and structures will be. I'll be paying close attention to the registers, command/response structures, and any Marvell-specific data structures or constants. This will give me the building blocks to understand the driver's operation.
Next, I need to see how the driver handles other interfaces, so I'll also read the `spi.c` and `i2c.c` files. I'll compare and contrast the different implementations, identifying common code sections or abstractions and the specific low-level interactions for SPI and I2C. How are interrupts handled? What are the clocking and timing considerations for each interface? I'll be looking for anything interface-specific and how the driver adapts to each.
Finally, I need to tie everything together. `main.c` is going to be crucial. This is where the driver initialization, probe, and removal functions are located. I'll analyze how the driver is registered with the system, how it probes for the NFC controller, and what resource management strategies are used. I'll look for any power management related code too. This will give me the overarching architecture and how the specific interface implementations slot into the broader driver framework.
I'm expecting to see a standard, modular design, but the specifics of the Marvell controller will be the key. I want to understand how they’ve implemented their command set and, especially, how they manage the critical timing required for NFC operations. I'll also be watching for any unique features or optimizations that this driver might have.
|
| 145/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:301 LineCount:100]
Results: map[Contents: 301: drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
302: if (!drv_data)
303: return -ENOMEM;
304:
305: ret = usb_find_common_endpoints(intf->cur_altsetting, &drv_data->bulk_rx_ep,
306: &drv_data->bulk_tx_ep, NULL, NULL);
307: if (ret)
308: return -ENODEV;
309:
310: drv_data->udev = udev;
311: drv_data->intf = intf;
312:
313: INIT_WORK(&drv_data->waker, nfcmrvl_waker);
314: spin_lock_init(&drv_data->txlock);
315:
316: init_usb_anchor(&drv_data->tx_anchor);
317: init_usb_anchor(&drv_data->bulk_anchor);
318: init_usb_anchor(&drv_data->deferred);
319:
320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
332:
333: static void nfcmrvl_disconnect(struct usb_interface *intf)
334: {
335: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
336:
337: if (!drv_data)
338: return;
339:
340: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
341:
342: nfcmrvl_nci_unregister_dev(drv_data->priv);
343:
344: usb_set_intfdata(drv_data->intf, NULL);
345: }
346:
347: #ifdef CONFIG_PM
348: static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
349: {
350: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
351:
352: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
353:
354: if (drv_data->suspend_count++)
355: return 0;
356:
357: spin_lock_irq(&drv_data->txlock);
358: if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
359: set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
360: spin_unlock_irq(&drv_data->txlock);
361: } else {
362: spin_unlock_irq(&drv_data->txlock);
363: drv_data->suspend_count--;
364: return -EBUSY;
365: }
366:
367: nfcmrvl_usb_stop_traffic(drv_data);
368: usb_kill_anchored_urbs(&drv_data->tx_anchor);
369:
370: return 0;
371: }
372:
373: static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
374: {
375: struct urb *urb;
376: int err;
377:
378: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
379: usb_anchor_urb(urb, &drv_data->tx_anchor);
380:
381: err = usb_submit_urb(urb, GFP_ATOMIC);
382: if (err) {
383: kfree(urb->setup_packet);
384: usb_unanchor_urb(urb);
385: usb_free_urb(urb);
386: break;
387: }
388:
389: drv_data->tx_in_flight++;
390: usb_free_urb(urb);
391: }
392:
393: /* Cleanup the rest deferred urbs. */
394: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
395: kfree(urb->setup_packet);
396: usb_free_urb(urb);
397: }
398: }
399:
400: static int nfcmrvl_resume(struct usb_interface *intf)
]
|
| 146/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:101 LineCount:50]
Results: map[Contents: 101: /* PHY driver context */
102: void *drv_data;
103: /* PHY device */
104: struct device *dev;
105: /* PHY type */
106: enum nfcmrvl_phy phy;
107: /* Low level driver ops */
108: const struct nfcmrvl_if_ops *if_ops;
109: };
110:
111: struct nfcmrvl_if_ops {
112: int (*nci_open) (struct nfcmrvl_private *priv);
113: int (*nci_close) (struct nfcmrvl_private *priv);
114: int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb);
115: void (*nci_update_config)(struct nfcmrvl_private *priv,
116: const void *param);
117: };
118:
119: void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
120: int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
121: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
122: void *drv_data,
123: const struct nfcmrvl_if_ops *ops,
124: struct device *dev,
125: const struct nfcmrvl_platform_data *pdata);
126:
127:
128: void nfcmrvl_chip_reset(struct nfcmrvl_private *priv);
129: void nfcmrvl_chip_halt(struct nfcmrvl_private *priv);
130:
131: int nfcmrvl_parse_dt(struct device_node *node,
132: struct nfcmrvl_platform_data *pdata);
133:
134: #endif
]
|
| 147/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-SPI driver: SPI interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/nfc.h>
11: #include <linux/of_irq.h>
12: #include <net/nfc/nci.h>
13: #include <net/nfc/nci_core.h>
14: #include <linux/spi/spi.h>
15: #include "nfcmrvl.h"
16:
17: #define SPI_WAIT_HANDSHAKE 1
18:
19: struct nfcmrvl_spi_drv_data {
20: unsigned long flags;
21: struct spi_device *spi;
22: struct nci_spi *nci_spi;
23: struct completion handshake_completion;
24: struct nfcmrvl_private *priv;
25: };
26:
27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
45: nfc_err(&drv_data->spi->dev, "failed to read spi packet");
46: return IRQ_HANDLED;
47: }
48:
49: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
50: nfc_err(&drv_data->spi->dev, "corrupted RX packet");
51:
52: return IRQ_HANDLED;
53: }
54:
55: static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
56: {
57: return 0;
58: }
59:
60: static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
61: {
62: return 0;
63: }
64:
65: static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
66: struct sk_buff *skb)
67: {
68: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
69: int err;
70:
71: /* Reinit completion for slave handshake */
72: reinit_completion(&drv_data->handshake_completion);
73: set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
74:
75: /*
76: * Append a dummy byte at the end of SPI frame. This is due to a
77: * specific DMA implementation in the controller
78: */
79: skb_put(skb, 1);
80:
81: /* Send the SPI packet */
82: err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
83: skb);
84: if (err)
85: nfc_err(priv->dev, "spi_send failed %d", err);
86:
87: return err;
88: }
89:
90: static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv,
91: const void *param)
92: {
93: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
94: const struct nfcmrvl_fw_spi_config *config = param;
95:
96: drv_data->nci_spi->xfer_speed_hz = config->clk;
97: }
98:
99: static const struct nfcmrvl_if_ops spi_ops = {
100: .nci_open = nfcmrvl_spi_nci_open,
]
|
| 148/4 |
2026/08/26 14:45 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-I2C driver: I2C interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/i2c.h>
11: #include <linux/nfc.h>
12: #include <linux/delay.h>
13: #include <linux/of_irq.h>
14: #include <net/nfc/nci.h>
15: #include <net/nfc/nci_core.h>
16: #include "nfcmrvl.h"
17:
18: struct nfcmrvl_i2c_drv_data {
19: unsigned long flags;
20: struct device *dev;
21: struct i2c_client *i2c;
22: struct nfcmrvl_private *priv;
23: };
24:
25: static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data,
26: struct sk_buff **skb)
27: {
28: int ret;
29: struct nci_ctrl_hdr nci_hdr;
30:
31: /* Read NCI header to know the payload size */
32: ret = i2c_master_recv(drv_data->i2c, (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE);
33: if (ret != NCI_CTRL_HDR_SIZE) {
34: nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n");
35: return -EBADMSG;
36: }
37:
38: *skb = nci_skb_alloc(drv_data->priv->ndev,
39: nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL);
40: if (!*skb)
41: return -ENOMEM;
42:
43: /* Copy NCI header into the SKB */
44: skb_put_data(*skb, &nci_hdr, NCI_CTRL_HDR_SIZE);
45:
46: if (nci_hdr.plen) {
47: /* Read the NCI payload */
48: ret = i2c_master_recv(drv_data->i2c,
49: skb_put(*skb, nci_hdr.plen),
50: nci_hdr.plen);
51:
52: if (ret != nci_hdr.plen) {
53: nfc_err(&drv_data->i2c->dev,
54: "Invalid frame payload length: %u (expected %u)\n",
55: ret, nci_hdr.plen);
56: kfree_skb(*skb);
57: return -EBADMSG;
58: }
59: }
60:
61: return 0;
62: }
63:
64: static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
65: {
66: struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
67: struct sk_buff *skb = NULL;
68: int ret;
69:
70: if (!drv_data->priv)
71: return IRQ_HANDLED;
72:
73: if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
74: return IRQ_HANDLED;
75:
76: ret = nfcmrvl_i2c_read(drv_data, &skb);
77:
78: switch (ret) {
79: case -EREMOTEIO:
80: set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
81: break;
82: case -ENOMEM:
83: case -EBADMSG:
84: nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
85: break;
86: default:
87: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
88: nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
89: break;
90: }
91: return IRQ_HANDLED;
92: }
93:
94: static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
95: {
96: struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
97:
98: if (!drv_data)
99: return -ENODEV;
100:
]
|
| 149/4 |
2026/08/26 14:45 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23719
output: 123
thoughts: 70
Thoughts: Here's my summary, as if I were thinking these thoughts:
**Analyzing NFC Driver Resume and Initialization**
Okay, I need to dig into the NFC driver's resume and initialization routines. My initial focus was `drivers/nfc/nfcmrvl/usb.c` around line 400, specifically the `nfcmrvl_resume` function. I’m thinking there might be something specific happening in that USB context that’s relevant to the issue I'm tracking down. But, let's broaden the scope. I know this Marvell implementation has both USB and SPI interfaces, so I need to check the equivalent functionality in `drivers/nfc/nfcmrvl/spi.c`. I expect the key logic to be similar, so I'll be looking for shared code or common patterns.
Further, I'll need to go to the `drivers/nfc/nfcmrvl/main.c` file as well. This will provide context, especially regarding the driver's overall initialization sequence, device probing, and module loading. I'll need to understand how these different pieces - the USB, SPI, and main control logic - interrelate during system resume. I need the full picture to understand what might be going awry. So, it's a deep dive across multiple files to understand the control flow.
|
| 150/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:400 LineCount:50]
Results: map[Contents: 400: static int nfcmrvl_resume(struct usb_interface *intf)
401: {
402: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
403: int err = 0;
404:
405: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
406:
407: if (--drv_data->suspend_count)
408: return 0;
409:
410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
411: goto done;
412:
413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
421: }
422:
423: spin_lock_irq(&drv_data->txlock);
424: nfcmrvl_play_deferred(drv_data);
425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
426: spin_unlock_irq(&drv_data->txlock);
427:
428: return 0;
429:
430: failed:
431: usb_scuttle_anchored_urbs(&drv_data->deferred);
432: done:
433: spin_lock_irq(&drv_data->txlock);
434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
435: spin_unlock_irq(&drv_data->txlock);
436:
437: return err;
438: }
439: #endif
440:
441: static struct usb_driver nfcmrvl_usb_driver = {
442: .name = "nfcmrvl",
443: .probe = nfcmrvl_probe,
444: .disconnect = nfcmrvl_disconnect,
445: #ifdef CONFIG_PM
446: .suspend = nfcmrvl_suspend,
447: .resume = nfcmrvl_resume,
448: .reset_resume = nfcmrvl_resume,
449: #endif
]
|
| 151/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:100 LineCount:100]
Results: map[Contents: 100: .nci_open = nfcmrvl_spi_nci_open,
101: .nci_close = nfcmrvl_spi_nci_close,
102: .nci_send = nfcmrvl_spi_nci_send,
103: .nci_update_config = nfcmrvl_spi_nci_update_config,
104: };
105:
106: static int nfcmrvl_spi_parse_dt(struct device_node *node,
107: struct nfcmrvl_platform_data *pdata)
108: {
109: int ret;
110:
111: ret = nfcmrvl_parse_dt(node, pdata);
112: if (ret < 0) {
113: pr_err("Failed to get generic entries\n");
114: return ret;
115: }
116:
117: ret = irq_of_parse_and_map(node, 0);
118: if (!ret) {
119: pr_err("Unable to get irq\n");
120: return -EINVAL;
121: }
122: pdata->irq = ret;
123:
124: return 0;
125: }
126:
127: static int nfcmrvl_spi_probe(struct spi_device *spi)
128: {
129: const struct nfcmrvl_platform_data *pdata;
130: struct nfcmrvl_platform_data config;
131: struct nfcmrvl_spi_drv_data *drv_data;
132: int ret = 0;
133:
134: drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL);
135: if (!drv_data)
136: return -ENOMEM;
137:
138: drv_data->spi = spi;
139: drv_data->priv = NULL;
140: spi_set_drvdata(spi, drv_data);
141:
142: pdata = spi->dev.platform_data;
143:
144: if (!pdata && spi->dev.of_node)
145: if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0)
146: pdata = &config;
147:
148: if (!pdata)
149: return -EINVAL;
150:
151: ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
152: NULL, nfcmrvl_spi_int_irq_thread_fn,
153: IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
154: "nfcmrvl_spi_int", drv_data);
155: if (ret < 0) {
156: nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler");
157: return -ENODEV;
158: }
159:
160: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
161: drv_data, &spi_ops,
162: &drv_data->spi->dev,
163: pdata);
164: if (IS_ERR(drv_data->priv))
165: return PTR_ERR(drv_data->priv);
166:
167: drv_data->priv->support_fw_dnld = true;
168:
169: drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10,
170: drv_data->priv->ndev);
171:
172: /* Init completion for slave handshake */
173: init_completion(&drv_data->handshake_completion);
174: return 0;
175: }
176:
177: static void nfcmrvl_spi_remove(struct spi_device *spi)
178: {
179: struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi);
180:
181: nfcmrvl_nci_unregister_dev(drv_data->priv);
182: }
183:
184: static const struct of_device_id of_nfcmrvl_spi_match[] __maybe_unused = {
185: { .compatible = "marvell,nfc-spi", },
186: {},
187: };
188: MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match);
189:
190: static const struct spi_device_id nfcmrvl_spi_id_table[] = {
191: { "nfcmrvl_spi", 0 },
192: { }
193: };
194: MODULE_DEVICE_TABLE(spi, nfcmrvl_spi_id_table);
195:
196: static struct spi_driver nfcmrvl_spi_driver = {
197: .probe = nfcmrvl_spi_probe,
198: .remove = nfcmrvl_spi_remove,
199: .id_table = nfcmrvl_spi_id_table,
]
|
| 152/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC driver: major functions
4: *
5: * Copyright (C) 2014-2015 Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/gpio/consumer.h>
10: #include <linux/delay.h>
11: #include <linux/of.h>
12: #include <linux/nfc.h>
13: #include <net/nfc/nci.h>
14: #include <net/nfc/nci_core.h>
15: #include "nfcmrvl.h"
16:
17: static int nfcmrvl_nci_open(struct nci_dev *ndev)
18: {
19: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
20: int err;
21:
22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
23: return 0;
24:
25: /* Reset possible fault of previous session */
26: clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
27:
28: err = priv->if_ops->nci_open(priv);
29:
30: if (err)
31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
32:
33: return err;
34: }
35:
36: static int nfcmrvl_nci_close(struct nci_dev *ndev)
37: {
38: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
39:
40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
41: return 0;
42:
43: priv->if_ops->nci_close(priv);
44:
45: return 0;
46: }
47:
48: static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
49: {
50: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
51:
52: nfc_info(priv->dev, "send entry, len %d\n", skb->len);
53:
54: skb->dev = (void *)ndev;
55:
56: if (priv->config.hci_muxed) {
57: unsigned char *hdr;
58: unsigned char len = skb->len;
59:
60: hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
61: hdr[0] = NFCMRVL_HCI_COMMAND_CODE;
62: hdr[1] = NFCMRVL_HCI_OGF;
63: hdr[2] = NFCMRVL_HCI_OCF;
64: hdr[3] = len;
65: }
66:
67: return priv->if_ops->nci_send(priv, skb);
68: }
69:
70: static int nfcmrvl_nci_setup(struct nci_dev *ndev)
71: {
72: __u8 val = 1;
73:
74: nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val);
75: return 0;
76: }
77:
78: static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
79: const char *firmware_name)
80: {
81: return nfcmrvl_fw_dnld_start(ndev, firmware_name);
82: }
83:
84: static const struct nci_ops nfcmrvl_nci_ops = {
85: .open = nfcmrvl_nci_open,
86: .close = nfcmrvl_nci_close,
87: .send = nfcmrvl_nci_send,
88: .setup = nfcmrvl_nci_setup,
89: .fw_download = nfcmrvl_nci_fw_download,
90: };
91:
92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
]
|
| 153/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 28099
output: 42
thoughts: 25
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving Deeper into nfcmrvl/main.c**
Alright, so I've been poking around in the NFC driver code, and now I'm thinking it's time to fully immerse myself in `drivers/nfc/nfcmrvl/main.c`. I know this is the entry point, the central hub for the Marvell NFC controller. I need to understand the initial setup, initialization routines, and how it interacts with the broader NFC subsystem within the kernel. I'll be looking for things like probe functions, module loading and unloading, interrupt handling registration, and any crucial data structures that define the device's state. I'm especially keen to see how they handle power management and error conditions at the driver's core. Digging into `main.c` is the next logical step to get the complete picture. Hopefully, I'll identify the key interaction points with the rest of the NFC stack and get a handle on the driver's operational flow.
|
| 154/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:101 LineCount:100]
Results: map[Contents: 101: int tailroom;
102: u32 protocols;
103:
104: priv = kzalloc_obj(*priv);
105: if (!priv)
106: return ERR_PTR(-ENOMEM);
107:
108: priv->drv_data = drv_data;
109: priv->if_ops = ops;
110: priv->dev = dev;
111: priv->phy = phy;
112:
113: memcpy(&priv->config, pdata, sizeof(*pdata));
114:
115: if (!priv->config.reset_gpio) {
116: priv->config.reset_gpio =
117: devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
118: if (IS_ERR(priv->config.reset_gpio)) {
119: priv->config.reset_gpio = NULL;
120: nfc_err(dev, "failed to get reset gpio\n");
121: }
122: }
123:
124: if (phy == NFCMRVL_PHY_SPI) {
125: headroom = NCI_SPI_HDR_LEN;
126: tailroom = 1;
127: } else
128: headroom = tailroom = 0;
129:
130: if (priv->config.hci_muxed)
131: headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
132:
133: protocols = NFC_PROTO_JEWEL_MASK
134: | NFC_PROTO_MIFARE_MASK
135: | NFC_PROTO_FELICA_MASK
136: | NFC_PROTO_ISO14443_MASK
137: | NFC_PROTO_ISO14443_B_MASK
138: | NFC_PROTO_ISO15693_MASK
139: | NFC_PROTO_NFC_DEP_MASK;
140:
141: priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
142: headroom, tailroom);
143: if (!priv->ndev) {
144: nfc_err(dev, "nci_allocate_device failed\n");
145: rc = -ENOMEM;
146: goto error_free;
147: }
148:
149: rc = nfcmrvl_fw_dnld_init(priv);
150: if (rc) {
151: nfc_err(dev, "failed to initialize FW download %d\n", rc);
152: goto error_free_dev;
153: }
154:
155: nci_set_drvdata(priv->ndev, priv);
156:
157: rc = nci_register_device(priv->ndev);
158: if (rc) {
159: nfc_err(dev, "nci_register_device failed %d\n", rc);
160: goto error_fw_dnld_deinit;
161: }
162:
163: /* Ensure that controller is powered off */
164: nfcmrvl_chip_halt(priv);
165:
166: nfc_info(dev, "registered with nci successfully\n");
167: return priv;
168:
169: error_fw_dnld_deinit:
170: nfcmrvl_fw_dnld_deinit(priv);
171: error_free_dev:
172: nci_free_device(priv->ndev);
173: error_free:
174: kfree(priv);
175: return ERR_PTR(rc);
176: }
177: EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
178:
179: void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
180: {
181: struct nci_dev *ndev = priv->ndev;
182:
183: nci_unregister_device(ndev);
184: if (priv->ndev->nfc_dev->fw_download_in_progress)
185: nfcmrvl_fw_dnld_abort(priv);
186:
187: nfcmrvl_fw_dnld_deinit(priv);
188:
189: nci_free_device(ndev);
190: kfree(priv);
191: }
192: EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
193:
194: int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
195: {
196: if (priv->config.hci_muxed) {
197: if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE &&
198: skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) {
199: /* Data packet, let's extract NCI payload */
200: skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
]
|
| 155/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 29971
output: 27
thoughts: 44
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Investigating the NFC Marvell Driver Implementation**
Right, let's dive back into this NFC Marvell driver code. I need to make sure I'm considering all relevant locations. Specifically, I'm thinking I should also investigate `drivers/nfc/nfcmrvl/uart.c` or any other files within the `drivers/nfc/nfcmrvl/` directory. It's always smart to have a solid overview of the codebase at your fingertips, so let's start by listing the contents of that directory to get a better sense of what's there. That should help me identify the most likely areas for the issue I'm chasing. Understanding the overall structure is key before I start digging into the details of any particular file.
|
| 156/4 |
2026/08/26 14:46 |
tool |
codesearch-dir-index |
0m
Args: map[Dir:drivers/nfc/nfcmrvl]
Results: map[Files:[fw_dnld.c fw_dnld.h i2c.c main.c nfcmrvl.h spi.c uart.c usb.c] Subdirs:<nil>]
|
| 157/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 30291
output: 40
thoughts: 22
Thoughts: Here's a summary of my thought process:
**Diving into the UART Implementation**
Okay, so I need to understand how the UART driver manages private data, interrupt handling, and registration within the system. My initial thought is to go straight to `uart.c`. That's the logical place to start digging into the core implementation details. I'm specifically interested in how the driver interacts with the private data structures, how it handles interrupt service routines, and the overall registration process within the larger framework. Let's see how they've structured things there. Time to dig in and see how they've solved these problems.
|
| 158/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/uart.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-UART driver
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/delay.h>
9: #include <linux/device.h>
10: #include <linux/err.h>
11: #include <linux/gpio/consumer.h>
12: #include <linux/module.h>
13: #include <linux/of.h>
14: #include <linux/printk.h>
15:
16: #include <net/nfc/nci.h>
17: #include <net/nfc/nci_core.h>
18:
19: #include "nfcmrvl.h"
20:
21: static unsigned int hci_muxed;
22: static unsigned int flow_control;
23: static unsigned int break_control;
24:
25: /*
26: * NFCMRVL NCI OPS
27: */
28:
29: static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv)
30: {
31: return 0;
32: }
33:
34: static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv)
35: {
36: return 0;
37: }
38:
39: static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
40: struct sk_buff *skb)
41: {
42: struct nci_uart *nu = priv->drv_data;
43:
44: return nu->ops.send(nu, skb);
45: }
46:
47: static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
48: const void *param)
49: {
50: struct nci_uart *nu = priv->drv_data;
51: const struct nfcmrvl_fw_uart_config *config = param;
52:
53: nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
54: config->flow_control);
55: }
56:
57: static const struct nfcmrvl_if_ops uart_ops = {
58: .nci_open = nfcmrvl_uart_nci_open,
59: .nci_close = nfcmrvl_uart_nci_close,
60: .nci_send = nfcmrvl_uart_nci_send,
61: .nci_update_config = nfcmrvl_uart_nci_update_config
62: };
63:
64: static int nfcmrvl_uart_parse_dt(struct device_node *node,
65: struct nfcmrvl_platform_data *pdata,
66: struct device *dev)
67: {
68: struct device_node *matched_node;
69: struct gpio_desc *reset_gpio;
70: int ret;
71:
72: matched_node = of_get_compatible_child(node, "marvell,nfc-uart");
73: if (!matched_node) {
74: matched_node = of_get_compatible_child(node, "mrvl,nfc-uart");
75: if (!matched_node)
76: return -ENODEV;
77: }
78:
79: ret = nfcmrvl_parse_dt(matched_node, pdata);
80: if (ret < 0) {
81: pr_err("Failed to get generic entries\n");
82: of_node_put(matched_node);
83: return ret;
84: }
85:
86: pdata->flow_control = of_property_read_bool(matched_node, "flow-control");
87: pdata->break_control = of_property_read_bool(matched_node, "break-control");
88:
89: reset_gpio = devm_fwnode_gpiod_get_optional(dev,
90: of_fwnode_handle(matched_node),
91: "reset", GPIOD_OUT_HIGH,
92: "nfcmrvl_reset_n");
93: if (IS_ERR(reset_gpio)) {
94: of_node_put(matched_node);
95: return PTR_ERR(reset_gpio);
96: }
97: pdata->reset_gpio = reset_gpio;
98:
99: of_node_put(matched_node);
100:
]
|
| 159/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 31909
output: 41
thoughts: 25
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Deep Dive into `uart.c` for Maximum Thoroughness**
Okay, I'm already pretty familiar with the overall architecture, but to be absolutely certain I haven't missed a critical nuance in the UART implementation, I need to get into the weeds, and to be as complete as possible. Therefore, I'm going to take a closer look at lines 101 through 160 of `uart.c`. This isn't just a cursory glance; I want to confirm my understanding of the interrupt handling, any potentially subtle buffering schemes, and any likely edge case handling within that specific segment. I know the likely areas of concern, but a complete review is still the best way to verify there are no hidden assumptions and that everything is behaving as I expect. It's a quick read, given my understanding, and I might catch something key, and can't hurt.
|
| 160/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/uart.c FirstLine:101 LineCount:60]
Results: map[Contents: 101: return 0;
102: }
103:
104: /*
105: * NCI UART OPS
106: */
107:
108: static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
109: {
110: struct nfcmrvl_private *priv;
111: struct nfcmrvl_platform_data config;
112: const struct nfcmrvl_platform_data *pdata = NULL;
113: struct device *dev = nu->tty->dev;
114:
115: /*
116: * Platform data cannot be used here since usually it is already used
117: * by low level serial driver. We can try to retrieve serial device
118: * and check if DT entries were added.
119: */
120:
121: if (dev && dev->parent && dev->parent->of_node)
122: if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config, dev) == 0)
123: pdata = &config;
124:
125: if (!pdata) {
126: pr_info("No platform data / DT -> fallback to module params\n");
127: config.hci_muxed = hci_muxed;
128: config.reset_gpio = NULL;
129: config.flow_control = flow_control;
130: config.break_control = break_control;
131: pdata = &config;
132: }
133:
134: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
135: dev, pdata);
136: if (IS_ERR(priv))
137: return PTR_ERR(priv);
138:
139: priv->support_fw_dnld = true;
140:
141: nu->drv_data = priv;
142: nu->ndev = priv->ndev;
143:
144: return 0;
145: }
146:
147: static void nfcmrvl_nci_uart_close(struct nci_uart *nu)
148: {
149: nfcmrvl_nci_unregister_dev((struct nfcmrvl_private *)nu->drv_data);
150: }
151:
152: static int nfcmrvl_nci_uart_recv(struct nci_uart *nu, struct sk_buff *skb)
153: {
154: return nfcmrvl_nci_recv_frame((struct nfcmrvl_private *)nu->drv_data,
155: skb);
156: }
157:
158: static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu)
159: {
160: struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
]
|
| 161/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 33107
output: 30
thoughts: 2534
Thoughts: **Analysis of USB Driver Code for Potential Bug**
Okay, let's dive into the `usb.c` file and start digging. My initial focus will be on the bulk URB handling, specifically around `nfcmrvl_submit_bulk_urb()` and its interaction with `nfcmrvl_bulk_complete()`. The first thing that stands out is the allocation of URBs and buffers within `nfcmrvl_submit_bulk_urb()`, the pipe setup, and the subsequent submission with `usb_submit_urb()`. The use of `URB_FREE_BUFFER` is a good practice, ensuring the buffer gets freed correctly upon completion. The `usb_anchor_urb()` and `usb_unanchor_urb()` calls are also standard practice for managing URBs and preventing race conditions, but must be checked for correct operation in conjunction with the other functions.
Next, I examined the completion routine, `nfcmrvl_bulk_complete()`. This is where the received data is processed, and the URB is resubmitted. The critical parts here are the checks on `urb->status`, the allocation of a new sk_buff (`nci_skb_alloc()`), and the subsequent data transfer and potential error handling. The resubmission of the URB within the completion routine is important for continuous data reception.
Now, I need to understand where `nfcmrvl_submit_bulk_urb()` is called from. A quick search reveals two call sites within `usb.c`: `nfcmrvl_usb_nci_open()` and `nfcmrvl_resume()`. In `nfcmrvl_usb_nci_open()`, the call appears straightforward; it's part of the initialization process for the USB-NCI interface.
However, `nfcmrvl_resume()` is where things get interesting. It's responsible for re-establishing the communication after a system suspend/resume cycle. I immediately honed in on line 410, where a conditional check is performed. This looks like a crucial point. It seems a check is being performed, but I see a potential error! The code tests a flag, `NFCMRVL_NCI_RUNNING`, that seems to belong to the `priv` structure (a `nfcmrvl_private` pointer).
But wait, the code is checking `drv_data->flags` and not `priv->flags`.
A further look at the code confirms this suspicion. The `drv_data` structure seems to store interface-specific data, while the `priv` structure likely contains the core driver state. Crucially, both structures define flags. Let's compare the flags. A quick glance at the header file reveals that `NFCMRVL_NCI_RUNNING` is actually defined as `1` *and* `NFCMRVL_USB_BULK_RUNNING` is defined as `1` as well! **This is a likely bug.** The driver is testing a bit in the *wrong* flag field. Specifically, the intent is to check if `priv` is running with `NFCMRVL_NCI_RUNNING`, but instead, is checking if `drv_data` is running with `NFCMRVL_USB_BULK_RUNNING`! This is being done in `nfcmrvl_resume`!
The consequence of this is that the URB resubmission might be skipped incorrectly if bit 1 is set in `drv_data->flags` (meaning, the bulk transfer is already running), even if the NCI device is still conceptually "running". This will lead to intermittent issues.
I need to confirm if there are any other instances of this bug. Let's do a quick grep search and double check. The problem seems to be an incorrect bit test.
|
| 162/4 |
2026/08/26 14:46 |
tool |
grepper |
0m
Args: map[Expression:flags PathPrefix:drivers/nfc/nfcmrvl]
Results: map[Output:drivers/nfc/nfcmrvl/i2c.c=18=struct nfcmrvl_i2c_drv_data {
drivers/nfc/nfcmrvl/i2c.c:19: unsigned long flags;
drivers/nfc/nfcmrvl/i2c.c-20- struct device *dev;
--
drivers/nfc/nfcmrvl/i2c.c=64=static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
--
drivers/nfc/nfcmrvl/i2c.c-72-
drivers/nfc/nfcmrvl/i2c.c:73: if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
drivers/nfc/nfcmrvl/i2c.c-74- return IRQ_HANDLED;
--
drivers/nfc/nfcmrvl/i2c.c-79- case -EREMOTEIO:
drivers/nfc/nfcmrvl/i2c.c:80: set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
drivers/nfc/nfcmrvl/i2c.c-81- break;
--
drivers/nfc/nfcmrvl/i2c.c=109=static int nfcmrvl_i2c_nci_send(struct nfcmrvl_private *priv,
--
drivers/nfc/nfcmrvl/i2c.c-114-
drivers/nfc/nfcmrvl/i2c.c:115: if (test_bit(NFCMRVL_PHY_ERROR, &priv->flags)) {
drivers/nfc/nfcmrvl/i2c.c-116- kfree_skb(skb);
--
drivers/nfc/nfcmrvl/main.c=17=static int nfcmrvl_nci_open(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-21-
drivers/nfc/nfcmrvl/main.c:22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-23- return 0;
--
drivers/nfc/nfcmrvl/main.c-25- /* Reset possible fault of previous session */
drivers/nfc/nfcmrvl/main.c:26: clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
drivers/nfc/nfcmrvl/main.c-27-
--
drivers/nfc/nfcmrvl/main.c-30- if (err)
drivers/nfc/nfcmrvl/main.c:31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
drivers/nfc/nfcmrvl/main.c-32-
--
drivers/nfc/nfcmrvl/main.c=36=static int nfcmrvl_nci_close(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-39-
drivers/nfc/nfcmrvl/main.c:40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-41- return 0;
--
drivers/nfc/nfcmrvl/main.c=194=int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
--
drivers/nfc/nfcmrvl/main.c-212-
drivers/nfc/nfcmrvl/main.c:213: if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-214- nci_recv_frame(priv->ndev, skb);
--
drivers/nfc/nfcmrvl/main.c=225=void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/main.c-227- /* Reset possible fault of previous session */
drivers/nfc/nfcmrvl/main.c:228: clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
drivers/nfc/nfcmrvl/main.c-229-
--
drivers/nfc/nfcmrvl/nfcmrvl.h=13=struct gpio_desc;
drivers/nfc/nfcmrvl/nfcmrvl.h-14-
drivers/nfc/nfcmrvl/nfcmrvl.h:15:/* Define private flags: */
drivers/nfc/nfcmrvl/nfcmrvl.h-16-#define NFCMRVL_NCI_RUNNING 1
--
drivers/nfc/nfcmrvl/nfcmrvl.h=81=struct nfcmrvl_private {
drivers/nfc/nfcmrvl/nfcmrvl.h-82-
drivers/nfc/nfcmrvl/nfcmrvl.h:83: unsigned long flags;
drivers/nfc/nfcmrvl/nfcmrvl.h-84-
--
drivers/nfc/nfcmrvl/spi.c=19=struct nfcmrvl_spi_drv_data {
drivers/nfc/nfcmrvl/spi.c:20: unsigned long flags;
drivers/nfc/nfcmrvl/spi.c-21- struct spi_device *spi;
--
drivers/nfc/nfcmrvl/spi.c=27=static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
--
drivers/nfc/nfcmrvl/spi.c-35- */
drivers/nfc/nfcmrvl/spi.c:36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
drivers/nfc/nfcmrvl/spi.c-37- complete(&drv_data->handshake_completion);
--
drivers/nfc/nfcmrvl/spi.c=65=static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
--
drivers/nfc/nfcmrvl/spi.c-72- reinit_completion(&drv_data->handshake_completion);
drivers/nfc/nfcmrvl/spi.c:73: set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
drivers/nfc/nfcmrvl/spi.c-74-
--
drivers/nfc/nfcmrvl/usb.c=26=struct nfcmrvl_usb_drv_data {
--
drivers/nfc/nfcmrvl/usb.c-28- struct usb_interface *intf;
drivers/nfc/nfcmrvl/usb.c:29: unsigned long flags;
drivers/nfc/nfcmrvl/usb.c-30- struct work_struct waker;
--
drivers/nfc/nfcmrvl/usb.c=43=static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
drivers/nfc/nfcmrvl/usb.c-44-{
drivers/nfc/nfcmrvl/usb.c:45: unsigned long flags;
drivers/nfc/nfcmrvl/usb.c-46- int rv;
drivers/nfc/nfcmrvl/usb.c-47-
drivers/nfc/nfcmrvl/usb.c:48: spin_lock_irqsave(&drv_data->txlock, flags);
drivers/nfc/nfcmrvl/usb.c:49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-50- if (!rv)
drivers/nfc/nfcmrvl/usb.c-51- drv_data->tx_in_flight++;
drivers/nfc/nfcmrvl/usb.c:52: spin_unlock_irqrestore(&drv_data->txlock, flags);
drivers/nfc/nfcmrvl/usb.c-53-
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-64-
drivers/nfc/nfcmrvl/usb.c:65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-66- return;
--
drivers/nfc/nfcmrvl/usb.c-83-
drivers/nfc/nfcmrvl/usb.c:84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-85- return;
--
drivers/nfc/nfcmrvl/usb.c=102=static int
drivers/nfc/nfcmrvl/usb.c:103:nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
drivers/nfc/nfcmrvl/usb.c-104-{
--
drivers/nfc/nfcmrvl/usb.c-112-
drivers/nfc/nfcmrvl/usb.c:113: urb = usb_alloc_urb(0, mem_flags);
drivers/nfc/nfcmrvl/usb.c-114- if (!urb)
--
drivers/nfc/nfcmrvl/usb.c-116-
drivers/nfc/nfcmrvl/usb.c:117: buf = kmalloc(size, mem_flags);
drivers/nfc/nfcmrvl/usb.c-118- if (!buf) {
--
drivers/nfc/nfcmrvl/usb.c-128-
drivers/nfc/nfcmrvl/usb.c:129: urb->transfer_flags |= URB_FREE_BUFFER;
drivers/nfc/nfcmrvl/usb.c-130-
--
drivers/nfc/nfcmrvl/usb.c-133-
drivers/nfc/nfcmrvl/usb.c:134: err = usb_submit_urb(urb, mem_flags);
drivers/nfc/nfcmrvl/usb.c-135- if (err) {
--
drivers/nfc/nfcmrvl/usb.c=147=static void nfcmrvl_tx_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-152- struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c:153: unsigned long flags;
drivers/nfc/nfcmrvl/usb.c-154-
--
drivers/nfc/nfcmrvl/usb.c-157-
drivers/nfc/nfcmrvl/usb.c:158: spin_lock_irqsave(&drv_data->txlock, flags);
drivers/nfc/nfcmrvl/usb.c-159- drv_data->tx_in_flight--;
drivers/nfc/nfcmrvl/usb.c:160: spin_unlock_irqrestore(&drv_data->txlock, flags);
drivers/nfc/nfcmrvl/usb.c-161-
--
drivers/nfc/nfcmrvl/usb.c=166=static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/usb.c-180-
drivers/nfc/nfcmrvl/usb.c:181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-182- nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
--
drivers/nfc/nfcmrvl/usb.c=197=static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/usb.c-203-
drivers/nfc/nfcmrvl/usb.c:204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-205-
--
drivers/nfc/nfcmrvl/usb.c=348=static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
--
drivers/nfc/nfcmrvl/usb.c-358- if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
drivers/nfc/nfcmrvl/usb.c:359: set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-360- spin_unlock_irq(&drv_data->txlock);
--
drivers/nfc/nfcmrvl/usb.c=400=static int nfcmrvl_resume(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-409-
drivers/nfc/nfcmrvl/usb.c:410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-411- goto done;
drivers/nfc/nfcmrvl/usb.c-412-
drivers/nfc/nfcmrvl/usb.c:413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
drivers/nfc/nfcmrvl/usb.c-414- err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
drivers/nfc/nfcmrvl/usb.c-415- if (err) {
drivers/nfc/nfcmrvl/usb.c:416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-417- goto failed;
--
drivers/nfc/nfcmrvl/usb.c-424- nfcmrvl_play_deferred(drv_data);
drivers/nfc/nfcmrvl/usb.c:425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-426- spin_unlock_irq(&drv_data->txlock);
--
drivers/nfc/nfcmrvl/usb.c-433- spin_lock_irq(&drv_data->txlock);
drivers/nfc/nfcmrvl/usb.c:434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-435- spin_unlock_irq(&drv_data->txlock);
]
|
| 163/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40562
output: 43
thoughts: 1175
Thoughts: **Analyzing Potential Race Conditions in SPI and I2C Interrupt Handlers**
Okay, I need to take a look at the interrupt handlers for the SPI and I2C drivers, specifically within the `nfcmrvl` framework. The goal is to identify any potential race conditions. I know from past experience that this area is prone to subtle bugs.
First, let's start by looking at `spi.c` and `i2c.c`. I see that the `nfcmrvl_spi_int_irq_thread_fn` in `spi.c` handles the SPI interrupt. My initial concern is whether the interrupt can fire before the driver is fully initialized. Looking at the code:
```c
static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
{
struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
struct sk_buff *skb;
if (!drv_data->priv)
return IRQ_HANDLED;
/*
* Special case where we are waiting for SPI_INT deassertion to start a
* transfer.
*/
if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
complete(&drv_data->handshake_completion);
return IRQ_HANDLED;
}
/* Normal case, SPI_INT deasserted by slave to trigger a master read */
skb = nci_spi_read(drv_data->nci_spi);
if (!skb) {
nfc_err(&drv_data->spi->dev, "failed to read spi packet");
return IRQ_HANDLED;
}
if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
nfc_err(&drv_data->spi->dev, "corrupted RX packet");
return IRQ_HANDLED;
}
```
Okay, the code checks for `!drv_data->priv` at the beginning. That's good, but I'm wondering if that's sufficient, or if it might miss a window where `drv_data->nci_spi` is not yet initialized. Let's look at how the IRQ is registered in `nfcmrvl_spi_probe()`:
```c
ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
NULL, nfcmrvl_spi_int_irq_thread_fn,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"nfcmrvl_spi_int", drv_data);
if (ret < 0) {
nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler");
return -ENODEV;
}
drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
drv_data, &spi_ops,
&drv_data->spi->dev,
pdata);
if (IS_ERR(drv_data->priv))
return PTR_ERR(drv_data->priv);
drv_data->priv->support_fw_dnld = true;
drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10,
drv_data->priv->ndev);
```
Ah, I see it. The `devm_request_threaded_irq` call happens *before* `nfcmrvl_nci_register_dev` which sets `drv_data->priv`, and before `nci_spi_allocate_spi` which sets `drv_data->nci_spi`. Therefore, there's a potential window where the interrupt fires before `drv_data->priv` *or* `drv_data->nci_spi` are initialized. The check for `!drv_data->priv` in the interrupt handler *should* protect against the first, but not necessarily against the second.
Now, let's look at `i2c.c` to see how it's handled there:
```c
static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *
|
| 164/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:140 LineCount:80]
Results: map[Contents: 140: kfree_skb(skb);
141: return ret;
142: }
143:
144: consume_skb(skb);
145: return 0;
146: }
147:
148: static void nfcmrvl_i2c_nci_update_config(struct nfcmrvl_private *priv,
149: const void *param)
150: {
151: }
152:
153: static const struct nfcmrvl_if_ops i2c_ops = {
154: .nci_open = nfcmrvl_i2c_nci_open,
155: .nci_close = nfcmrvl_i2c_nci_close,
156: .nci_send = nfcmrvl_i2c_nci_send,
157: .nci_update_config = nfcmrvl_i2c_nci_update_config,
158: };
159:
160: static int nfcmrvl_i2c_parse_dt(struct device_node *node,
161: struct nfcmrvl_platform_data *pdata)
162: {
163: int ret;
164:
165: ret = nfcmrvl_parse_dt(node, pdata);
166: if (ret < 0) {
167: pr_err("Failed to get generic entries\n");
168: return ret;
169: }
170:
171: if (of_property_read_bool(node, "i2c-int-falling"))
172: pdata->irq_polarity = IRQF_TRIGGER_FALLING;
173: else
174: pdata->irq_polarity = IRQF_TRIGGER_RISING;
175:
176: ret = irq_of_parse_and_map(node, 0);
177: if (!ret) {
178: pr_err("Unable to get irq\n");
179: return -EINVAL;
180: }
181: pdata->irq = ret;
182:
183: return 0;
184: }
185:
186: static int nfcmrvl_i2c_probe(struct i2c_client *client)
187: {
188: const struct nfcmrvl_platform_data *pdata;
189: struct nfcmrvl_i2c_drv_data *drv_data;
190: struct nfcmrvl_platform_data config;
191: int ret;
192:
193: if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
194: nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
195: return -ENODEV;
196: }
197:
198: drv_data = devm_kzalloc(&client->dev, sizeof(*drv_data), GFP_KERNEL);
199: if (!drv_data)
200: return -ENOMEM;
201:
202: drv_data->i2c = client;
203: drv_data->dev = &client->dev;
204: drv_data->priv = NULL;
205:
206: i2c_set_clientdata(client, drv_data);
207:
208: pdata = client->dev.platform_data;
209:
210: if (!pdata && client->dev.of_node)
211: if (nfcmrvl_i2c_parse_dt(client->dev.of_node, &config) == 0)
212: pdata = &config;
213:
214: if (!pdata)
215: return -EINVAL;
216:
217: /* Request the read IRQ */
218: ret = devm_request_threaded_irq(&drv_data->i2c->dev, pdata->irq,
219: NULL, nfcmrvl_i2c_int_irq_thread_fn,
]
|
| 165/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 44061
output: 43
thoughts: 24
Thoughts: Here's my summary, as if I were thinking these thoughts:
**Analyzing the I2C Implementation**
Okay, so I'm diving back into this `i2c.c` file again. I need to get a better handle on the current state of the I2C driver. I've been focusing on certain areas, but I think a targeted look at lines 220 through 240 would be useful right now. I have a hunch that something critical is happening there regarding [insert the user's field of expertise here]. Going back to the core implementation should give me a better understanding of how the design is handling [key concept]. Hopefully, this will clarify a few lingering questions and lead to some insights that have eluded me. Let's see what's happening in that section.
|
| 166/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:220 LineCount:30]
Results: map[Contents: 220: pdata->irq_polarity | IRQF_ONESHOT,
221: "nfcmrvl_i2c_int", drv_data);
222: if (ret < 0) {
223: nfc_err(&drv_data->i2c->dev,
224: "Unable to register IRQ handler\n");
225: return ret;
226: }
227:
228: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
229: drv_data, &i2c_ops,
230: &drv_data->i2c->dev, pdata);
231:
232: if (IS_ERR(drv_data->priv))
233: return PTR_ERR(drv_data->priv);
234:
235: drv_data->priv->support_fw_dnld = true;
236:
237: return 0;
238: }
239:
240: static void nfcmrvl_i2c_remove(struct i2c_client *client)
241: {
242: struct nfcmrvl_i2c_drv_data *drv_data = i2c_get_clientdata(client);
243:
244: nfcmrvl_nci_unregister_dev(drv_data->priv);
245: }
246:
247:
248: static const struct of_device_id of_nfcmrvl_i2c_match[] __maybe_unused = {
249: { .compatible = "marvell,nfc-i2c", },
]
|
| 167/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 44804
output: 30
thoughts: 6766
Thoughts: **Analyzing Potential Race Conditions in the NFCMRVL Drivers**
Okay, so I'm diving into this NFCMRVL driver code, specifically looking at potential race conditions, as I've been instructed. The initial focus is on the SPI driver (`spi.c`), but I'm also keeping an eye on the USB driver (`usb.c`) to see if the issue is systemic.
The core concern is that the interrupt handler `nfcmrvl_spi_int_irq_thread_fn` in `spi.c` might fire before `drv_data->priv` is fully initialized, leading to a null pointer dereference. The fix suggested is to add a check: `if (!drv_data->priv) return IRQ_HANDLED;` at the beginning of the handler. Seems straightforward enough, and aligns with the pattern used in the i2c driver.
However, I'm not stopping there. The prompt suggests that this pattern could be present elsewhere. I need to be thorough. I'm taking a close look at the USB driver, specifically at `nfcmrvl_submit_bulk_urb()` and `nfcmrvl_bulk_complete()`. The goal is to make sure this is not a symptom of a larger, systemic bug. I see that the original intent was to pass `drv_data->priv` directly as the URB context in `nfcmrvl_submit_bulk_urb()`, then retrieve it in `nfcmrvl_bulk_complete()`.
So, it's about making sure the right `priv` pointer is being used at the right time in the USB driver. By directly passing `priv` as the URB context, it's then reliably accessible in the completion routine. It fixes the potential race condition by ensuring that the `priv` pointer is initialized and valid when it's accessed in `nfcmrvl_bulk_complete`. Similarly, in `nfcmrvl_resume()` and `nfcmrvl_tx_complete()`, the correct `priv` pointer is being accessed.
Having read through all the relevant code and following the instructions, it's time to apply the suggested fix to the SPI driver by adding the null check and modifying the USB driver to pass `priv` as the URB context and fixing the typo for the flag.
|
| 168/4 |
2026/08/26 14:46 |
tool |
git-log |
0m
Args: map[Count:10 PathPrefix:drivers/nfc/nfcmrvl]
Results: map[Output:567ab7727adb nfc: Use named initializers for struct i2c_device_id
dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
c49a9eb650d5 NFC: nfcmrvl: Replace strcpy() with strscpy()
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
41cb08555c41 treewide, timers: Rename from_timer() to timer_container_of()
8fa7292fee5c treewide: Switch/rename to timer_delete[_sync]()
a26892ee1297 nfc: mrvl: Don't use "proxy" headers
5f60d5f6bbc1 move asm/unaligned.h to linux/unaligned.h
]
|
| 169/4 |
2026/08/26 14:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 52365
output: 43
thoughts: 1065
Thoughts: **Analyzing Interrupt Handling in nfcmrvl Drivers**
Okay, let's dive into this interrupt handling. First, I need to check how the `i2c.c` driver handles its IRQ thread. Looking at lines 64-92, the `nfcmrvl_i2c_int_irq_thread_fn` function checks for a NULL `drv_data->priv` pointer right at the beginning. This is crucial for preventing a crash if an interrupt fires before the driver is fully initialized and the `priv` field is assigned. The function then proceeds to read data, handle errors like `EREMOTEIO`, `ENOMEM`, and `EBADMSG`, and passes received frames to `nfcmrvl_nci_recv_frame`.
Now, let's turn our attention to `spi.c` and its interrupt handler on lines 27-53. Hmm, I see a potential issue here – the conditional `if (!drv_data->priv)` check is missing! However, hold on, I should verify this assumption by tracing how the `drv_data->priv` member is initialized in the probe function. Let's look at `nfcmrvl_spi_probe()`. Bingo! The code explicitly initializes `drv_data->priv` to `NULL` before requesting the threaded IRQ. This means that, even though the check is missing in the thread function, if the interrupt triggers prematurely, the driver will handle the NULL pointer gracefully during the initial interrupt firing. It appears the absence of the null check within the interrupt thread function isn't a bug.
I'm now curious if there are any other instances of this pattern or potentially problematic code in the `drivers/nfc/nfcmrvl/` directory. I'll need to quickly review the following files: `usb.c`, `spi.c`, `i2c.c`, `uart.c`, `main.c`, and `fw_dnld.c`. Just to be thorough, I'll start with `fw_dnld.c`.
|
| 170/4 |
2026/08/26 14:46 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/fw_dnld.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC driver: Firmware downloader
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/unaligned.h>
10: #include <linux/firmware.h>
11: #include <linux/nfc.h>
12: #include <net/nfc/nci.h>
13: #include <net/nfc/nci_core.h>
14: #include "nfcmrvl.h"
15:
16: #define FW_DNLD_TIMEOUT 15000
17:
18: #define NCI_OP_PROPRIETARY_BOOT_CMD nci_opcode_pack(NCI_GID_PROPRIETARY, \
19: NCI_OP_PROP_BOOT_CMD)
20:
21: /* FW download states */
22:
23: enum {
24: STATE_RESET = 0,
25: STATE_INIT,
26: STATE_SET_REF_CLOCK,
27: STATE_SET_HI_CONFIG,
28: STATE_OPEN_LC,
29: STATE_FW_DNLD,
30: STATE_CLOSE_LC,
31: STATE_BOOT
32: };
33:
34: enum {
35: SUBSTATE_WAIT_COMMAND = 0,
36: SUBSTATE_WAIT_ACK_CREDIT,
37: SUBSTATE_WAIT_NACK_CREDIT,
38: SUBSTATE_WAIT_DATA_CREDIT,
39: };
40:
41: /*
42: * Patterns for responses
43: */
44:
45: static const uint8_t nci_pattern_core_reset_ntf[] = {
46: 0x60, 0x00, 0x02, 0xA0, 0x01
47: };
48:
49: static const uint8_t nci_pattern_core_init_rsp[] = {
50: 0x40, 0x01, 0x11
51: };
52:
53: static const uint8_t nci_pattern_core_set_config_rsp[] = {
54: 0x40, 0x02, 0x02, 0x00, 0x00
55: };
56:
57: static const uint8_t nci_pattern_core_conn_create_rsp[] = {
58: 0x40, 0x04, 0x04, 0x00
59: };
60:
61: static const uint8_t nci_pattern_core_conn_close_rsp[] = {
62: 0x40, 0x05, 0x01, 0x00
63: };
64:
65: static const uint8_t nci_pattern_core_conn_credits_ntf[] = {
66: 0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01
67: };
68:
69: static const uint8_t nci_pattern_proprietary_boot_rsp[] = {
70: 0x4F, 0x3A, 0x01, 0x00
71: };
72:
73: static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen)
74: {
75: struct sk_buff *skb;
76: struct nci_data_hdr *hdr;
77:
78: skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL);
79: if (!skb)
80: return NULL;
81:
82: hdr = skb_put(skb, NCI_DATA_HDR_SIZE);
83: hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
84: hdr->rfu = 0;
85: hdr->plen = plen;
86:
87: nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
88: nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
89:
90: return skb;
91: }
92:
93: static void fw_dnld_over(struct nfcmrvl_private *priv, u32 error)
94: {
95: if (priv->fw_dnld.fw) {
96: release_firmware(priv->fw_dnld.fw);
97: priv->fw_dnld.fw = NULL;
98: priv->fw_dnld.header = NULL;
99: priv->fw_dnld.binary_config = NULL;
100: }
]
|
| 171/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 55414
output: 40
thoughts: 2386
Thoughts: **Analysis of Marvell NFC Driver Code for `priv` Usage**
Okay, let's break this down. The first thing I'm seeing is that `fw_dnld.c` is correctly utilizing `priv` – that's a good starting point. My primary concern here is how the `priv` structure is being handled across the different driver implementations, particularly around device open and close operations, and interrupt handling, to ensure that it's consistent and safe. I need to make sure this is correctly handled.
I'm focused on the changes needed in `drivers/nfc/nfcmrvl/usb.c` and `drivers/nfc/nfcmrvl/spi.c` specifically, but I'm going to also do a wider review of the other drivers to make sure that these patterns are broadly consistent.
In `drivers/nfc/nfcmrvl/usb.c`, lines 57-100 and 102-145 (the `nfcmrvl_bulk_complete` and `nfcmrvl_submit_bulk_urb` functions) are fine. They seem to correctly use `priv` (derived from `urb->context`). Also, the assignment of `priv` is safe, as well as the checks of the `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` flags.
Lines 166-190, the `nfcmrvl_usb_nci_open` function are important. I see that `usb_autopm_get_interface` is used here, which is fine, but it has no direct relationship to `priv`. The `nfcmrvl_submit_bulk_urb` is called to initialize the bulk URBs. Later, the `nfcmrvl_resume` function, lines 400-422, looks good, it's checking `drv_data->suspend_count` and also correctly using `priv` through `drv_data`. The calls to `nfcmrvl_submit_bulk_urb` inside `nfcmrvl_resume` are also good.
Now, let's verify that the interrupt handler for SPI has `priv` correctly checked. In `drivers/nfc/nfcmrvl/spi.c` lines 27-32, the interrupt handler `nfcmrvl_spi_int_irq_thread_fn` explicitly checks `drv_data->priv`. This is essential because, if the device is closed, the driver data might be cleaned up, and this check prevents use-after-free scenarios in the IRQ context.
Now, let's widen the lens and check the other drivers in the `drivers/nfc/nfcmrvl/` directory to ensure consistent usage of the `priv` pointer. I reviewed `i2c.c` which, like `spi.c`, has `if (!drv_data->priv)` checks. Also, functions like `nfcmrvl_i2c_nci_open`, `nfcmrvl_i2c_nci_close`, and `nfcmrvl_i2c_nci_send` all correctly use `priv`. `uart.c`'s `uart_ops` functions, are also fine. Furthermore, the `nfcmrvl_nci_open`, `nfcmrvl_nci_uart_recv` and other functions there have no direct impact, since the pointer to `priv` is correctly used. Finally, `main.c` is also fine.
Based on this review, I'm confident that the code uses `priv` correctly, and that the interrupt handlers and other potentially tricky functions are guarded against use-after-free situations through appropriate null checks. There are no immediate red flags, and the code appears consistent in its approach.
|
| 172/4 |
2026/08/26 14:47 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:55 LineCount:45]
Results: map[Contents: 55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
]
|
| 173/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 59369
output: 41
thoughts: 39
Thoughts: Okay, here's my take on those lines of code, presented as a personal thought process, assuming I'm already pretty familiar with the nfc subsystem:
**Digging into the `nfcmrvl` USB Driver**
Alright, time to dive into those specific sections of the Marvell NFC USB driver. I want to zero in on lines 100-145 and then 165-195 within `drivers/nfc/nfcmrvl/usb.c`. My initial hunch is that these sections likely relate to either initialization, command handling, or perhaps interrupt processing, considering the file context. I need to refresh my memory on the likely data structures and control flow that the `nfcmrvl` driver uses for USB communication.
I'll be specifically looking for how the driver sets up the USB endpoints, handles URBs, and interacts with the NFC hardware. Are they using bulk transfers? Interrupts? Control transfers for configuration? Understanding the data transfer patterns will be key. Within the initial lines, I'll be expecting to find USB descriptor parsing and device probing.
I'll then analyze lines 165-195 for further initialization, perhaps some USB-specific setup or potential error handling and control flow. Knowing what these sections are supposed to do in detail will help me pinpoint any issues or potential areas for improvement more efficiently.
|
| 174/4 |
2026/08/26 14:47 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:100 LineCount:45]
Results: map[Contents: 100: }
101:
102: static int
103: nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
104: {
105: struct urb *urb;
106: unsigned char *buf;
107: unsigned int pipe;
108: int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
109:
110: if (!drv_data->bulk_rx_ep)
111: return -ENODEV;
112:
113: urb = usb_alloc_urb(0, mem_flags);
114: if (!urb)
115: return -ENOMEM;
116:
117: buf = kmalloc(size, mem_flags);
118: if (!buf) {
119: usb_free_urb(urb);
120: return -ENOMEM;
121: }
122:
123: pipe = usb_rcvbulkpipe(drv_data->udev,
124: drv_data->bulk_rx_ep->bEndpointAddress);
125:
126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
127: nfcmrvl_bulk_complete, drv_data);
128:
129: urb->transfer_flags |= URB_FREE_BUFFER;
130:
131: usb_mark_last_busy(drv_data->udev);
132: usb_anchor_urb(urb, &drv_data->bulk_anchor);
133:
134: err = usb_submit_urb(urb, mem_flags);
135: if (err) {
136: if (err != -EPERM && err != -ENODEV)
137: nfc_err(&drv_data->udev->dev,
138: "urb %p submission failed (%d)\n", urb, -err);
139: usb_unanchor_urb(urb);
140: }
141:
142: usb_free_urb(urb);
143:
144: return err;
]
|
| 175/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 60408
output: 41
thoughts: 29
Thoughts: Okay, here's my interpretation of what's going through my mind, rendered in the first person, as if I'm the one having these thoughts, keeping in mind my presumed expertise in this field:
**Diving into the NFC Driver's USB Interaction**
Alright, I need to get to the bottom of this. Let's start with a focused look at the `drivers/nfc/nfcmrvl/usb.c` file. I'm going to jump right to lines 400 through 438. Those lines are probably the most relevant portion. This is probably where the core interaction with the USB interface lives. I suspect there's a specific function call or block of code dealing with something that's concerning me, or I wouldn't have singled out that area. I need to understand what's happening within that region of the code. Is it data transfer, error handling, device initialization, or perhaps a problematic race condition? My experience tells me that area is the most likely spot to find the smoking gun. It's time to dig in and decipher what this code is actually *doing* to the USB bus.
|
| 176/4 |
2026/08/26 14:47 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:400 LineCount:40]
Results: map[Contents: 400: static int nfcmrvl_resume(struct usb_interface *intf)
401: {
402: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
403: int err = 0;
404:
405: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
406:
407: if (--drv_data->suspend_count)
408: return 0;
409:
410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
411: goto done;
412:
413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
421: }
422:
423: spin_lock_irq(&drv_data->txlock);
424: nfcmrvl_play_deferred(drv_data);
425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
426: spin_unlock_irq(&drv_data->txlock);
427:
428: return 0;
429:
430: failed:
431: usb_scuttle_anchored_urbs(&drv_data->deferred);
432: done:
433: spin_lock_irq(&drv_data->txlock);
434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
435: spin_unlock_irq(&drv_data->txlock);
436:
437: return err;
438: }
439: #endif
]
|
| 177/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 61376
output: 40
thoughts: 27
Thoughts: Here's a summary of my mental process:
**Diving into the NFC Driver Code**
Okay, time to dig in. I need to refresh my memory on how the Marvell NFC chip interacts with the system via SPI. Let's start with the driver source, specifically `drivers/nfc/nfcmrvl/spi.c`. I'm going to focus on lines 25 to 45. That's usually where the core SPI initialization and configuration are handled. I'll be looking for the crucial SPI bus setup, clock frequency configuration, and any potential device-specific initialization sequences that might be present. I'm expecting to see calls to the SPI core, likely using the device tree for resource allocation. Any unusual register settings or clock manipulation in that section is definitely worth a closer look.
|
| 178/4 |
2026/08/26 14:47 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:25 LineCount:25]
Results: map[Contents: 25: };
26:
27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
45: nfc_err(&drv_data->spi->dev, "failed to read spi packet");
46: return IRQ_HANDLED;
47: }
48:
49: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
]
|
| 179/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 62008
output: 60
thoughts: 325
Thoughts: **Analyzing `nfcmrvl_submit_bulk_urb` Usage**
Okay, let's dive into this. I need to make sure I haven't missed anything related to the potential impact of this function. My first step is to quickly scan `usb.c` and other related files to get a lay of the land. Then I'm zeroing in on every instance where `nfcmrvl_submit_bulk_urb` is called.
Here's what I've got so far:
* **Line 103:** That's the function's definition itself, which I'll keep in mind for context.
* **Line 177 & 182:** Inside `nfcmrvl_usb_nci_open`, it's called twice with `GFP_KERNEL`. This is a crucial area to understand, especially the memory allocation context.
* **Line 414 & 420:** In the `nfcmrvl_resume` function, it's called again twice, but this time with `GFP_NOIO`. This is another critical piece of context regarding when the function is being called.
Now, I'm going to run a global search. I can't be sure the local code is exhaustive. I need to make sure I've identified all calls to this function across the entire project tree. I want to ensure my understanding of its uses is complete.
|
| 180/4 |
2026/08/26 14:47 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c IncludeSnippetLines:0 Name:nfcmrvl_submit_bulk_urb SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_resume SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:414] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_resume SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:420] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_usb_nci_open SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:177] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_usb_nci_open SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:182]] TruncatedOutput:false]
|
| 181/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 62956
output: 1835
thoughts: 1472
Thoughts: **Analyzing the nfcmrvl Driver's USB and SPI Interrupt Handling**
Okay, so I've been tracing the USB and SPI interrupt handling in this `nfcmrvl` driver. Let's see... looking at the call sites, there are only four, which narrows things down.
First, I examined `nfcmrvl_bulk_complete`. This is the callback function passed to `usb_fill_bulk_urb`. Inside, I can see the Urb's context remains as 'priv', and is not touched. The urb is correctly re-submitted after completion. So, when it completes again, the `urb->context` is still correctly referencing 'priv'. Everything there looks seamless.
Next, I looked into `nfcmrvl_resume`. This function handles resuming after a USB suspend event. If it detects that `NFCMRVL_USB_BULK_RUNNING` is set, it re-submits the bulk URBs using `nfcmrvl_submit_bulk_urb`. It also does some deferred task handling related to transmission. This seems sensible; it ensures the bulk transfer is re-established after a suspend/resume cycle.
Finally, I checked `nfcmrvl_spi_int_irq_thread_fn`. This is the interrupt handler for the SPI interface. It seems to handle two primary cases: a special handshake case where it waits for a SPI_INT deassertion and a normal read case where the slave signals for a master read. In the normal case, it calls `nci_spi_read` to receive data. Then, if the read is successful, `nfcmrvl_nci_recv_frame` is called. So, the interrupt handling seems logical and correctly handles both the handshake and data reception.
With that I can be confident in the proposed edits.
|
| 182/4 |
2026/08/26 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_usb_drv_data *drv_data = urb->context;
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))
return;
if (!urb->status) {
struct sk_buff *skb;
skb = nci_skb_alloc(drv_data->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)
nfc_err(&drv_data->udev->dev,
"corrupted Rx packet\n");
}
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
return;
usb_anchor_urb(urb, &drv_data->bulk_anchor);
usb_mark_last_busy(drv_data->udev);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
/* -EPERM: urb is being killed;
* -ENODEV: device got disconnected
*/
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p failed to resubmit (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
}
static int
nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
{
struct urb *urb;
unsigned char *buf;
unsigned int pipe;
int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
if (!drv_data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(drv_data->udev,
drv_data->bulk_rx_ep->bEndpointAddress);
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, drv_data);
urb->transfer_flags |= URB_FREE_BUFFER;
usb_mark_last_busy(drv_data->udev);
usb_anchor_urb(urb, &drv_data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
} NewCode:static void nfcmrvl_bulk_complete(struct urb *urb)
{
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, &priv->flags))
return;
if (!urb->status) {
struct sk_buff *skb;
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(priv, skb) < 0)
nfc_err(&drv_data->udev->dev,
"corrupted Rx packet\n");
}
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
return;
usb_anchor_urb(urb, &drv_data->bulk_anchor);
usb_mark_last_busy(drv_data->udev);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
/* -EPERM: urb is being killed;
* -ENODEV: device got disconnected
*/
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p failed to resubmit (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
}
static int
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;
int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
if (!drv_data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(drv_data->udev,
drv_data->bulk_rx_ep->bEndpointAddress);
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
urb->transfer_flags |= URB_FREE_BUFFER;
usb_mark_last_busy(drv_data->udev);
usb_anchor_urb(urb, &drv_data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
} SourceFile:drivers/nfc/nfcmrvl/usb.c]
Results:
|
| 183/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 66660
output: 82
thoughts: 72
Thoughts: Here's my thinking:
**Examining USB NFC Driver Code**
Alright, time to dig into the `drivers/nfc/nfcmrvl/usb.c` file again. Specifically, I need to focus on the `nfcmrvl_usb_nci_open` and `nfcmrvl_resume` functions. I'll need to carefully review the code surrounding those functions to understand their context and any potential dependencies. This is where the real work begins, and the details matter.
|
| 184/4 |
2026/08/26 14:47 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:165 LineCount:35]
Results: map[Contents: 165:
166: static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
167: {
168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
169: int err;
170:
171: err = usb_autopm_get_interface(drv_data->intf);
172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
188: usb_autopm_put_interface(drv_data->intf);
189: return err;
190: }
191:
192: static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
193: {
194: usb_kill_anchored_urbs(&drv_data->bulk_anchor);
195: }
196:
197: static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
198: {
199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
]
|
| 185/4 |
2026/08/26 14:47 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:400 LineCount:35]
Results: map[Contents: 400: static int nfcmrvl_resume(struct usb_interface *intf)
401: {
402: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
403: int err = 0;
404:
405: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
406:
407: if (--drv_data->suspend_count)
408: return 0;
409:
410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
411: goto done;
412:
413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
421: }
422:
423: spin_lock_irq(&drv_data->txlock);
424: nfcmrvl_play_deferred(drv_data);
425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
426: spin_unlock_irq(&drv_data->txlock);
427:
428: return 0;
429:
430: failed:
431: usb_scuttle_anchored_urbs(&drv_data->deferred);
432: done:
433: spin_lock_irq(&drv_data->txlock);
434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
]
|
| 186/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 68077
output: 510
thoughts: 42
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Focusing on nfcmrvl USB Driver Functions**
Right, time to dig into the `nfcmrvl` USB driver, specifically the `drivers/nfc/nfcmrvl/usb.c` file. My gut tells me these two functions, `nfcmrvl_usb_nci_open` and `nfcmrvl_resume`, are prime candidates for attention. I'm likely dealing with either initialization or power management/resume issues, given their names. The "nci" in `nci_open` immediately flags it as a crucial part of the Near Field Communication interface. Any hiccups there, and the entire NFC functionality is crippled. And `nfcmrvl_resume` is, well, pretty self-explanatory – I'm expecting to find the key to handling USB suspend/resume cycles correctly. This is where those tricky race conditions or state inconsistencies love to hide. I need to figure out the exact flow, the data structures they interact with, and any potential locking issues. Let's see if the existing code adheres to the best practices for USB drivers. Time to fire up the debugger and start tracing through these functions... and see what's what!
|
| 187/4 |
2026/08/26 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
drv_data->intf->needs_remote_wakeup = 1;
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
usb_autopm_put_interface(drv_data->intf);
return 0;
failed:
usb_autopm_put_interface(drv_data->intf);
return err;
} NewCode:static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
drv_data->intf->needs_remote_wakeup = 1;
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(priv, GFP_KERNEL);
usb_autopm_put_interface(drv_data->intf);
return 0;
failed:
usb_autopm_put_interface(drv_data->intf);
return err;
} SourceFile:drivers/nfc/nfcmrvl/usb.c]
Results:
|
| 188/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 68907
output: 553
thoughts: 26
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Diving into the NFC Marvell USB Driver**
Alright, time to get my hands dirty. Looks like I need to make some adjustments to the `nfcmrvl_resume` function, specifically within the `drivers/nfc/nfcmrvl/usb.c` file. This means I'm probably dealing with some power management or device state transition logic related to the USB interface of the Marvell NFC controller. I'll need to carefully consider how this function interacts with the overall suspend/resume cycle of the system and how it handles potential USB-specific quirks or limitations. I'll be looking for potential race conditions, incorrect state handling, or perhaps even inefficient power usage when the device wakes up. I'll need to keep an eye on how the firmware interacts with the host during resume and how to handle it when the USB is connected or disconnected. Time to start digging into the code and see what's actually going on.
|
| 189/4 |
2026/08/26 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int nfcmrvl_resume(struct usb_interface *intf)
{
struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
int err = 0;
nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
if (--drv_data->suspend_count)
return 0;
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
goto done;
if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
if (err) {
clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
goto failed;
}
nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
} NewCode: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);
if (--drv_data->suspend_count)
return 0;
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(priv, GFP_NOIO);
if (err) {
clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
goto failed;
}
nfcmrvl_submit_bulk_urb(priv, GFP_NOIO);
} SourceFile:drivers/nfc/nfcmrvl/usb.c]
Results:
|
| 190/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 69710
output: 40
thoughts: 24
Thoughts: Here's my take on the process, framed as a first-person expert perspective:
**Diving into the NFC Marvell SPI Driver**
Okay, time to get my hands dirty. I'm going to jump into the `drivers/nfc/nfcmrvl/spi.c` file. This is where the core SPI interface for the Marvell NFC controller resides, so it's a critical component. I expect to find the low-level communication functions here: the register read/write operations, the initialization sequence, and probably the interrupt handlers if it's setup that way. I'll need to examine the function calls, data structures, and the flow of the code. My primary objective at this point is to understand its current implementation and identify areas for potential modification or optimization, perhaps for a specific use case I have in mind, or to address a known issue. Let's see what's in there...
|
| 191/4 |
2026/08/26 14:47 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:25 LineCount:20]
Results: map[Contents: 25: };
26:
27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
]
|
| 192/4 |
2026/08/26 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 70277
output: 259
thoughts: 38
Thoughts: Here's my summary, as if I were thinking these thoughts:
**Addressing a Potential Null Pointer Dereference in the NFC Driver**
Okay, time to dive into `drivers/nfc/nfcmrvl/spi.c`. I need to add a crucial null pointer check. Specifically, in this section, I need to insert a conditional statement. It's important to preemptively handle a possible `NULL` value for `drv_data->priv`. A null dereference there could be catastrophic, leading to a kernel panic, and that's the last thing we want. So, the fix is to include `if (!drv_data->priv) return IRQ_HANDLED;`. That should safely exit the interrupt handler if the driver's private data isn't initialized. This is a common defensive coding practice, and it's essential for robustness, especially in interrupt service routines. Makes me feel better knowing I've added this protection.
|
| 193/4 |
2026/08/26 14:48 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
{
struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
struct sk_buff *skb;
/*
* Special case where we are waiting for SPI_INT deassertion to start a
* transfer.
*/ NewCode:static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
{
struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
struct sk_buff *skb;
if (!drv_data->priv)
return IRQ_HANDLED;
/*
* Special case where we are waiting for SPI_INT deassertion to start a
* transfer.
*/ SourceFile:drivers/nfc/nfcmrvl/spi.c]
Results:
|
| 194/4 |
2026/08/26 14:48 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 70780
output: 10
thoughts: 15
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Reviewing Changes with `patch-diff`**
Alright, time to dive into the specifics of this patch. I need a granular understanding of exactly what's changed, and the best way to do that is to fire up `patch-diff`. Forget general overviews; I want to see the nitty-gritty – line-by-line, context by context. I need to be certain about the *what*, the *where*, and the *why* of every single modification this patch introduces. There's no room for assumptions or surface-level assessments. This is where the real analysis begins. I'm expecting this to be a deep dive. I'm looking for potential regressions, unintended side effects, or any unexpected behavior. Time to get my hands dirty.
|
| 195/4 |
2026/08/26 14:48 |
tool |
patch-diff |
0m
Error:
|
| 196/4 |
2026/08/26 14:48 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 71015
output: 15
thoughts: 15
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the `patch-diff` Call**
Okay, so I'm looking at this `patch-diff` call, specifically the part where it's being given an empty string for the file argument. My initial thought process is to consider why we'd be doing that. Normally, a file argument provides the source against which the patch will be applied. An empty string is... unusual. It immediately flags a few potential scenarios, and each needs a closer look:
* **Potential Redundancy/Bug:** Is this a remnant of some debugging or refactoring process? It's possible an earlier implementation or test case used a placeholder, and it hasn't been properly removed. Perhaps the logic intended to dynamically determine the file, but the code isn't fully implemented or has an error. Or, perhaps, it expects standard in.
* **Implied Standard Input:** Could this be a deliberate signal to `patch-diff` that it should operate on standard input, either because the patch is piped in or because the intended base content is implicitly elsewhere, like perhaps from a previous step in a build process? This would be a more sophisticated design pattern. I need to check the tool's documentation or the source code to confirm the behavior on a blank file argument.
* **Specialized Use Case:** Is `patch-diff` part of a larger workflow or toolchain? Could an empty filename have a very specific, application-dependent meaning? It might be calling another tool with standard input. This seems less likely on face value, but I shouldn't dismiss it yet.
* **Security Implications:** While rare, it's worth considering if this presents any security vulnerabilities, such as a code injection issue if the empty string is handled in an unexpected way. Even if unlikely, best practice is to at least consider the corner cases.
Ultimately, I need to know why the empty file parameter is necessary. The most important next step is to examine the surrounding code to determine intent, and failing that, consult documentation for the `patch-diff` tool.
|
| 197/4 |
2026/08/26 14:48 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
index 9c8cde125..f3e0ae6ee 100644
--- a/drivers/nfc/nfcmrvl/spi.c
+++ b/drivers/nfc/nfcmrvl/spi.c
@@ -22,32 +22,35 @@ struct nfcmrvl_spi_drv_data {
struct nci_spi *nci_spi;
struct completion handshake_completion;
struct nfcmrvl_private *priv;
};
static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
{
struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
struct sk_buff *skb;
+ if (!drv_data->priv)
+ return IRQ_HANDLED;
+
/*
* Special case where we are waiting for SPI_INT deassertion to start a
* transfer.
*/
if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
complete(&drv_data->handshake_completion);
return IRQ_HANDLED;
}
/* Normal case, SPI_INT deasserted by slave to trigger a master read */
skb = nci_spi_read(drv_data->nci_spi);
if (!skb) {
nfc_err(&drv_data->spi->dev, "failed to read spi packet");
return IRQ_HANDLED;
}
if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
nfc_err(&drv_data->spi->dev, "corrupted RX packet");
return IRQ_HANDLED;
}
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
@@ -49,97 +49,99 @@ static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
if (!rv)
drv_data->tx_in_flight++;
spin_unlock_irqrestore(&drv_data->txlock, flags);
return rv;
}
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");
}
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
return;
usb_anchor_urb(urb, &drv_data->bulk_anchor);
usb_mark_last_busy(drv_data->udev);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
/* -EPERM: urb is being killed;
* -ENODEV: device got disconnected
*/
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p failed to resubmit (%d)\n", urb, -err);
usb_unanchor_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;
int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
if (!drv_data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(drv_data->udev,
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;
usb_mark_last_busy(drv_data->udev);
usb_anchor_urb(urb, &drv_data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
}
@@ -166,27 +168,27 @@ static void nfcmrvl_tx_complete(struct urb *urb)
static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
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;
failed:
usb_autopm_put_interface(drv_data->intf);
return err;
}
static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
@@ -393,47 +395,48 @@ static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
/* Cleanup the rest deferred urbs. */
while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
kfree(urb->setup_packet);
usb_free_urb(urb);
}
}
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);
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);
nfcmrvl_play_deferred(drv_data);
clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
spin_unlock_irq(&drv_data->txlock);
return 0;
failed:
usb_scuttle_anchored_urbs(&drv_data->deferred);
done:
spin_lock_irq(&drv_data->txlock);
clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
spin_unlock_irq(&drv_data->txlock);
return err;
}
#endif
]
|
| 198/4 |
2026/08/26 14:48 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 74437
output: 904
thoughts: 936
Thoughts: **Code Review: NFC Driver - Marvell USB Implementation**
Okay, let's take a look at this diff. I've been carefully reviewing these changes, ensuring they are correct and follow best practices.
First, I examined the `drivers/nfc/nfcmrvl/spi.c` file and specifically the `nfcmrvl_spi_int_irq_thread_fn` function. The initial check for `drv_data->priv` seems reasonable to me, preventing a potential null pointer dereference if the driver data isn't properly initialized. It's a standard and good practice.
Next, I moved on to `drivers/nfc/nfcmrvl/usb.c`, which is the main focus of this review. The `nfcmrvl_bulk_complete` function immediately caught my eye. The check `if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))` is good; it prevents processing data when the NCI layer isn't active. The error handling with `nfc_err()` on memory allocation failure with `nci_skb_alloc()` is also standard, making sure the code handles possible out-of-memory errors appropriately. I see the check `if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))` is good for an early return. The use of `skb_put_data` and the call to `nfcmrvl_nci_recv_frame` for processing the received data make sense in this context.
Looking at `nfcmrvl_submit_bulk_urb` and `nfcmrvl_usb_nci_open` confirms the setup. I appreciate the use of `usb_fill_bulk_urb` to properly configure the USB bulk transfer and `nfcmrvl_bulk_complete` as the completion callback. The double call to `nfcmrvl_submit_bulk_urb` in `nfcmrvl_usb_nci_open` after `set_bit` suggests a double buffering or perhaps a pipelining strategy that looks reasonable.
Finally, in `nfcmrvl_resume`, I checked how the USB bulk transfers are re-submitted after a resume event. The decrement of `drv_data->suspend_count` with the early return in the initial `if` statement is good. The check for `NFCMRVL_NCI_RUNNING` and re-submission of the URBs are what I expect to see to ensure continuous operation after a suspend/resume cycle. The conditional re-submission using the `NFCMRVL_USB_BULK_RUNNING` flag and the error handling there seem perfectly fine.
Overall, the code looks solid and well-structured. The checks for `NULL` pointers, and flags seem complete. The error handling is sensible and appropriate for the context. I don't see any obvious issues or potential vulnerabilities at this glance.
|
| 199/3 |
2026/08/26 14:48 |
action |
test-patch |
20m
Results: map[PatchDiff:diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
index 9c8cde125..f3e0ae6ee 100644
--- a/drivers/nfc/nfcmrvl/spi.c
+++ b/drivers/nfc/nfcmrvl/spi.c
@@ -29,6 +29,9 @@ static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
struct sk_buff *skb;
+ if (!drv_data->priv)
+ return IRQ_HANDLED;
+
/*
* Special case where we are waiting for SPI_INT deassertion to start a
* transfer.
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);
TestError:]
|
| 200/3 |
2026/08/26 15:08 |
action |
If |
2m
Args:
|
| 201/4 |
2026/08/26 15:08 |
agent |
patch-reviewer |
2m
Model:
Results: map[ReviewApproved:false ReviewComments:[Defensive sentinel guard in drivers/nfc/nfcmrvl/spi.c: Adding 'if (!drv_data->priv) return IRQ_HANDLED;' inside nfcmrvl_spi_int_irq_thread_fn() is an ad-hoc sentinel band-aid that masks improper initialization order in nfcmrvl_spi_probe(). Moreover, it is incomplete and fragile because drv_data->nci_spi and drv_data->handshake_completion are also not initialized until after probe progresses. The IRQ handler should not be burdened with defensive guards for uninitialized driver state. Lifecycle ordering in SPI probe: If addressing the SPI driver lifecycle, the IRQ registration (devm_request_threaded_irq) must be performed after all driver structures (drv_data->priv, drv_data->nci_spi, and drv_data->handshake_completion) are fully allocated and initialized. Otherwise, keep the patch focused strictly on the USB PHY bug.]]
Instruction: You are an experienced Linux kernel maintainer reviewing a proposed patch that compiles and passes reproducer tests.
Evaluate the patch diff against the crash report, root cause, and the architectural design guidelines below.
Scope Constraint (Single-Patch Submissions):
We only generate single, standalone patches (not patch series). If a comprehensive architectural redesign would require a multi-patch series touching many files, accept a localized single-patch fix as long as it correctly fixes the bug, does not introduce new issues, and avoids sentinel band-aids within its local scope.
Evaluation:
- Set ReviewApproved=true (and leave ReviewComments empty) if the patch is acceptable for upstream submission as a single-patch fix.
- Set ReviewApproved=false and list concise, actionable feedback in ReviewComments if the patch contains fixable "Kernel Architectural Design & Maintainer Taste" violations.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
---
# Kernel Architectural Design & Maintainer Taste
Evaluate kernel patches and fix proposals beyond binary bug presence. Enforce **maintainer taste**, **lifecycle symmetry**, **typestate soundness**, and **topological graph simplicity**.
---
## 1. Core Philosophy: Band-Aid vs. Architectural Design
Naive patches often introduce **defensive sentinel guards** (e.g., ad-hoc `if (!ptr) return;` checks, status flags, or suppressed warnings) directly at the point of failure. While locally avoiding crashes, they degrade subsystem architecture by leaving uninitialized states reachable.
Maintainer "good taste" solves the root cause by **shifting invariants** (making invalid states unrepresentable) and moving dynamic runtime checks into static typestate guarantees.
```
HACKY BAND-AID FIX (Monolithic Cleanup) ARCHITECTURAL DESIGN (Scoped RAII / Invariant Shift)
─────────────────────────────────────── ────────────────────────────────────────────────────
[ Init / Entry ] [ Init / Entry ]
│ │
(Register Global Cleanup) (Allocate Resource A)
│ │
[ Init Sub-B ] ──fail──┐ [ Init Sub-B ] ──fail──┐
│ (success) │ │ (success) │
▼ │ ▼ ▼
[ Setup Complete ] │ (Arm B Cleanup / RAII) (Unwind A Only:
│ │ │ B never armed)
(Deferred Teardown) │ ▼ │
│ │ [ Setup Complete ] ▼
▼ ▼ │ [ Error Exit ]
[ release_all ] ◄───────┘ (LIFO Scope Teardown)
│ │
(if (!ctx->b) return;) <-- Defensive guard! ▼
│ [ Clean Destruction ]
[ Unsafe State ] (Zero sentinel checks needed)
```
```c
// ANTI-PATTERN (Defensive Sentinel Guard): Teardown called on partial init -> callee needs guard
void driver_cleanup(struct ctx *ctx) {
if (!ctx->buf) return; // <-- Sentinel band-aid
free_buffer(ctx->buf);
}
// CANONICAL SHIFT (Granular Scoped Action): Registered only upon complete initialization
ctx->buf = alloc_buffer();
if (!ctx->buf) return -ENOMEM;
devm_add_action_or_reset(dev, free_buffer_action, ctx->buf);
```
---
## 2. Maintainer Taste as Graph Topology & Structural Simplicity
Software design quality maps directly to graph-theoretic properties across the Control-Flow Graph (CFG), Data-Flow Graph (DFG), and Object Lifecycle DAG:
### A. Control-Flow Graph (CFG) Simplification
* **Branch Minimization & Path Explosion (McCabe 1976):** Every defensive check added to a compound destructor or callback (`if (!ctx->buffer) return;`) adds a predicate node ($\pi$), increases cyclomatic complexity $v(G) = |E| - |V| + 2$, causes exponential path explosion ($O(2^k)$ paths), and enlarges explicit state spaces ($|S| = \prod |D_i|$). Good taste eliminates the branch by guaranteeing destructors are invoked only on initialized typestates. *(Note: Standard idempotent leaf deallocators like `kfree(NULL)` and public API input sanitizers are exempt).*
* **Single-Entry Single-Exit (SESE) Symmetry (Ferrante 1987, Johnson 1994, Dijkstra 1972):** Resource acquisition and release must form strict **nested dominator trees**. If resource $R_i$ is acquired at node $A$, the set of release actions $\{B_1, \dots, B_m\}$ must form a strict **post-dominating cut** relative to $A$ across all maximal exit paths.
* **Pointer Uniformity (Linus's "Good Taste" Rule):** Eliminate special-case conditional branches by operating on address indirection (e.g., indirect pointers `**curr` in linked list unlinking) to unify edge and interior cases into a branchless invariant.
* **Lexical Scope Invariants & Affine Lifecycles (Wadler 1990, RAII):** Enforce SESE symmetry and "consumed exactly once" affine invariants using compiler-backed scoped cleanup (`<linux/cleanup.h>` `guard()`, `scoped_guard()`, `__free()`).
### B. Ownership & Lifetime DAGs
* **Acyclic Lifecycles & Topological Teardown (Tarjan 1972, Kahn 1962):** Resource ownership must form a strict Directed Acyclic Graph (DAG) $G = (R, E)$. Teardown order must strictly follow reverse topological sort $\text{toposort}(G)^R$. Mixing conflicting lifetime paradigms (e.g., embedding a dynamic refcounted `kref`/socket struct inside a device-managed `devres` buffer or parent container) violates DAG acyclicity, creating synchronous blocking hacks (`wait_for_completion`), circular pins, and Use-After-Free hazards.
* **Three-Phase Concurrent Quiescence:** Multi-threaded and asynchronous teardown (networking, block layer, RCU) must strictly sequence: (1) **Deactivation/Delisting** (make unreachable) -> (2) **Quiescence & Draining** (`synchronize_rcu()`, `cancel_work_sync()`, `napi_disable()`) -> (3) **Physical Reclamation** (`kfree()`, `kmem_cache_destroy()`).
* **Typestate Validity (Strom & Yemini 1986, Aldrich et al. 2009):** A struct with $N$ fields should not use runtime boolean flags (`ctx->is_initialized`) to model incomplete typestates. Sub-resources must transition as a deterministic typestate automaton ($S_{uninit} \xrightarrow{\text{alloc}} S_{init} \xrightarrow{\text{publish}} S_{registered}$), and registration functions must accept only fully initialized typestates.
---
## 3. Deterministic Decision Trigger Matrix
| Code Symptom / Trigger (When you see X) | Anti-Pattern Band-Aid (DO NOT DO Z) | Canonical Invariant Shift (DO Y) |
| :--- | :--- | :--- |
| **Null deref in compound destructor / cleanup callback** | Add `if (!priv->buf) return;` in composite cleanup handler | Register granular cleanup immediately upon allocation via `devm_add_action_or_reset()`, `<linux/cleanup.h>` `__free()`, or discrete reverse LIFO labels |
| **UAF on dynamic object after container unbind** | Allocate with `devm_kzalloc()` and block on `wait_for_completion()` | Allocate with `kzalloc()`, manage lifetime via `kref_get()`/`kref_put()`, call unbind/delist on unbind, free in `kref` release callback |
| **Goto ladder lock leaks on early error exit** | Sprinkle manual `mutex_unlock()` across error returns | Use `guard(mutex)(&lock)` or `scoped_guard(spinlock, &lock)` from `<linux/cleanup.h>` |
| **Callback / IRQ / timer fires before full init** | Add `if (!priv->ready)` check inside IRQ/timer handler | Move `request_irq()`, `timer_setup()`, or `napi_enable()` strictly to the end of setup after all state structures are fully initialized |
| **Multi-step setup failure leaks resources** | Route all errors to a single `err:` label calling a monolithic `cleanup(priv)` with NULL checks | Use `cleanup.h` RAII, granular `devm` actions, or a strict reverse LIFO goto ladder (`err_free_b:` -> `err_free_a:`) |
| **Mixed ownership / asymmetric refcount drops** | Conditionally call `kref_put()` in caller based on error code | Enforce unconditional callee-cleans or caller-cleans ownership convention across all paths |
| **Ad-hoc state flag polling during teardown** | Add `priv->stopping = true` and spin/poll in callbacks | Use atomic typestate transitions and synchronous flush/drain APIs (`cancel_work_sync()`, `drain_workqueue()`) |
---
## 4. The Architectural Review Checklist
Before finalizing any kernel fix or review, audit against the following four criteria:
1. **The Sentinel Test (Caller vs. Callee Responsibility):**
* *Smell:* Adding a defensive guard (`if (!ptr)` or `if (flags & INITIALIZED)`) inside a compound teardown callback or destructor to mask partial initialization.
* *Invariant:* If a destructor executes on uninitialized data, the defect is at the **caller's registration/invocation point**, not the callee.
* *Note:* Standard C allocator no-ops (`kfree(NULL)`) and public API parameter validators (`if (WARN_ON(!ptr)) return -EINVAL;`) are exempt; this rule targets *internal subsystem lifecycle pipelines and teardown paths*.
* *Action:* Move registration to the point of complete initialization (`devm_add_action_or_reset()`, RAII/`__free()`, or discrete caller unwinding).
2. **LIFO Stack Unwinding Symmetry:**
* *Smell:* Releasing resources in arbitrary order or invoking destructors for partially initialized subsystems.
* *Invariant:* Resource release must strictly mirror acquisition order in reverse (LIFO) without monolithic cleanup guards.
* *Action:* Verify that partial setup/initialization failures (e.g., `probe()`, `mount()`, `open()`, `alloc_pool()`) unwind only already-allocated resources in reverse order via strict LIFO labels or `cleanup.h` scope exit.
3. **Ownership Decoupling:**
* *Smell:* Embedding dynamically refcounted objects (`kref`, `struct sock`, `struct inode`, `struct file`) directly inside hardware device-bound allocations (`devres`) or parent containers (`super_block`, `net_device`).
* *Invariant:* Dynamic consumer references must outlive parent unregistration without use-after-free, circular pins, or blocking hacks.
* *Action:* Decouple the refcounted core object from the container binding; container teardown must unbind/delist the object, but memory is freed exclusively by the final `kref_put()`.
4. **Codebase Idiom Alignment:**
* *Smell:* Using legacy custom rollback ladders, ad-hoc state flags, or manual lock unlocking on error paths.
* *Invariant:* Prefer standard, modern kernel abstractions with compiler-enforced safety guarantees.
* *Action (Modern >= 6.6):* Replace manual boilerplate with `cleanup.h` (`guard()`, `scoped_guard()`, `__free()`), `devm_add_action_or_reset()`, `refcount_t`, and standard `kref` helpers.
* *Action (Legacy / LTS / No-RAII Subsystems):* Enforce clean, branchless reverse LIFO goto ladders (`err_free_b:` -> `err_free_a:`).
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
Bug title:
general protection fault in nfcmrvl_bulk_complete
The crash report:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The root cause explanation:
The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
The proposed patch diff:
diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
index 9c8cde125..f3e0ae6ee 100644
--- a/drivers/nfc/nfcmrvl/spi.c
+++ b/drivers/nfc/nfcmrvl/spi.c
@@ -29,6 +29,9 @@ static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
struct sk_buff *skb;
+ if (!drv_data->priv)
+ return IRQ_HANDLED;
+
/*
* Special case where we are waiting for SPI_INT deassertion to start a
* transfer.
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);
Review this patch diff against the crash report, root cause explanation, and kernel architectural design guidelines.
|
| 202/5 |
2026/08/26 15:08 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 14728
output: 164
thoughts: 140
Thoughts: Okay, here's my breakdown of what's going through my mind as I approach this issue.
**Analyzing the NFC Driver Problem**
Right, let's dive in. My immediate thought is to systematically dissect this. We're looking at a problem, which means we need a thorough understanding of the root cause, potential fixes, how those fixes will look (the patch diff), and how all of this aligns with established design guidelines. I'm going to follow my standard approach.
First, the codebase exploration. Time to get familiar with the relevant pieces. I'm going straight for the `drivers/nfc/nfcmrvl/` directory – that's the obvious starting point. I'll need to use my preferred tools, probably `read-file` or `codesearch-*`, to examine the key files. I'm especially interested in `usb.c`, `spi.c`, `i2c.c`, `main.c`, and `nfcmrvl.h`. Those are the usual suspects for this kind of low-level driver implementation. I'll need to understand the interaction between those components. Let's see what we've got.
|
| 203/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-USB driver: USB interface related functions
4: *
5: * Copyright (C) 2014, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/usb.h>
10: #include <linux/nfc.h>
11: #include <net/nfc/nci.h>
12: #include <net/nfc/nci_core.h>
13: #include "nfcmrvl.h"
14:
15: static struct usb_device_id nfcmrvl_table[] = {
16: { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
17: USB_CLASS_VENDOR_SPEC, 4, 1) },
18: { } /* Terminating entry */
19: };
20:
21: MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
22:
23: #define NFCMRVL_USB_BULK_RUNNING 1
24: #define NFCMRVL_USB_SUSPENDING 2
25:
26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
30: struct work_struct waker;
31: struct usb_anchor tx_anchor;
32: struct usb_anchor bulk_anchor;
33: struct usb_anchor deferred;
34: int tx_in_flight;
35: /* protects tx_in_flight */
36: spinlock_t txlock;
37: struct usb_endpoint_descriptor *bulk_tx_ep;
38: struct usb_endpoint_descriptor *bulk_rx_ep;
39: int suspend_count;
40: struct nfcmrvl_private *priv;
41: };
42:
43: static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
44: {
45: unsigned long flags;
46: int rv;
47:
48: spin_lock_irqsave(&drv_data->txlock, flags);
49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
100: }
]
|
| 204/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:101 LineCount:100]
Results: map[Contents: 101:
102: static int
103: nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
104: {
105: struct urb *urb;
106: unsigned char *buf;
107: unsigned int pipe;
108: int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
109:
110: if (!drv_data->bulk_rx_ep)
111: return -ENODEV;
112:
113: urb = usb_alloc_urb(0, mem_flags);
114: if (!urb)
115: return -ENOMEM;
116:
117: buf = kmalloc(size, mem_flags);
118: if (!buf) {
119: usb_free_urb(urb);
120: return -ENOMEM;
121: }
122:
123: pipe = usb_rcvbulkpipe(drv_data->udev,
124: drv_data->bulk_rx_ep->bEndpointAddress);
125:
126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
127: nfcmrvl_bulk_complete, drv_data);
128:
129: urb->transfer_flags |= URB_FREE_BUFFER;
130:
131: usb_mark_last_busy(drv_data->udev);
132: usb_anchor_urb(urb, &drv_data->bulk_anchor);
133:
134: err = usb_submit_urb(urb, mem_flags);
135: if (err) {
136: if (err != -EPERM && err != -ENODEV)
137: nfc_err(&drv_data->udev->dev,
138: "urb %p submission failed (%d)\n", urb, -err);
139: usb_unanchor_urb(urb);
140: }
141:
142: usb_free_urb(urb);
143:
144: return err;
145: }
146:
147: static void nfcmrvl_tx_complete(struct urb *urb)
148: {
149: struct sk_buff *skb = urb->context;
150: struct nci_dev *ndev = (struct nci_dev *)skb->dev;
151: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
152: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
153: unsigned long flags;
154:
155: nfc_info(priv->dev, "urb %p status %d count %d\n",
156: urb, urb->status, urb->actual_length);
157:
158: spin_lock_irqsave(&drv_data->txlock, flags);
159: drv_data->tx_in_flight--;
160: spin_unlock_irqrestore(&drv_data->txlock, flags);
161:
162: kfree(urb->setup_packet);
163: kfree_skb(skb);
164: }
165:
166: static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
167: {
168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
169: int err;
170:
171: err = usb_autopm_get_interface(drv_data->intf);
172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
188: usb_autopm_put_interface(drv_data->intf);
189: return err;
190: }
191:
192: static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
193: {
194: usb_kill_anchored_urbs(&drv_data->bulk_anchor);
195: }
196:
197: static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
198: {
199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
200: int err;
]
|
| 205/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:400 LineCount:100]
Results: map[Contents: 400: static int nfcmrvl_resume(struct usb_interface *intf)
401: {
402: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
403: int err = 0;
404:
405: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
406:
407: if (--drv_data->suspend_count)
408: return 0;
409:
410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
411: goto done;
412:
413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
421: }
422:
423: spin_lock_irq(&drv_data->txlock);
424: nfcmrvl_play_deferred(drv_data);
425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
426: spin_unlock_irq(&drv_data->txlock);
427:
428: return 0;
429:
430: failed:
431: usb_scuttle_anchored_urbs(&drv_data->deferred);
432: done:
433: spin_lock_irq(&drv_data->txlock);
434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
435: spin_unlock_irq(&drv_data->txlock);
436:
437: return err;
438: }
439: #endif
440:
441: static struct usb_driver nfcmrvl_usb_driver = {
442: .name = "nfcmrvl",
443: .probe = nfcmrvl_probe,
444: .disconnect = nfcmrvl_disconnect,
445: #ifdef CONFIG_PM
446: .suspend = nfcmrvl_suspend,
447: .resume = nfcmrvl_resume,
448: .reset_resume = nfcmrvl_resume,
449: #endif
450: .id_table = nfcmrvl_table,
451: .supports_autosuspend = 1,
452: .disable_hub_initiated_lpm = 1,
453: .soft_unbind = 1,
454: };
455: module_usb_driver(nfcmrvl_usb_driver);
456:
457: MODULE_AUTHOR("Marvell International Ltd.");
458: MODULE_DESCRIPTION("Marvell NFC-over-USB driver");
459: MODULE_LICENSE("GPL v2");
]
|
| 206/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC driver: major functions
4: *
5: * Copyright (C) 2014-2015 Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/gpio/consumer.h>
10: #include <linux/delay.h>
11: #include <linux/of.h>
12: #include <linux/nfc.h>
13: #include <net/nfc/nci.h>
14: #include <net/nfc/nci_core.h>
15: #include "nfcmrvl.h"
16:
17: static int nfcmrvl_nci_open(struct nci_dev *ndev)
18: {
19: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
20: int err;
21:
22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
23: return 0;
24:
25: /* Reset possible fault of previous session */
26: clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
27:
28: err = priv->if_ops->nci_open(priv);
29:
30: if (err)
31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
32:
33: return err;
34: }
35:
36: static int nfcmrvl_nci_close(struct nci_dev *ndev)
37: {
38: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
39:
40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
41: return 0;
42:
43: priv->if_ops->nci_close(priv);
44:
45: return 0;
46: }
47:
48: static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
49: {
50: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
51:
52: nfc_info(priv->dev, "send entry, len %d\n", skb->len);
53:
54: skb->dev = (void *)ndev;
55:
56: if (priv->config.hci_muxed) {
57: unsigned char *hdr;
58: unsigned char len = skb->len;
59:
60: hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
61: hdr[0] = NFCMRVL_HCI_COMMAND_CODE;
62: hdr[1] = NFCMRVL_HCI_OGF;
63: hdr[2] = NFCMRVL_HCI_OCF;
64: hdr[3] = len;
65: }
66:
67: return priv->if_ops->nci_send(priv, skb);
68: }
69:
70: static int nfcmrvl_nci_setup(struct nci_dev *ndev)
71: {
72: __u8 val = 1;
73:
74: nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val);
75: return 0;
76: }
77:
78: static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
79: const char *firmware_name)
80: {
81: return nfcmrvl_fw_dnld_start(ndev, firmware_name);
82: }
83:
84: static const struct nci_ops nfcmrvl_nci_ops = {
85: .open = nfcmrvl_nci_open,
86: .close = nfcmrvl_nci_close,
87: .send = nfcmrvl_nci_send,
88: .setup = nfcmrvl_nci_setup,
89: .fw_download = nfcmrvl_nci_fw_download,
90: };
91:
92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
]
|
| 207/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 20741
output: 167
thoughts: 94
Thoughts: Here's my thinking process:
**Diving into the Marvell NFC Driver Code**
Okay, so I need to get a handle on the Marvell NFC driver implementation. To do that, I'm going to focus my attention on several key files. First, the header file, `drivers/nfc/nfcmrvl/nfcmrvl.h` – that's the starting point for understanding the overall structure and the defined interfaces. Then, I'll move on to `drivers/nfc/nfcmrvl/main.c`, which is likely where the core driver logic resides. I'm assuming it has the crucial initialization, registration, and core operational functions.
Next, I need to understand how this driver interacts with the hardware, particularly in the context of various transports, so I'll need to drill down into `drivers/nfc/nfcmrvl/usb.c`, paying close attention to the `probe` and `disconnect` routines. The way the USB interface is managed will probably be a good indicator of overall device handling. I'll need to see how the device is initialized and uninitialized.
After that, I'll broaden my view to cover other potential interfaces, specifically `drivers/nfc/nfcmrvl/spi.c` and `drivers/nfc/nfcmrvl/i2c.c`. These files should shed light on how the driver is structured to support these alternative hardware transports. I'm expecting to find similar `probe` and `disconnect` style functions or equivalent. By examining all of these, I should get a fairly complete picture of the Marvell NFC driver's design and implementation.
|
| 208/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:1 LineCount:100]
Results: map[Contents: 1: /* SPDX-License-Identifier: GPL-2.0-only */
2: /*
3: * Marvell NFC driver
4: *
5: * Copyright (C) 2014-2015, Marvell International Ltd.
6: */
7:
8: #ifndef _NFCMRVL_H_
9: #define _NFCMRVL_H_
10:
11: #include "fw_dnld.h"
12:
13: struct gpio_desc;
14:
15: /* Define private flags: */
16: #define NFCMRVL_NCI_RUNNING 1
17: #define NFCMRVL_PHY_ERROR 2
18:
19: #define NFCMRVL_EXT_COEX_ID 0xE0
20: #define NFCMRVL_NOT_ALLOWED_ID 0xE1
21: #define NFCMRVL_ACTIVE_ID 0xE2
22: #define NFCMRVL_EXT_COEX_ENABLE 1
23: #define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA
24: #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB
25: #define NFCMRVL_NCI_MAX_EVENT_SIZE 260
26:
27: /*
28: * NCI FW Parameters
29: */
30:
31: #define NFCMRVL_PB_BAIL_OUT 0x11
32: #define NFCMRVL_PROP_REF_CLOCK 0xF0
33: #define NFCMRVL_PROP_SET_HI_CONFIG 0xF1
34:
35: /*
36: * HCI defines
37: */
38:
39: #define NFCMRVL_HCI_EVENT_HEADER_SIZE 0x04
40: #define NFCMRVL_HCI_EVENT_CODE 0x04
41: #define NFCMRVL_HCI_NFC_EVENT_CODE 0xFF
42: #define NFCMRVL_HCI_COMMAND_CODE 0x01
43: #define NFCMRVL_HCI_OGF 0x81
44: #define NFCMRVL_HCI_OCF 0xFE
45:
46: enum nfcmrvl_phy {
47: NFCMRVL_PHY_USB = 0,
48: NFCMRVL_PHY_UART = 1,
49: NFCMRVL_PHY_I2C = 2,
50: NFCMRVL_PHY_SPI = 3,
51: };
52:
53: struct nfcmrvl_platform_data {
54: /*
55: * Generic
56: */
57:
58: /* GPIO that is wired to RESET_N signal */
59: struct gpio_desc *reset_gpio;
60: /* Tell if transport is muxed in HCI one */
61: bool hci_muxed;
62:
63: /*
64: * UART specific
65: */
66:
67: /* Tell if UART needs flow control at init */
68: bool flow_control;
69: /* Tell if firmware supports break control for power management */
70: bool break_control;
71:
72:
73: /*
74: * I2C specific
75: */
76:
77: unsigned int irq;
78: unsigned int irq_polarity;
79: };
80:
81: struct nfcmrvl_private {
82:
83: unsigned long flags;
84:
85: /* Platform configuration */
86: struct nfcmrvl_platform_data config;
87:
88: /* Parent dev */
89: struct nci_dev *ndev;
90:
91: /* FW download context */
92: struct nfcmrvl_fw_dnld fw_dnld;
93:
94: /* FW download support */
95: bool support_fw_dnld;
96:
97: /*
98: * PHY related information
99: */
100:
]
|
| 209/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:101 LineCount:100]
Results: map[Contents: 101: int tailroom;
102: u32 protocols;
103:
104: priv = kzalloc_obj(*priv);
105: if (!priv)
106: return ERR_PTR(-ENOMEM);
107:
108: priv->drv_data = drv_data;
109: priv->if_ops = ops;
110: priv->dev = dev;
111: priv->phy = phy;
112:
113: memcpy(&priv->config, pdata, sizeof(*pdata));
114:
115: if (!priv->config.reset_gpio) {
116: priv->config.reset_gpio =
117: devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
118: if (IS_ERR(priv->config.reset_gpio)) {
119: priv->config.reset_gpio = NULL;
120: nfc_err(dev, "failed to get reset gpio\n");
121: }
122: }
123:
124: if (phy == NFCMRVL_PHY_SPI) {
125: headroom = NCI_SPI_HDR_LEN;
126: tailroom = 1;
127: } else
128: headroom = tailroom = 0;
129:
130: if (priv->config.hci_muxed)
131: headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
132:
133: protocols = NFC_PROTO_JEWEL_MASK
134: | NFC_PROTO_MIFARE_MASK
135: | NFC_PROTO_FELICA_MASK
136: | NFC_PROTO_ISO14443_MASK
137: | NFC_PROTO_ISO14443_B_MASK
138: | NFC_PROTO_ISO15693_MASK
139: | NFC_PROTO_NFC_DEP_MASK;
140:
141: priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
142: headroom, tailroom);
143: if (!priv->ndev) {
144: nfc_err(dev, "nci_allocate_device failed\n");
145: rc = -ENOMEM;
146: goto error_free;
147: }
148:
149: rc = nfcmrvl_fw_dnld_init(priv);
150: if (rc) {
151: nfc_err(dev, "failed to initialize FW download %d\n", rc);
152: goto error_free_dev;
153: }
154:
155: nci_set_drvdata(priv->ndev, priv);
156:
157: rc = nci_register_device(priv->ndev);
158: if (rc) {
159: nfc_err(dev, "nci_register_device failed %d\n", rc);
160: goto error_fw_dnld_deinit;
161: }
162:
163: /* Ensure that controller is powered off */
164: nfcmrvl_chip_halt(priv);
165:
166: nfc_info(dev, "registered with nci successfully\n");
167: return priv;
168:
169: error_fw_dnld_deinit:
170: nfcmrvl_fw_dnld_deinit(priv);
171: error_free_dev:
172: nci_free_device(priv->ndev);
173: error_free:
174: kfree(priv);
175: return ERR_PTR(rc);
176: }
177: EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
178:
179: void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
180: {
181: struct nci_dev *ndev = priv->ndev;
182:
183: nci_unregister_device(ndev);
184: if (priv->ndev->nfc_dev->fw_download_in_progress)
185: nfcmrvl_fw_dnld_abort(priv);
186:
187: nfcmrvl_fw_dnld_deinit(priv);
188:
189: nci_free_device(ndev);
190: kfree(priv);
191: }
192: EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
193:
194: int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
195: {
196: if (priv->config.hci_muxed) {
197: if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE &&
198: skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) {
199: /* Data packet, let's extract NCI payload */
200: skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
]
|
| 210/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:300 LineCount:100]
Results: map[Contents: 300:
301: drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
302: if (!drv_data)
303: return -ENOMEM;
304:
305: ret = usb_find_common_endpoints(intf->cur_altsetting, &drv_data->bulk_rx_ep,
306: &drv_data->bulk_tx_ep, NULL, NULL);
307: if (ret)
308: return -ENODEV;
309:
310: drv_data->udev = udev;
311: drv_data->intf = intf;
312:
313: INIT_WORK(&drv_data->waker, nfcmrvl_waker);
314: spin_lock_init(&drv_data->txlock);
315:
316: init_usb_anchor(&drv_data->tx_anchor);
317: init_usb_anchor(&drv_data->bulk_anchor);
318: init_usb_anchor(&drv_data->deferred);
319:
320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
332:
333: static void nfcmrvl_disconnect(struct usb_interface *intf)
334: {
335: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
336:
337: if (!drv_data)
338: return;
339:
340: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
341:
342: nfcmrvl_nci_unregister_dev(drv_data->priv);
343:
344: usb_set_intfdata(drv_data->intf, NULL);
345: }
346:
347: #ifdef CONFIG_PM
348: static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
349: {
350: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
351:
352: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
353:
354: if (drv_data->suspend_count++)
355: return 0;
356:
357: spin_lock_irq(&drv_data->txlock);
358: if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
359: set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
360: spin_unlock_irq(&drv_data->txlock);
361: } else {
362: spin_unlock_irq(&drv_data->txlock);
363: drv_data->suspend_count--;
364: return -EBUSY;
365: }
366:
367: nfcmrvl_usb_stop_traffic(drv_data);
368: usb_kill_anchored_urbs(&drv_data->tx_anchor);
369:
370: return 0;
371: }
372:
373: static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
374: {
375: struct urb *urb;
376: int err;
377:
378: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
379: usb_anchor_urb(urb, &drv_data->tx_anchor);
380:
381: err = usb_submit_urb(urb, GFP_ATOMIC);
382: if (err) {
383: kfree(urb->setup_packet);
384: usb_unanchor_urb(urb);
385: usb_free_urb(urb);
386: break;
387: }
388:
389: drv_data->tx_in_flight++;
390: usb_free_urb(urb);
391: }
392:
393: /* Cleanup the rest deferred urbs. */
394: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
395: kfree(urb->setup_packet);
396: usb_free_urb(urb);
397: }
398: }
399:
]
|
| 211/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-SPI driver: SPI interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/nfc.h>
11: #include <linux/of_irq.h>
12: #include <net/nfc/nci.h>
13: #include <net/nfc/nci_core.h>
14: #include <linux/spi/spi.h>
15: #include "nfcmrvl.h"
16:
17: #define SPI_WAIT_HANDSHAKE 1
18:
19: struct nfcmrvl_spi_drv_data {
20: unsigned long flags;
21: struct spi_device *spi;
22: struct nci_spi *nci_spi;
23: struct completion handshake_completion;
24: struct nfcmrvl_private *priv;
25: };
26:
27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
45: nfc_err(&drv_data->spi->dev, "failed to read spi packet");
46: return IRQ_HANDLED;
47: }
48:
49: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
50: nfc_err(&drv_data->spi->dev, "corrupted RX packet");
51:
52: return IRQ_HANDLED;
53: }
54:
55: static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
56: {
57: return 0;
58: }
59:
60: static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
61: {
62: return 0;
63: }
64:
65: static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
66: struct sk_buff *skb)
67: {
68: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
69: int err;
70:
71: /* Reinit completion for slave handshake */
72: reinit_completion(&drv_data->handshake_completion);
73: set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
74:
75: /*
76: * Append a dummy byte at the end of SPI frame. This is due to a
77: * specific DMA implementation in the controller
78: */
79: skb_put(skb, 1);
80:
81: /* Send the SPI packet */
82: err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
83: skb);
84: if (err)
85: nfc_err(priv->dev, "spi_send failed %d", err);
86:
87: return err;
88: }
89:
90: static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv,
91: const void *param)
92: {
93: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
94: const struct nfcmrvl_fw_spi_config *config = param;
95:
96: drv_data->nci_spi->xfer_speed_hz = config->clk;
97: }
98:
99: static const struct nfcmrvl_if_ops spi_ops = {
100: .nci_open = nfcmrvl_spi_nci_open,
]
|
| 212/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 27147
output: 84
thoughts: 32
Thoughts: Here's my take on the thought process, as if it were my own:
**Investigating NFC Driver Initialization**
Okay, so we've got an NFC driver issue to troubleshoot. My gut feeling is to immediately dive into the initialization process. Let's start by comparing how we're handling the I2C interface in the `drivers/nfc/nfcmrvl/i2c.c` file and the SPI interface in the `spi.c` file. Specifically, I want to zero in on their respective probe functions. The probe function is where the hardware is typically detected, and the driver is bound to the device. Any errors there, whether I2C or SPI related, could be the root cause. This side-by-side comparison should illuminate any glaring differences in how the Marvell NFC chip is being set up via these two distinct communication protocols. I’m thinking there might be something fundamental we are missing, or potentially a typo in either file that is preventing the chips from being found.
|
| 213/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:101 LineCount:100]
Results: map[Contents: 101: .nci_close = nfcmrvl_spi_nci_close,
102: .nci_send = nfcmrvl_spi_nci_send,
103: .nci_update_config = nfcmrvl_spi_nci_update_config,
104: };
105:
106: static int nfcmrvl_spi_parse_dt(struct device_node *node,
107: struct nfcmrvl_platform_data *pdata)
108: {
109: int ret;
110:
111: ret = nfcmrvl_parse_dt(node, pdata);
112: if (ret < 0) {
113: pr_err("Failed to get generic entries\n");
114: return ret;
115: }
116:
117: ret = irq_of_parse_and_map(node, 0);
118: if (!ret) {
119: pr_err("Unable to get irq\n");
120: return -EINVAL;
121: }
122: pdata->irq = ret;
123:
124: return 0;
125: }
126:
127: static int nfcmrvl_spi_probe(struct spi_device *spi)
128: {
129: const struct nfcmrvl_platform_data *pdata;
130: struct nfcmrvl_platform_data config;
131: struct nfcmrvl_spi_drv_data *drv_data;
132: int ret = 0;
133:
134: drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL);
135: if (!drv_data)
136: return -ENOMEM;
137:
138: drv_data->spi = spi;
139: drv_data->priv = NULL;
140: spi_set_drvdata(spi, drv_data);
141:
142: pdata = spi->dev.platform_data;
143:
144: if (!pdata && spi->dev.of_node)
145: if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0)
146: pdata = &config;
147:
148: if (!pdata)
149: return -EINVAL;
150:
151: ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
152: NULL, nfcmrvl_spi_int_irq_thread_fn,
153: IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
154: "nfcmrvl_spi_int", drv_data);
155: if (ret < 0) {
156: nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler");
157: return -ENODEV;
158: }
159:
160: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
161: drv_data, &spi_ops,
162: &drv_data->spi->dev,
163: pdata);
164: if (IS_ERR(drv_data->priv))
165: return PTR_ERR(drv_data->priv);
166:
167: drv_data->priv->support_fw_dnld = true;
168:
169: drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10,
170: drv_data->priv->ndev);
171:
172: /* Init completion for slave handshake */
173: init_completion(&drv_data->handshake_completion);
174: return 0;
175: }
176:
177: static void nfcmrvl_spi_remove(struct spi_device *spi)
178: {
179: struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi);
180:
181: nfcmrvl_nci_unregister_dev(drv_data->priv);
182: }
183:
184: static const struct of_device_id of_nfcmrvl_spi_match[] __maybe_unused = {
185: { .compatible = "marvell,nfc-spi", },
186: {},
187: };
188: MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match);
189:
190: static const struct spi_device_id nfcmrvl_spi_id_table[] = {
191: { "nfcmrvl_spi", 0 },
192: { }
193: };
194: MODULE_DEVICE_TABLE(spi, nfcmrvl_spi_id_table);
195:
196: static struct spi_driver nfcmrvl_spi_driver = {
197: .probe = nfcmrvl_spi_probe,
198: .remove = nfcmrvl_spi_remove,
199: .id_table = nfcmrvl_spi_id_table,
200: .driver = {
]
|
| 214/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-I2C driver: I2C interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/i2c.h>
11: #include <linux/nfc.h>
12: #include <linux/delay.h>
13: #include <linux/of_irq.h>
14: #include <net/nfc/nci.h>
15: #include <net/nfc/nci_core.h>
16: #include "nfcmrvl.h"
17:
18: struct nfcmrvl_i2c_drv_data {
19: unsigned long flags;
20: struct device *dev;
21: struct i2c_client *i2c;
22: struct nfcmrvl_private *priv;
23: };
24:
25: static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data,
26: struct sk_buff **skb)
27: {
28: int ret;
29: struct nci_ctrl_hdr nci_hdr;
30:
31: /* Read NCI header to know the payload size */
32: ret = i2c_master_recv(drv_data->i2c, (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE);
33: if (ret != NCI_CTRL_HDR_SIZE) {
34: nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n");
35: return -EBADMSG;
36: }
37:
38: *skb = nci_skb_alloc(drv_data->priv->ndev,
39: nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL);
40: if (!*skb)
41: return -ENOMEM;
42:
43: /* Copy NCI header into the SKB */
44: skb_put_data(*skb, &nci_hdr, NCI_CTRL_HDR_SIZE);
45:
46: if (nci_hdr.plen) {
47: /* Read the NCI payload */
48: ret = i2c_master_recv(drv_data->i2c,
49: skb_put(*skb, nci_hdr.plen),
50: nci_hdr.plen);
51:
52: if (ret != nci_hdr.plen) {
53: nfc_err(&drv_data->i2c->dev,
54: "Invalid frame payload length: %u (expected %u)\n",
55: ret, nci_hdr.plen);
56: kfree_skb(*skb);
57: return -EBADMSG;
58: }
59: }
60:
61: return 0;
62: }
63:
64: static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
65: {
66: struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
67: struct sk_buff *skb = NULL;
68: int ret;
69:
70: if (!drv_data->priv)
71: return IRQ_HANDLED;
72:
73: if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
74: return IRQ_HANDLED;
75:
76: ret = nfcmrvl_i2c_read(drv_data, &skb);
77:
78: switch (ret) {
79: case -EREMOTEIO:
80: set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
81: break;
82: case -ENOMEM:
83: case -EBADMSG:
84: nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
85: break;
86: default:
87: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
88: nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
89: break;
90: }
91: return IRQ_HANDLED;
92: }
93:
94: static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
95: {
96: struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
97:
98: if (!drv_data)
99: return -ENODEV;
100:
]
|
| 215/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 30599
output: 70
thoughts: 26
Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts:
**Investigating Potential Probe Issues**
Right, so we're looking for the root cause of this problem. Let's make sure we've covered the basics. My initial instinct is to check the `i2c.c` probe function, as I'm thinking that's a common area for initialization issues. I'll need to confirm that that is the correct file, of course. Also, in case the probe mechanism is using a serial interface as a fallback, I should also double-check the `uart.c` file and probe function, if there is one. We can't afford to overlook any potential interface that might be involved here. Better to be thorough and exclude possibilities than miss something obvious.
|
| 216/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:150 LineCount:70]
Results: map[Contents: 150: {
151: }
152:
153: static const struct nfcmrvl_if_ops i2c_ops = {
154: .nci_open = nfcmrvl_i2c_nci_open,
155: .nci_close = nfcmrvl_i2c_nci_close,
156: .nci_send = nfcmrvl_i2c_nci_send,
157: .nci_update_config = nfcmrvl_i2c_nci_update_config,
158: };
159:
160: static int nfcmrvl_i2c_parse_dt(struct device_node *node,
161: struct nfcmrvl_platform_data *pdata)
162: {
163: int ret;
164:
165: ret = nfcmrvl_parse_dt(node, pdata);
166: if (ret < 0) {
167: pr_err("Failed to get generic entries\n");
168: return ret;
169: }
170:
171: if (of_property_read_bool(node, "i2c-int-falling"))
172: pdata->irq_polarity = IRQF_TRIGGER_FALLING;
173: else
174: pdata->irq_polarity = IRQF_TRIGGER_RISING;
175:
176: ret = irq_of_parse_and_map(node, 0);
177: if (!ret) {
178: pr_err("Unable to get irq\n");
179: return -EINVAL;
180: }
181: pdata->irq = ret;
182:
183: return 0;
184: }
185:
186: static int nfcmrvl_i2c_probe(struct i2c_client *client)
187: {
188: const struct nfcmrvl_platform_data *pdata;
189: struct nfcmrvl_i2c_drv_data *drv_data;
190: struct nfcmrvl_platform_data config;
191: int ret;
192:
193: if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
194: nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
195: return -ENODEV;
196: }
197:
198: drv_data = devm_kzalloc(&client->dev, sizeof(*drv_data), GFP_KERNEL);
199: if (!drv_data)
200: return -ENOMEM;
201:
202: drv_data->i2c = client;
203: drv_data->dev = &client->dev;
204: drv_data->priv = NULL;
205:
206: i2c_set_clientdata(client, drv_data);
207:
208: pdata = client->dev.platform_data;
209:
210: if (!pdata && client->dev.of_node)
211: if (nfcmrvl_i2c_parse_dt(client->dev.of_node, &config) == 0)
212: pdata = &config;
213:
214: if (!pdata)
215: return -EINVAL;
216:
217: /* Request the read IRQ */
218: ret = devm_request_threaded_irq(&drv_data->i2c->dev, pdata->irq,
219: NULL, nfcmrvl_i2c_int_irq_thread_fn,
]
|
| 217/5 |
2026/08/26 15:09 |
tool |
codesearch-dir-index |
0m
Args: map[Dir:drivers/nfc/nfcmrvl]
Results: map[Files:[fw_dnld.c fw_dnld.h i2c.c main.c nfcmrvl.h spi.c uart.c usb.c] Subdirs:<nil>]
|
| 218/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 32070
output: 43
thoughts: 19
Thoughts: Okay, here's my attempt at summarizing the thought process, as requested, in the first person and targeted towards an expert:
**Diving Deeper into `nfcmrvl_i2c_probe`**
Alright, let's take a closer look at what's happening within `nfcmrvl_i2c_probe`. My primary concern, of course, is the integrity and efficiency of the probe process. I'm expecting to see the standard I2C device probing sequence, but with the Marvell NFC controller specifics in mind. I'm already anticipating areas where custom handling might be required, particularly around initialization and potential power-up sequences specific to this hardware. I want to check for proper error handling at each step: are they meticulously checking return values from I2C transfers? Are they handling potential timeouts gracefully, perhaps with retries? And, even more importantly, are they considering the implications of this probe within the context of the larger system? Does this function play nice with any existing resource management, interrupt handling, or power management frameworks? I am going to have to make sure that these assumptions are either valid, or must be resolved and documented if not. I need to understand whether this is a well-behaved driver or one that requires careful attention to avoid resource contention. I'm also watching for any "clever" or potentially fragile optimizations; while optimization is always welcome, I'm hoping it doesn't sacrifice readability or robustness in the process. Time to dig in and see.
|
| 219/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:220 LineCount:40]
Results: map[Contents: 220: pdata->irq_polarity | IRQF_ONESHOT,
221: "nfcmrvl_i2c_int", drv_data);
222: if (ret < 0) {
223: nfc_err(&drv_data->i2c->dev,
224: "Unable to register IRQ handler\n");
225: return ret;
226: }
227:
228: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
229: drv_data, &i2c_ops,
230: &drv_data->i2c->dev, pdata);
231:
232: if (IS_ERR(drv_data->priv))
233: return PTR_ERR(drv_data->priv);
234:
235: drv_data->priv->support_fw_dnld = true;
236:
237: return 0;
238: }
239:
240: static void nfcmrvl_i2c_remove(struct i2c_client *client)
241: {
242: struct nfcmrvl_i2c_drv_data *drv_data = i2c_get_clientdata(client);
243:
244: nfcmrvl_nci_unregister_dev(drv_data->priv);
245: }
246:
247:
248: static const struct of_device_id of_nfcmrvl_i2c_match[] __maybe_unused = {
249: { .compatible = "marvell,nfc-i2c", },
250: {},
251: };
252: MODULE_DEVICE_TABLE(of, of_nfcmrvl_i2c_match);
253:
254: static const struct i2c_device_id nfcmrvl_i2c_id_table[] = {
255: { .name = "nfcmrvl_i2c" },
256: { }
257: };
258: MODULE_DEVICE_TABLE(i2c, nfcmrvl_i2c_id_table);
259:
]
|
| 220/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 33110
output: 40
thoughts: 11
Thoughts: Here's my take on the `uart.c` analysis:
**Delving into the UART Implementation**
Okay, so we're looking at `uart.c` now. This is a crucial piece, obviously, dealing with the Universal Asynchronous Receiver/Transmitter. This is where the hardware interaction happens, the low-level stuff. I'm going to want to see how they've handled the initialization first and foremost. Are they using the standard registers, or did they define some macros for clearer addressing? Efficiency is key here; are they implementing interrupts correctly, or is it polling? A robust implementation usually benefits from interrupt-driven transfers to avoid blocking. I'll need to check the baud rate configuration; is it precise, and how is it derived from the system clock? What about error handling? Parity, framing errors, overrun errors - are they addressed? I'll also be interested in how they're handling transmit and receive buffers. Are they using circular buffers for efficient data flow? And finally, how well documented is the code? Clear comments are going to tell me a lot about the design philosophy and potential maintainability of this module. Let's dig in and see what's what.
|
| 221/5 |
2026/08/26 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/uart.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-UART driver
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/delay.h>
9: #include <linux/device.h>
10: #include <linux/err.h>
11: #include <linux/gpio/consumer.h>
12: #include <linux/module.h>
13: #include <linux/of.h>
14: #include <linux/printk.h>
15:
16: #include <net/nfc/nci.h>
17: #include <net/nfc/nci_core.h>
18:
19: #include "nfcmrvl.h"
20:
21: static unsigned int hci_muxed;
22: static unsigned int flow_control;
23: static unsigned int break_control;
24:
25: /*
26: * NFCMRVL NCI OPS
27: */
28:
29: static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv)
30: {
31: return 0;
32: }
33:
34: static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv)
35: {
36: return 0;
37: }
38:
39: static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
40: struct sk_buff *skb)
41: {
42: struct nci_uart *nu = priv->drv_data;
43:
44: return nu->ops.send(nu, skb);
45: }
46:
47: static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
48: const void *param)
49: {
50: struct nci_uart *nu = priv->drv_data;
51: const struct nfcmrvl_fw_uart_config *config = param;
52:
53: nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
54: config->flow_control);
55: }
56:
57: static const struct nfcmrvl_if_ops uart_ops = {
58: .nci_open = nfcmrvl_uart_nci_open,
59: .nci_close = nfcmrvl_uart_nci_close,
60: .nci_send = nfcmrvl_uart_nci_send,
61: .nci_update_config = nfcmrvl_uart_nci_update_config
62: };
63:
64: static int nfcmrvl_uart_parse_dt(struct device_node *node,
65: struct nfcmrvl_platform_data *pdata,
66: struct device *dev)
67: {
68: struct device_node *matched_node;
69: struct gpio_desc *reset_gpio;
70: int ret;
71:
72: matched_node = of_get_compatible_child(node, "marvell,nfc-uart");
73: if (!matched_node) {
74: matched_node = of_get_compatible_child(node, "mrvl,nfc-uart");
75: if (!matched_node)
76: return -ENODEV;
77: }
78:
79: ret = nfcmrvl_parse_dt(matched_node, pdata);
80: if (ret < 0) {
81: pr_err("Failed to get generic entries\n");
82: of_node_put(matched_node);
83: return ret;
84: }
85:
86: pdata->flow_control = of_property_read_bool(matched_node, "flow-control");
87: pdata->break_control = of_property_read_bool(matched_node, "break-control");
88:
89: reset_gpio = devm_fwnode_gpiod_get_optional(dev,
90: of_fwnode_handle(matched_node),
91: "reset", GPIOD_OUT_HIGH,
92: "nfcmrvl_reset_n");
93: if (IS_ERR(reset_gpio)) {
94: of_node_put(matched_node);
95: return PTR_ERR(reset_gpio);
96: }
97: pdata->reset_gpio = reset_gpio;
98:
99: of_node_put(matched_node);
100:
]
|
| 222/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 34837
output: 55
thoughts: 2337
Thoughts: **Analyzing the nfcmrvl Driver Initialization and Potential Race Conditions**
Okay, let's break this down systematically. I'm focusing on the initialization sequence of the `nfcmrvl` driver, specifically the USB and SPI implementations, and where the proposed patch tries to address a potential issue.
Initially, I'm examining the USB driver's `nfcmrvl_probe` function. It seems to call `nfcmrvl_nci_register_dev()`, which is responsible for registering the NFC device with the NCI core. Notably, the registration happens *before* the driver fully sets up all of its internal structures, specifically `drv_data->priv`. My immediate thought is, "Wait, that's unusual." In `nfcmrvl_nci_register_dev()`, it allocates and registers the device. Then the driver sets `drv_data->priv` *afterward*. This sequence suggests that the NCI core or userspace might be able to interact with the device before the driver is fully ready. Userspace can trigger an `nfc_dev_up` call immediately, which then opens the device, potentially calling a `nfcmrvl_usb_nci_open` function. In this function, the driver submits a USB bulk URB. The critical point is that at the time of URB submission, `drv_data->priv` might still be `NULL`.
Then I examine the proposed patch, which adds a check for `!drv_data->priv` in `nfcmrvl_spi_int_irq_thread_fn()`. My mind immediately jumps to an architectural question: Why is the SPI driver's interrupt firing before everything is initialized, and why does this check seem like a band-aid? Looking at `nfcmrvl_spi_probe()`, it's clear the interrupt is requested *before* `nfcmrvl_nci_register_dev()` and also before the allocation of `drv_data->nci_spi`. This raises a red flag; it's a direct violation of the "Callback/IRQ/timer fires before full init" anti-pattern identified in the Architectural Review Checklist. The check in the patch is a classic defensive guard in an IRQ handler. It looks like a sentinel test for an uninitialized pointer, which is generally a symptom of a deeper problem. And, of course, there's no check for `drv_data->nci_spi`! If the `drv_data->priv` is set, but the `drv_data->nci_spi` is not, the read call will crash. The IRQ being requested before initializing all the data structures and allocating `nci_spi` is a fundamental issue here!
This leads me to question the overall design. Why does `nfcmrvl_nci_register_dev` allocate *and* register in one step? Why doesn't the `nfcmrvl_probe` set up all data structures first, then call `nci_register_device`? Why is `drv_data->priv` accessed directly when there is an indirection, i.e., `nci_get_drvdata(ndev)`? The code in both `usb.c` and `spi.c` seem to share this unsafe pattern. It appears that `drv_data` contains the pointer to `priv`, which then can point to `drv_data` through `priv->drv_data`.
I then went through `usb.c` to understand where `drv_data->priv` is actually used. In the USB implementation, the `urb->context` is set to `drv_data`. In the `nfcmrvl_bulk_complete` callback, it uses `drv_data` via `urb->context` to access `drv_data->priv`, and in `nfcmrvl_tx_complete` it uses `nci_get_drvdata` to get `priv` and `priv->drv_data`. The fact that `drv_data` contains `drv_data->priv` seems redundant and the reason for the unsafe pattern. The `nfcmrvl_nci_register_dev` function is doing too much and should be split.
|
| 223/5 |
2026/08/26 15:09 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c IncludeSnippetLines:5 Name:nfcmrvl_usb_drv_data::priv]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_bulk_complete SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:71 SourceSnippet: 66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_bulk_complete SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:78 SourceSnippet: 73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_disconnect SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:342 SourceSnippet: 337: if (!drv_data)
338: return;
339:
340: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
341:
342: nfcmrvl_nci_unregister_dev(drv_data->priv);
343:
344: usb_set_intfdata(drv_data->intf, NULL);
345: }
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_probe SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:325 SourceSnippet: 320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_probe SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:326 SourceSnippet: 321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
]] TruncatedOutput:false]
|
| 224/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 39241
output: 43
thoughts: 1309
Thoughts: **Analyzing the `usb.c` Bug and Patch**
Okay, let's break this down systematically. I need to understand this null pointer dereference in `nfcmrvl_bulk_complete`. First, in `usb.c`, `drv_data->priv` seems to be the critical pointer here. It's used in `nfcmrvl_bulk_complete` to get `priv->ndev`, in `nfcmrvl_disconnect` for unregistering the device, and in `nfcmrvl_probe` to be set and initialized.
Now, that specific crash is in `nfcmrvl_bulk_complete`, specifically on line 71: `skb = nci_skb_alloc(drv_data->priv->ndev, ...);`. The error report says it's a null pointer dereference, meaning `drv_data->priv` must be NULL when this code is executed. From the context, this indicates that `priv->ndev` is at a 0x20 offset in the `struct nfcmrvl_private`!
Let's look at the patch to `nfcmrvl_resume`. The original code just used `drv_data`, whereas the patch introduces `priv` of type `struct nfcmrvl_private` to be used. The patch, specifically, does a direct assignment: `struct nfcmrvl_private *priv = drv_data->priv;`. Then, the patch uses `priv` instead of `drv_data`. The patch changes `nfcmrvl_resume` by using `priv` instead of `drv_data` in calls to `nfcmrvl_submit_bulk_urb`. The patch changes the conditional check with `NFCMRVL_NCI_RUNNING` from `drv_data->flags` to `priv->flags`.
So, the original code had an issue with the check of the running state, which meant the code used `drv_data` when it should have used `priv`, which led to an incorrect check. Let me re-verify that. The bug is triggered when `drv_data->priv` is null when line 71 is executed.
We have a null pointer dereference in `nfcmrvl_bulk_complete`, occurring because `drv_data->priv` is NULL. Let me double-check that `drv_data->priv` is initialized correctly in other places.
|
| 225/5 |
2026/08/26 15:09 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c Name:nfcmrvl_private]
Results: map[Fields:[map[Name:flags OffsetBits:0 SizeBits:64] map[Name:config OffsetBits:64 SizeBits:192] map[Name:ndev OffsetBits:256 SizeBits:64] map[Name:fw_dnld OffsetBits:320 SizeBits:2624] map[Name:support_fw_dnld OffsetBits:2944 SizeBits:8] map[Name:drv_data OffsetBits:3008 SizeBits:64] map[Name:dev OffsetBits:3072 SizeBits:64] map[Name:phy OffsetBits:3136 SizeBits:32] map[Name:if_ops OffsetBits:3200 SizeBits:64]]]
|
| 226/5 |
2026/08/26 15:09 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 41316
output: 30
thoughts: 6308
Thoughts: **Analysis of the nfcmrvl Driver Initialization and Potential Null Pointer Dereference**
Okay, so I've been tracing the root cause of this null pointer dereference, and it's becoming clear that the core issue is a race condition or incorrect initialization ordering in the `nfcmrvl` driver, specifically within the `usb.c`, `spi.c`, and `i2c.c` files. The problem arises because critical structures and their pointers are not fully initialized or are accessed prematurely before they are properly set.
My initial thought, that `drv_data->priv` at offset 0x20 would immediately cause a null pointer dereference, is correct. The vulnerability occurs because the `drv_data->priv` pointer, and subsequently `drv_data->priv->ndev`, is being accessed before it is set to a valid value. This is happening because of incorrect initialization ordering. I can see this now.
In `usb.c`, within `nfcmrvl_probe`, the `nfcmrvl_nci_register_dev` function is called to register the NCI device, which returns a pointer to a `nfcmrvl_private` structure. However, the `drv_data->priv` field is assigned *after* `nfcmrvl_nci_register_dev` is called. Critically, during this time, other threads, such as those spawned by `nfcmrvl_usb_nci_open` are executing, and those threads are submitting URBs, which call `nfcmrvl_bulk_complete`. In `nfcmrvl_bulk_complete`, code is accessing `drv_data->priv`, when the value is not yet assigned.
Furthermore, `usb_set_intfdata(intf, drv_data)` is also called *after* `nfcmrvl_nci_register_dev`, creating a potential vulnerability if `nfcmrvl_suspend`, `nfcmrvl_resume`, or `nfcmrvl_disconnect` is called, potentially dereferencing a NULL `drv_data` pointer.
The examination of `spi.c` and `i2c.c` reveals similar flaws, with `devm_request_threaded_irq` being called *before* critical driver data structures are fully initialized. In `spi.c` specifically, this can result in the interrupt handler `nfcmrvl_spi_int_irq_thread_fn` accessing uninitialized pointers, like `drv_data->nci_spi`, or even `drv_data->priv`, which can lead to crashes. The inclusion of defensive sentinel guards like `if (!drv_data->priv)` is a band-aid solution, masking the underlying issue of incorrect initialization order.
The proposed patch in the diff does introduce `if (!drv_data->priv)` in `spi.c` and in multiple places in `usb.c`. This is a clear example of the anti-pattern identified in the architectural design guidelines - a defensive guard or sentinel which simply hides the underlying error. The correct approach, as specified in the guidelines, involves shifting the `request_irq`, `timer_setup`, or `napi_enable` calls to the *end* of the initialization process, ensuring that all state structures are fully initialized. It appears that the patch author recognized an issue, based on observing similar bad code in `i2c.c`, but did not understand the correct solution. They didn't solve the underlying problem.
In `usb.c`, the patch changes `nfcmrvl_bulk_complete` from `drv_data->priv` to `priv` and `nfcmrvl_submit_bulk_urb` calls to pass the `priv` variable. This may be the correct fix, to pass `priv` rather than `drv_data` at all stages. The patch did some good, but not all of the good. The root cause of the crash in `nfcmrvl_bulk_complete` is now understood, but the changes in the patch are not the only location of similar errors in `nfcmrvl`.
The `nfcmrvl_probe` function is assigning `drv_data->priv` after `nfcmrvl_nci_register_dev` is called. `nfcmrvl_nci_register_dev` returns the pointer `priv`. However, if `nfcmrvl_nci_register_dev` returns an error, the error path within `nfcmrvl_probe` doesn't handle the need to set `usb_set_intfdata(intf, NULL)`, which could result in a subsequent use-after-free scenario. This will require further investigation.
I need to dig deeper into the historical code, by using `git log` and see how
|
| 227/5 |
2026/08/26 15:10 |
tool |
git-log |
0m
Args: map[Count:10 PathPrefix:drivers/nfc/nfcmrvl]
Results: map[Output:567ab7727adb nfc: Use named initializers for struct i2c_device_id
dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
c49a9eb650d5 NFC: nfcmrvl: Replace strcpy() with strscpy()
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
41cb08555c41 treewide, timers: Rename from_timer() to timer_container_of()
8fa7292fee5c treewide: Switch/rename to timer_delete[_sync]()
a26892ee1297 nfc: mrvl: Don't use "proxy" headers
5f60d5f6bbc1 move asm/unaligned.h to linux/unaligned.h
]
|
| 228/5 |
2026/08/26 15:10 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 48954
output: 41
thoughts: 22
Thoughts: Here's my summary, as though I'm thinking through this process:
**Diving into the NFC/MRVL Driver History**
Okay, so I need to get a handle on the evolution of this `drivers/nfc/nfcmrvl` code. First thing I'm going to do is examine the initial commits, or maybe even dig into the entire commit history for this section. That should tell the story of how it was brought into the project and any major architectural decisions that were made early on. I'm looking for the key milestones - when it was introduced, significant refactors, bug fixes, or performance optimizations. Understanding the chronology is crucial. I want to see the evolution of the feature set, the API changes, and any potential integration challenges that were encountered along the way.
|
| 229/5 |
2026/08/26 15:10 |
tool |
git-log |
0m
Args: map[Count:10 MessageRegexps:[nfcmrvl] PathPrefix:drivers/nfc/nfcmrvl]
Results: map[Output:dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
c49a9eb650d5 NFC: nfcmrvl: Replace strcpy() with strscpy()
053fdaa841bd nfc: mrvl: Move platform_data struct into driver
e204ead35401 nfc: nfcmrvl: Fix potential memory leak in nfcmrvl_i2c_nci_send()
93d904a734a7 nfc: nfcmrvl: Fix potential memory leak in nfcmrvl_i2c_nci_send()
5a478a653b4c nfc: nfcmrvl: Fix irq_of_parse_and_map() return value
8a4d480702b7 nfc: nfcmrvl: Fix memory leak in nfcmrvl_play_deferred
d270453a0d9e nfc: nfcmrvl: main: reorder destructive operations in nfcmrvl_nci_unregister_dev to avoid bugs
3833b87408e5 nfc: mrvl: correct nfcmrvl_spi_parse_dt() device_node argument
]
|
| 230/5 |
2026/08/26 15:10 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 49518
output: 30
thoughts: 37
Thoughts: Okay, here's my take on those thoughts, framed as an expert in the field:
**Digging into NFC USB/NCI Driver Registration**
Right, so I need to understand how the NFC USB and NCI drivers are managing their device registrations. The usual suspect for handling this kind of process within a Linux kernel context is going to be `nci_register_device` function, so that's the starting point. I need to get a clear picture of the flow.
Time to cut through the noise and get to the relevant code. I'll search across the `drivers/nfc/` directory for any instances of `nci_register_device`. Hopefully, a code search will turn up concrete examples I can analyze. I want to see how other drivers in the NFC ecosystem are handling device registration to get some ideas and see if there are better solutions to my problem. The goal is to identify common patterns, potential pitfalls, and best practices. From there, I can figure out the best approach.
|
| 231/5 |
2026/08/26 15:10 |
tool |
grepper |
0m
Args: map[Expression:nci_register_device PathPrefix:drivers/nfc]
Results: map[Output:drivers/nfc/fdp/fdp.c=699=int fdp_nci_probe(struct fdp_i2c_phy *phy, const struct nfc_phy_ops *phy_ops,
--
drivers/nfc/fdp/fdp.c-737-
drivers/nfc/fdp/fdp.c:738: r = nci_register_device(ndev);
drivers/nfc/fdp/fdp.c-739- if (r)
--
drivers/nfc/nfcmrvl/main.c=92=struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
--
drivers/nfc/nfcmrvl/main.c-156-
drivers/nfc/nfcmrvl/main.c:157: rc = nci_register_device(priv->ndev);
drivers/nfc/nfcmrvl/main.c-158- if (rc) {
drivers/nfc/nfcmrvl/main.c:159: nfc_err(dev, "nci_register_device failed %d\n", rc);
drivers/nfc/nfcmrvl/main.c-160- goto error_fw_dnld_deinit;
--
drivers/nfc/nxp-nci/core.c=135=int nxp_nci_probe(void *phy_id, struct device *pdev,
--
drivers/nfc/nxp-nci/core.c-169- nci_set_drvdata(info->ndev, info);
drivers/nfc/nxp-nci/core.c:170: r = nci_register_device(info->ndev);
drivers/nfc/nxp-nci/core.c-171- if (r < 0) {
--
drivers/nfc/s3fwrn5/core.c=160=int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
--
drivers/nfc/s3fwrn5/core.c-184-
drivers/nfc/s3fwrn5/core.c:185: ret = nci_register_device(info->ndev);
drivers/nfc/s3fwrn5/core.c-186- if (ret < 0) {
--
drivers/nfc/st-nci/core.c=112=int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
--
drivers/nfc/st-nci/core.c-148-
drivers/nfc/st-nci/core.c:149: r = nci_register_device(ndlc->ndev);
drivers/nfc/st-nci/core.c-150- if (r) {
--
drivers/nfc/virtual_ncidev.c=133=static int virtual_ncidev_open(struct inode *inode, struct file *file)
--
drivers/nfc/virtual_ncidev.c-152-
drivers/nfc/virtual_ncidev.c:153: ret = nci_register_device(vdev->ndev);
drivers/nfc/virtual_ncidev.c-154- if (ret < 0) {
]
|
| 232/5 |
2026/08/26 15:10 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 50667
output: 43
thoughts: 1941
Thoughts: **Analyzing the Device Registration and Data Structures in nfcmrvl**
Okay, let's break this down. The core issue seems to be a race condition related to device registration and the initialization of crucial data structures, specifically `drv_data` and `priv`, within the `nfcmrvl` driver. It appears that `nfcmrvl_nci_register_dev` registers the device before returning the `priv` pointer, and this leads to a situation where userspace can open the device before certain data fields are properly initialized.
My initial thought was that `nfcmrvl_nci_register_dev` was returning `priv` too early. I began tracing the execution flow, specifically the sequence of calls when a user space application opens the device, and I could see that the `nfcmrvl_probe` function allocates `drv_data`, initializes locks, and then calls `nfcmrvl_nci_register_dev`. This latter function allocates `priv`, sets up the necessary links between `priv` and `drv_data`, allocates an `ndev`, and registers it using `nci_register_device()`.
The crucial point is that during or right after this registration, a userspace application opens the device, which triggers `nci_dev_up` followed by `nci_open_device` and ultimately `nfcmrvl_nci_open`. Within `nfcmrvl_nci_open`, the driver retrieves the `priv` pointer using `nci_get_drvdata(ndev)`, sets a flag, and then calls the `nci_open` function associated with the particular transport (USB, SPI, etc.). For the USB case, this is `nfcmrvl_usb_nci_open`.
The problem occurs when an URB completes because `urb->context` is passed as `drv_data`, but `nfcmrvl_bulk_complete` then attempts to access `drv_data->priv` which can be NULL. This is because, in the unpatched code, the `priv` pointer within `drv_data` is not set *before* device registration. My analysis reveals that `drv_data` is allocated in `nfcmrvl_probe`, while `priv` is allocated and assigned within `nfcmrvl_nci_register_dev`. The critical flaw lies in the sequence: `nci_register_device` is called before `drv_data->priv` is fully initialized in the calling functions (`usb.c`, `spi.c`, `i2c.c`).
I initially questioned the need for `drv_data->priv` at all, but I then traced the ownership of data structures. The relationship between `drv_data` and `priv` seems to be a back-pointer relationship: `priv->drv_data = drv_data` and `drv_data->priv = priv`. Each transport has a different `drv_data` struct (USB, SPI, I2C, UART), but only USB, SPI and I2C have this `priv` backpointer, UART has no `priv` pointer, and instead uses `nu`.
My attention then focused on how the `drv_data->priv` pointer is initialized in the various transport-specific probe functions, specifically in `usb.c`, `spi.c`, and `i2c.c`. In all of these cases, `drv_data->priv` is assigned the *return value* of `nfcmrvl_nci_register_dev()`. I realized that `nfcmrvl_nci_register_dev` registers the device *before* it returns, which exposes the device before `drv_data->priv` is set. Further inspection of those probe functions shows that the driver sets `drv_data->priv->support_fw_dnld` after registration, as well as performs other crucial initialization steps.
The implications are serious: If `nfcmrvl_nci_register_dev` registers the device *before* the caller finishes setting up `drv_data` and assigning `drv_data->priv`, then it is possible for userspace access to occur while `drv_data` and therefore `drv_data->priv` is not fully initialized, leading to the NULL pointer dereference in the `nfcmrvl_bulk_complete` function. The sequence is the problem.
|
| 233/5 |
2026/08/26 15:10 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/fw_dnld.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC driver: Firmware downloader
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/unaligned.h>
10: #include <linux/firmware.h>
11: #include <linux/nfc.h>
12: #include <net/nfc/nci.h>
13: #include <net/nfc/nci_core.h>
14: #include "nfcmrvl.h"
15:
16: #define FW_DNLD_TIMEOUT 15000
17:
18: #define NCI_OP_PROPRIETARY_BOOT_CMD nci_opcode_pack(NCI_GID_PROPRIETARY, \
19: NCI_OP_PROP_BOOT_CMD)
20:
21: /* FW download states */
22:
23: enum {
24: STATE_RESET = 0,
25: STATE_INIT,
26: STATE_SET_REF_CLOCK,
27: STATE_SET_HI_CONFIG,
28: STATE_OPEN_LC,
29: STATE_FW_DNLD,
30: STATE_CLOSE_LC,
31: STATE_BOOT
32: };
33:
34: enum {
35: SUBSTATE_WAIT_COMMAND = 0,
36: SUBSTATE_WAIT_ACK_CREDIT,
37: SUBSTATE_WAIT_NACK_CREDIT,
38: SUBSTATE_WAIT_DATA_CREDIT,
39: };
40:
41: /*
42: * Patterns for responses
43: */
44:
45: static const uint8_t nci_pattern_core_reset_ntf[] = {
46: 0x60, 0x00, 0x02, 0xA0, 0x01
47: };
48:
49: static const uint8_t nci_pattern_core_init_rsp[] = {
50: 0x40, 0x01, 0x11
51: };
52:
53: static const uint8_t nci_pattern_core_set_config_rsp[] = {
54: 0x40, 0x02, 0x02, 0x00, 0x00
55: };
56:
57: static const uint8_t nci_pattern_core_conn_create_rsp[] = {
58: 0x40, 0x04, 0x04, 0x00
59: };
60:
61: static const uint8_t nci_pattern_core_conn_close_rsp[] = {
62: 0x40, 0x05, 0x01, 0x00
63: };
64:
65: static const uint8_t nci_pattern_core_conn_credits_ntf[] = {
66: 0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01
67: };
68:
69: static const uint8_t nci_pattern_proprietary_boot_rsp[] = {
70: 0x4F, 0x3A, 0x01, 0x00
71: };
72:
73: static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen)
74: {
75: struct sk_buff *skb;
76: struct nci_data_hdr *hdr;
77:
78: skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL);
79: if (!skb)
80: return NULL;
81:
82: hdr = skb_put(skb, NCI_DATA_HDR_SIZE);
83: hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
84: hdr->rfu = 0;
85: hdr->plen = plen;
86:
87: nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
88: nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
89:
90: return skb;
91: }
92:
93: static void fw_dnld_over(struct nfcmrvl_private *priv, u32 error)
94: {
95: if (priv->fw_dnld.fw) {
96: release_firmware(priv->fw_dnld.fw);
97: priv->fw_dnld.fw = NULL;
98: priv->fw_dnld.header = NULL;
99: priv->fw_dnld.binary_config = NULL;
100: }
]
|
| 234/5 |
2026/08/26 15:10 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 55084
output: 42
thoughts: 10273
Thoughts: **Analysis of the Proposed Patch**
Okay, here's the breakdown of my thinking on this patch. I'm seeing a few key areas that need careful consideration, particularly concerning potential race conditions, adhering to architectural guidelines, and whether the proposed changes are truly addressing the reported crash.
First, let's look at the `spi.c` change. The patch introduces a `if (!drv_data->priv) return IRQ_HANDLED;` check inside the interrupt handler `nfcmrvl_spi_int_irq_thread_fn`. This is a big red flag. It's a classic "band-aid" fix, as described in the architectural guidelines. The problem is that the interrupt handler *might* fire before the driver is fully initialized. I see that this check is copied from `i2c.c`, which previously used the same pattern. However, this is an anti-pattern. The correct approach would be to ensure the interrupt is only requested *after* all necessary structures are initialized (like the `nci_spi` pointer). Furthermore, the `spi.c` code changes are completely unrelated to the USB crash report, and adding the sentinel guard is a text-book violation of "Caller vs. Callee Responsibility" principles. This change, while seemingly innocuous, has the potential to mask a deeper issue.
Moving on to `usb.c`, the patch modifies `nfcmrvl_bulk_complete` and `nfcmrvl_submit_bulk_urb`. The main change is changing `urb->context` from `drv_data` to `priv`. This seems to fix a fundamental confusion: previously, the code was mistakenly using `drv_data->flags` to check the `NFCMRVL_NCI_RUNNING` bit, which should have been on `priv->flags`. This is a bug fix that addresses a mis-match of state flags. It also simplifies access to `priv->drv_data`. The change also shifts the `urb->context` to `priv`, which then makes the code semantically correct.
Another change is in `nfcmrvl_resume()`:
The original version had a potential race condition. However, this has been properly addressed by changing the flag to `priv->flags` instead of `drv_data->flags`.
The critical question now is whether the proposed change to `usb.c` truly addresses the root cause of the "general protection fault in nfcmrvl_bulk_complete". With the use of `priv` passed through the URB context and `test_bit(NFCMRVL_NCI_RUNNING)` checks, there are no obvious race conditions.
Overall, the patch has several issues. The `spi.c` part is a clear architectural violation, introducing a band-aid fix instead of a proper initialization fix, and is unrelated to the crash report. The changes in `usb.c` appear to fix a flag mis-match bug, and the change from `drv_data` to `priv` in URB context is the semantically correct.
Given the potential for deeper initialization-related issues in `spi.c` that are not addressed by this patch, it's not a complete fix and fails a basic architectural test.
|
| 235/5 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:200 LineCount:100]
Results: map[Contents: 200: int err;
201:
202: cancel_work_sync(&drv_data->waker);
203:
204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
205:
206: nfcmrvl_usb_stop_traffic(drv_data);
207: usb_kill_anchored_urbs(&drv_data->tx_anchor);
208: err = usb_autopm_get_interface(drv_data->intf);
209: if (err)
210: goto failed;
211:
212: drv_data->intf->needs_remote_wakeup = 0;
213: usb_autopm_put_interface(drv_data->intf);
214:
215: failed:
216: usb_scuttle_anchored_urbs(&drv_data->deferred);
217: return 0;
218: }
219:
220: static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
221: struct sk_buff *skb)
222: {
223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
224: struct urb *urb;
225: unsigned int pipe;
226: int err;
227:
228: if (!drv_data->bulk_tx_ep)
229: return -ENODEV;
230:
231: urb = usb_alloc_urb(0, GFP_ATOMIC);
232: if (!urb)
233: return -ENOMEM;
234:
235: pipe = usb_sndbulkpipe(drv_data->udev,
236: drv_data->bulk_tx_ep->bEndpointAddress);
237:
238: usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
239: nfcmrvl_tx_complete, skb);
240:
241: err = nfcmrvl_inc_tx(drv_data);
242: if (err) {
243: usb_anchor_urb(urb, &drv_data->deferred);
244: schedule_work(&drv_data->waker);
245: err = 0;
246: goto done;
247: }
248:
249: usb_anchor_urb(urb, &drv_data->tx_anchor);
250:
251: err = usb_submit_urb(urb, GFP_ATOMIC);
252: if (err) {
253: if (err != -EPERM && err != -ENODEV)
254: nfc_err(&drv_data->udev->dev,
255: "urb %p submission failed (%d)\n", urb, -err);
256: kfree(urb->setup_packet);
257: usb_unanchor_urb(urb);
258: } else {
259: usb_mark_last_busy(drv_data->udev);
260: }
261:
262: done:
263: usb_free_urb(urb);
264: return err;
265: }
266:
267: static const struct nfcmrvl_if_ops usb_ops = {
268: .nci_open = nfcmrvl_usb_nci_open,
269: .nci_close = nfcmrvl_usb_nci_close,
270: .nci_send = nfcmrvl_usb_nci_send,
271: };
272:
273: static void nfcmrvl_waker(struct work_struct *work)
274: {
275: struct nfcmrvl_usb_drv_data *drv_data =
276: container_of(work, struct nfcmrvl_usb_drv_data, waker);
277: int err;
278:
279: err = usb_autopm_get_interface(drv_data->intf);
280: if (err)
281: return;
282:
283: usb_autopm_put_interface(drv_data->intf);
284: }
285:
286: static int nfcmrvl_probe(struct usb_interface *intf,
287: const struct usb_device_id *id)
288: {
289: struct nfcmrvl_usb_drv_data *drv_data;
290: struct nfcmrvl_private *priv;
291: struct usb_device *udev = interface_to_usbdev(intf);
292: struct nfcmrvl_platform_data config;
293: int ret;
294:
295: /* No configuration for USB */
296: memset(&config, 0, sizeof(config));
297: config.reset_gpio = NULL;
298:
299: nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
]
|
| 236/5 |
2026/08/26 15:11 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 67623
output: 236
thoughts: 2862
Thoughts: **Review of USB and SPI Patch for Potential Architectural Violations**
Okay, here's the breakdown of this patch and my assessment, considering the crash report, root cause, and architectural guidelines:
My initial thought is to examine the `nfcmrvl_usb_nci_send` function, and how `skb->dev` is being assigned. Then, I need to ensure that the code is accessing `priv` and `drv_data` in a safe and correct manner, which is crucial for handling the crash report.
It seems the primary goal is to fix a crash within the USB bulk URB completion handler by addressing flag mismanagement, especially concerning the `NFCMRVL_NCI_RUNNING` bit and its potential collision between the `priv` and `drv_data` structures. The patch aims to pass `priv` as `urb->context` instead of `drv_data`, which seems architecturally sound, as it simplifies the access patterns and avoids the potential race conditions with state initialization.
The critical issue is the addition of `if (!drv_data->priv) return IRQ_HANDLED;` in `spi.c`. This is a textbook example of a *defensive sentinel guard* anti-pattern, violating the *Core Philosophy: Band-Aid vs. Architectural Design* guideline and *The Sentinel Test* in the *Architectural Review Checklist*. This guard doesn't *fix* the fundamental issue, which is the order of initialization in `nfcmrvl_spi_probe()`. Specifically, the IRQ is requested *before* `nfcmrvl_nci_register_dev()`, resource allocation, and completion initialization, which means that the `drv_data->priv` could be NULL when the IRQ fires. Even with the check, the handler may still dereference uninitialized resources, making the patch incomplete. The correct architectural solution is to adhere to the *Deterministic Decision Trigger Matrix*, specifically: "Move `request_irq()` ... strictly to the end of setup after all state structures are fully initialized." This is a violation of the Architectural Design & Maintainer Taste guidelines.
Regarding the USB code itself, after the initial flag-fix, it seems the patch is better architected, avoiding direct use of `drv_data->priv` in the bulk URB completion callback, and correctly managing the `priv` NCI device instance as the context.
However, the lingering use of `drv_data->priv` in `nfcmrvl_probe`, `nfcmrvl_disconnect` (via `usb_get_intfdata`), and `nfcmrvl_resume` in `usb.c` introduces needless complexity and creates a point of potential contention, especially during teardown. This can be cleaned up, but it is not directly related to the fix.
Based on this analysis:
```json
{
"ReviewApproved": false,
"ReviewComments": [
"The added `if (!drv_data->priv) return IRQ_HANDLED;` in `spi.c` is a defensive sentinel guard and violates the \"Core Philosophy: Band-Aid vs. Architectural Design\" guideline. The correct architectural fix is to ensure the IRQ is requested/enabled only *after* all related resources are properly initialized, including calls to `nfcmrvl_nci_register_dev()`, resource allocation, and completion setup, as per the \"Deterministic Decision Trigger Matrix\" and kernel design principles. This also violates The Sentinel Test.",
"While the USB changes are an improvement, the use of `drv_data->priv` can be further cleaned up and simplified in `nfcmrvl_probe`, `nfcmrvl_disconnect`, and `nfcmrvl_resume` for improved readability and maintainability."
]
}
```
|
| 237/5 |
2026/08/26 15:11 |
tool |
set-results |
0m
Args: map[ReviewApproved:false ReviewComments:[Defensive sentinel guard in drivers/nfc/nfcmrvl/spi.c: Adding 'if (!drv_data->priv) return IRQ_HANDLED;' inside nfcmrvl_spi_int_irq_thread_fn() is an ad-hoc sentinel band-aid that masks improper initialization order in nfcmrvl_spi_probe(). Moreover, it is incomplete and fragile because drv_data->nci_spi and drv_data->handshake_completion are also not initialized until after probe progresses. The IRQ handler should not be burdened with defensive guards for uninitialized driver state. Lifecycle ordering in SPI probe: If addressing the SPI driver lifecycle, the IRQ registration (devm_request_threaded_irq) must be performed after all driver structures (drv_data->priv, drv_data->nci_spi, and drv_data->handshake_completion) are fully allocated and initialized. Otherwise, keep the patch focused strictly on the USB PHY bug.]]
Results: map[ReviewApproved:false ReviewComments:[Defensive sentinel guard in drivers/nfc/nfcmrvl/spi.c: Adding 'if (!drv_data->priv) return IRQ_HANDLED;' inside nfcmrvl_spi_int_irq_thread_fn() is an ad-hoc sentinel band-aid that masks improper initialization order in nfcmrvl_spi_probe(). Moreover, it is incomplete and fragile because drv_data->nci_spi and drv_data->handshake_completion are also not initialized until after probe progresses. The IRQ handler should not be burdened with defensive guards for uninitialized driver state. Lifecycle ordering in SPI probe: If addressing the SPI driver lifecycle, the IRQ registration (devm_request_threaded_irq) must be performed after all driver structures (drv_data->priv, drv_data->nci_spi, and drv_data->handshake_completion) are fully allocated and initialized. Otherwise, keep the patch focused strictly on the USB PHY bug.]]
|
| 238/4 |
2026/08/26 15:11 |
action |
evaluate-patch-review |
0m
Results: map[NeedsIteration:true ReviewFeedback:- Defensive sentinel guard in drivers/nfc/nfcmrvl/spi.c: Adding 'if (!drv_data->priv) return IRQ_HANDLED;' inside nfcmrvl_spi_int_irq_thread_fn() is an ad-hoc sentinel band-aid that masks improper initialization order in nfcmrvl_spi_probe(). Moreover, it is incomplete and fragile because drv_data->nci_spi and drv_data->handshake_completion are also not initialized until after probe progresses. The IRQ handler should not be burdened with defensive guards for uninitialized driver state.
- Lifecycle ordering in SPI probe: If addressing the SPI driver lifecycle, the IRQ registration (devm_request_threaded_irq) must be performed after all driver structures (drv_data->priv, drv_data->nci_spi, and drv_data->handshake_completion) are fully allocated and initialized. Otherwise, keep the patch focused strictly on the USB PHY bug.]
|
| 239/2 |
2026/08/26 15:11 |
iteration |
1 |
10m
|
| 240/3 |
2026/08/26 15:11 |
agent |
patch-generator |
1m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The explanation of the root cause of the bug is:
The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
Historical context on how similar bugs were fixed in the past:
Based on the historical context and similar bugs in the NFC subsystem, the root cause of this bug is a classic **"register before initialize"** race condition. The device is exposed to userspace (via `nci_register_device()`) before its driver data structures are fully initialized (`drv_data->priv = priv`).
Here is a summary of how similar bugs were addressed in the past and the idioms that should be followed for the fix:
### 1. Reordering Initialization vs. Passing Valid Context
In the past, similar race conditions in the NFC subsystem were fixed by strictly reordering the initialization sequence so that the device or IRQ is only registered *after* all data structures are fully set up.
* For example, in commit `32ecc75ded72` ("NFC: pn533: change order operations in dev registation"), a NULL pointer dereference in `pn533_i2c` was fixed by moving `request_threaded_irq()` to happen *after* `phy->priv` was assigned.
* Similarly, commit `d270453a0d9e` ("nfc: nfcmrvl: main: reorder destructive operations...") fixed race conditions during device unregistration by strictly reordering operations relative to `nci_unregister_device()`.
However, in `nfcmrvl/usb.c`, reordering is difficult because `nci_register_device()` is called deep inside `nfcmrvl_nci_register_dev()`, which allocates and returns `priv`. Therefore, the proposed fix of **passing the fully initialized `priv` structure as the URB context** (instead of the partially initialized `drv_data`) is the correct and robust idiom here. It ensures the asynchronous callback (`nfcmrvl_bulk_complete`) always operates on valid data, completely bypassing the race window.
### 2. Early IRQ Handler Checks (SPI/I2C)
For interfaces where hardware interrupts might fire before the driver is fully bound (like SPI or I2C), the established pattern in this subsystem is to add an early return check at the top of the IRQ handler.
* If you look at `drivers/nfc/nfcmrvl/i2c.c`, this exact pattern is already implemented in `nfcmrvl_i2c_int_irq_thread_fn()`:
```c
if (!drv_data->priv)
return IRQ_HANDLED;
```
* As noted in the bug explanation, applying this exact same check to `nfcmrvl_spi_int_irq_thread_fn()` in `drivers/nfc/nfcmrvl/spi.c` is the correct historical idiom to prevent the identical race condition on the SPI PHY.
### 3. Fixing the Macro Collision Typo
The accidental check of `NFCMRVL_NCI_RUNNING` against `drv_data->flags` (instead of `priv->flags`) is a logic error that masked the bug on the first URB. Fixing this typo to correctly check `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)` is necessary and aligns with how the flags were originally intended to be separated between the PHY layer (`drv_data->flags`) and the core layer (`priv->flags`).
### Conclusion
The proposed fix strategy is perfectly aligned with prior art in the subsystem. You should proceed with:
1. Changing `nfcmrvl_submit_bulk_urb()` to take `priv` and pass it as the URB context to `usb_fill_bulk_urb()`.
2. Retrieving `priv` from `urb->context` in `nfcmrvl_bulk_complete()`.
3. Fixing the `NFCMRVL_NCI_RUNNING` typo to check `priv->flags`.
4. Adding the `if (!drv_data->priv) return IRQ_HANDLED;` safeguard to the SPI IRQ handler, mirroring the existing code in `i2c.c`.
Another developer tried to fix this bug with the following strategy:
### Root Cause Analysis
The crash occurs due to a race condition during device registration in `nfcmrvl_probe()`, compounded by a macro collision typo in flag checking:
1. **Race Window during Initialization**:
In `nfcmrvl_probe()` (`drivers/nfc/nfcmrvl/usb.c`), the driver calls `nfcmrvl_nci_register_dev()`, which creates the `struct nfcmrvl_private` object, sets up device driver data, and calls `nci_register_device()`. Registering with NCI immediately exposes the device to userspace via Netlink.
However, `drv_data->priv = priv;` in `nfcmrvl_probe()` is executed only *after* `nfcmrvl_nci_register_dev()` returns. If userspace brings the interface up in between, `nfcmrvl_usb_nci_open()` submits bulk URBs with `drv_data` as the URB context. When an URB completes before `drv_data->priv` is assigned, `nfcmrvl_bulk_complete()` dereferences `drv_data->priv` (`NULL`), leading to a NULL pointer dereference.
2. **Flag Typo Masking the First URB Completion**:
In `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()`, the check:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
checked `drv_data->flags` instead of `priv->flags`. Because `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` are both defined as bit `1`, this erroneously tested whether `NFCMRVL_USB_BULK_RUNNING` was set on `drv_data->flags`. When opening the device, the first URB was submitted before setting `NFCMRVL_USB_BULK_RUNNING`, so the first URB completion exited early, masking the bug until the second URB was submitted.
3. **Similar Issue in SPI Driver**:
In `drivers/nfc/nfcmrvl/spi.c`, `devm_request_threaded_irq()` is called before `drv_data->priv` is assigned by `nfcmrvl_nci_register_dev()`. Unlike `drivers/nfc/nfcmrvl/i2c.c`, `nfcmrvl_spi_int_irq_thread_fn()` lacked a check for `!drv_data->priv` and could crash if an interrupt fired during registration.
---
### Solution Details
1. **Pass `priv` as URB Context**:
Modified `nfcmrvl_submit_bulk_urb()` in `drivers/nfc/nfcmrvl/usb.c` to accept `struct nfcmrvl_private *priv` rather than `drv_data`. The URB context is set to `priv`, ensuring that the completion callback `nfcmrvl_bulk_complete()` always receives the fully initialized `priv` pointer directly from `urb->context` without depending on `drv_data->priv`.
2. **Fix `NFCMRVL_NCI_RUNNING` Flag Checks**:
- In `nfcmrvl_bulk_complete()`, updated the check to `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`.
- In `nfcmrvl_resume()`, updated the check to verify `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`.
3. **Add Early Check in SPI IRQ Handler**:
In `drivers/nfc/nfcmrvl/spi.c`, added `if (!drv_data->priv) return IRQ_HANDLED;` to `nfcmrvl_spi_int_irq_thread_fn()`, mirroring the implementation in `drivers/nfc/nfcmrvl/i2c.c`.
and the following patch:
diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
index 9c8cde125..f3e0ae6ee 100644
--- a/drivers/nfc/nfcmrvl/spi.c
+++ b/drivers/nfc/nfcmrvl/spi.c
@@ -29,6 +29,9 @@ static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
struct sk_buff *skb;
+ if (!drv_data->priv)
+ return IRQ_HANDLED;
+
/*
* Special case where we are waiting for SPI_INT deassertion to start a
* transfer.
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);
However, a senior maintainer reviewed the patch and requested architectural/design revisions:
- Defensive sentinel guard in drivers/nfc/nfcmrvl/spi.c: Adding 'if (!drv_data->priv) return IRQ_HANDLED;' inside nfcmrvl_spi_int_irq_thread_fn() is an ad-hoc sentinel band-aid that masks improper initialization order in nfcmrvl_spi_probe(). Moreover, it is incomplete and fragile because drv_data->nci_spi and drv_data->handshake_completion are also not initialized until after probe progresses. The IRQ handler should not be burdened with defensive guards for uninitialized driver state.
- Lifecycle ordering in SPI probe: If addressing the SPI driver lifecycle, the IRQ registration (devm_request_threaded_irq) must be performed after all driver structures (drv_data->priv, drv_data->nci_spi, and drv_data->handshake_completion) are fully allocated and initialized. Otherwise, keep the patch focused strictly on the USB PHY bug.
Revise the patch to eliminate these architectural flaws, satisfy maintainer invariants,
and address all reviewer feedback.
Note: the source tree does not contain the patch yet (so you need to recreate the complete,
revised patch from scratch using the codeeditor tool).
Reply: ### Cause of the Bug
The crash occurs due to an initialization race condition in `drivers/nfc/nfcmrvl/usb.c`:
1. **Race Condition (`NULL` pointer dereference)**:
In `nfcmrvl_probe()`, the driver calls `nfcmrvl_nci_register_dev()`, which allocates the core `struct nfcmrvl_private` (`priv`) structure and registers the device with NCI via `nci_register_device()`. Calling `nci_register_device()` immediately exposes the NFC interface to userspace.
However, `nfcmrvl_probe()` assigns `drv_data->priv = priv` **after** `nfcmrvl_nci_register_dev()` returns. If userspace brings up the interface immediately (via Netlink `nfc_dev_up`), `nfcmrvl_usb_nci_open()` is executed. It submits bulk RX URBs passing `drv_data` as the URB context (`urb->context`). When an URB completes before `nfcmrvl_probe()` assigns `drv_data->priv`, `nfcmrvl_bulk_complete()` attempts to dereference `drv_data->priv` (`NULL`), causing a General Protection Fault / NULL pointer dereference.
2. **Flag Typo Masking Initial Completion**:
In `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()`, the driver tested `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)` instead of `&priv->flags`. Because `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` both share value `1`, the check inadvertently tested `drv_data->flags` (for `NFCMRVL_USB_BULK_RUNNING`), causing the first URB completion in `nfcmrvl_usb_nci_open()` to return early before `NFCMRVL_USB_BULK_RUNNING` was set, while crashing on subsequent completions.
---
### Solution
1. **Pass `priv` as URB Context**:
Changed `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` and set `priv` as `urb->context` in `usb_fill_bulk_urb()`. Since `priv` is already fully allocated and initialized when `nfcmrvl_usb_nci_open()` is invoked, `nfcmrvl_bulk_complete()` obtains `priv` directly from `urb->context`, eliminating any dependency on `drv_data->priv`.
2. **Fix `NFCMRVL_NCI_RUNNING` Flag Checks**:
Updated `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to check `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`.
3. **Keep Patch Focused**:
Avoided adding ad-hoc defensive sentinels in unrelated PHY drivers (such as `spi.c`), keeping the fix strictly focused and clean for the USB driver.
|
| 241/4 |
2026/08/26 15:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 14527
output: 166
thoughts: 328
Thoughts: **Analyzing the Reviewer's Feedback: A Path Forward**
Okay, let's break this down. The reviewer's feedback is pretty clear; my original fix for the issue in `drivers/nfc/nfcmrvl/spi.c` wasn't just a simple fix; it was a "sentinel band-aid" masking a deeper architectural problem. They are pointing out that adding a check like `if (!drv_data->priv) return IRQ_HANDLED;` inside `nfcmrvl_spi_int_irq_thread_fn()` is the wrong approach. It's masking an improper initialization order within `nfcmrvl_spi_probe()`.
The reviewer is saying I'm trying to defend the IRQ handler from an incomplete initialization state. Specifically, the structures `drv_data->priv`, `drv_data->nci_spi`, and `drv_data->handshake_completion` might not be ready when the interrupt fires. The IRQ handler *shouldn't* have to deal with these defensive guards.
They're offering two potential solutions, one is an architectural change that will require updating the order that resources are created.
The second solution is to limit the patch to the USB PHY bug, in which case the changes to protect the IRQ handler would be reverted. The preferred path is likely to address the lifecycle issues.
The key takeaway is that the `devm_request_threaded_irq` call (the IRQ registration) needs to happen *after* all those driver structures are fully allocated and initialized. That's the core of the problem.
Alright, I'll need to dig into the `drivers/nfc/nfcmrvl/` directory to understand the current initialization sequence using `read-file` or `codesearch-definition-source` to get a better handle on the existing code. Looks like I'll need to recreate the entire, revised patch from scratch, keeping in mind the maintainer's invariants. Time to dive in.
|
| 242/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-USB driver: USB interface related functions
4: *
5: * Copyright (C) 2014, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/usb.h>
10: #include <linux/nfc.h>
11: #include <net/nfc/nci.h>
12: #include <net/nfc/nci_core.h>
13: #include "nfcmrvl.h"
14:
15: static struct usb_device_id nfcmrvl_table[] = {
16: { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
17: USB_CLASS_VENDOR_SPEC, 4, 1) },
18: { } /* Terminating entry */
19: };
20:
21: MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
22:
23: #define NFCMRVL_USB_BULK_RUNNING 1
24: #define NFCMRVL_USB_SUSPENDING 2
25:
26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
30: struct work_struct waker;
31: struct usb_anchor tx_anchor;
32: struct usb_anchor bulk_anchor;
33: struct usb_anchor deferred;
34: int tx_in_flight;
35: /* protects tx_in_flight */
36: spinlock_t txlock;
37: struct usb_endpoint_descriptor *bulk_tx_ep;
38: struct usb_endpoint_descriptor *bulk_rx_ep;
39: int suspend_count;
40: struct nfcmrvl_private *priv;
41: };
42:
43: static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
44: {
45: unsigned long flags;
46: int rv;
47:
48: spin_lock_irqsave(&drv_data->txlock, flags);
49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
100: }
]
|
| 243/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:101 LineCount:100]
Results: map[Contents: 101:
102: static int
103: nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
104: {
105: struct urb *urb;
106: unsigned char *buf;
107: unsigned int pipe;
108: int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
109:
110: if (!drv_data->bulk_rx_ep)
111: return -ENODEV;
112:
113: urb = usb_alloc_urb(0, mem_flags);
114: if (!urb)
115: return -ENOMEM;
116:
117: buf = kmalloc(size, mem_flags);
118: if (!buf) {
119: usb_free_urb(urb);
120: return -ENOMEM;
121: }
122:
123: pipe = usb_rcvbulkpipe(drv_data->udev,
124: drv_data->bulk_rx_ep->bEndpointAddress);
125:
126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
127: nfcmrvl_bulk_complete, drv_data);
128:
129: urb->transfer_flags |= URB_FREE_BUFFER;
130:
131: usb_mark_last_busy(drv_data->udev);
132: usb_anchor_urb(urb, &drv_data->bulk_anchor);
133:
134: err = usb_submit_urb(urb, mem_flags);
135: if (err) {
136: if (err != -EPERM && err != -ENODEV)
137: nfc_err(&drv_data->udev->dev,
138: "urb %p submission failed (%d)\n", urb, -err);
139: usb_unanchor_urb(urb);
140: }
141:
142: usb_free_urb(urb);
143:
144: return err;
145: }
146:
147: static void nfcmrvl_tx_complete(struct urb *urb)
148: {
149: struct sk_buff *skb = urb->context;
150: struct nci_dev *ndev = (struct nci_dev *)skb->dev;
151: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
152: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
153: unsigned long flags;
154:
155: nfc_info(priv->dev, "urb %p status %d count %d\n",
156: urb, urb->status, urb->actual_length);
157:
158: spin_lock_irqsave(&drv_data->txlock, flags);
159: drv_data->tx_in_flight--;
160: spin_unlock_irqrestore(&drv_data->txlock, flags);
161:
162: kfree(urb->setup_packet);
163: kfree_skb(skb);
164: }
165:
166: static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
167: {
168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
169: int err;
170:
171: err = usb_autopm_get_interface(drv_data->intf);
172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
188: usb_autopm_put_interface(drv_data->intf);
189: return err;
190: }
191:
192: static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
193: {
194: usb_kill_anchored_urbs(&drv_data->bulk_anchor);
195: }
196:
197: static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
198: {
199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
200: int err;
]
|
| 244/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:201 LineCount:100]
Results: map[Contents: 201:
202: cancel_work_sync(&drv_data->waker);
203:
204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
205:
206: nfcmrvl_usb_stop_traffic(drv_data);
207: usb_kill_anchored_urbs(&drv_data->tx_anchor);
208: err = usb_autopm_get_interface(drv_data->intf);
209: if (err)
210: goto failed;
211:
212: drv_data->intf->needs_remote_wakeup = 0;
213: usb_autopm_put_interface(drv_data->intf);
214:
215: failed:
216: usb_scuttle_anchored_urbs(&drv_data->deferred);
217: return 0;
218: }
219:
220: static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
221: struct sk_buff *skb)
222: {
223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
224: struct urb *urb;
225: unsigned int pipe;
226: int err;
227:
228: if (!drv_data->bulk_tx_ep)
229: return -ENODEV;
230:
231: urb = usb_alloc_urb(0, GFP_ATOMIC);
232: if (!urb)
233: return -ENOMEM;
234:
235: pipe = usb_sndbulkpipe(drv_data->udev,
236: drv_data->bulk_tx_ep->bEndpointAddress);
237:
238: usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
239: nfcmrvl_tx_complete, skb);
240:
241: err = nfcmrvl_inc_tx(drv_data);
242: if (err) {
243: usb_anchor_urb(urb, &drv_data->deferred);
244: schedule_work(&drv_data->waker);
245: err = 0;
246: goto done;
247: }
248:
249: usb_anchor_urb(urb, &drv_data->tx_anchor);
250:
251: err = usb_submit_urb(urb, GFP_ATOMIC);
252: if (err) {
253: if (err != -EPERM && err != -ENODEV)
254: nfc_err(&drv_data->udev->dev,
255: "urb %p submission failed (%d)\n", urb, -err);
256: kfree(urb->setup_packet);
257: usb_unanchor_urb(urb);
258: } else {
259: usb_mark_last_busy(drv_data->udev);
260: }
261:
262: done:
263: usb_free_urb(urb);
264: return err;
265: }
266:
267: static const struct nfcmrvl_if_ops usb_ops = {
268: .nci_open = nfcmrvl_usb_nci_open,
269: .nci_close = nfcmrvl_usb_nci_close,
270: .nci_send = nfcmrvl_usb_nci_send,
271: };
272:
273: static void nfcmrvl_waker(struct work_struct *work)
274: {
275: struct nfcmrvl_usb_drv_data *drv_data =
276: container_of(work, struct nfcmrvl_usb_drv_data, waker);
277: int err;
278:
279: err = usb_autopm_get_interface(drv_data->intf);
280: if (err)
281: return;
282:
283: usb_autopm_put_interface(drv_data->intf);
284: }
285:
286: static int nfcmrvl_probe(struct usb_interface *intf,
287: const struct usb_device_id *id)
288: {
289: struct nfcmrvl_usb_drv_data *drv_data;
290: struct nfcmrvl_private *priv;
291: struct usb_device *udev = interface_to_usbdev(intf);
292: struct nfcmrvl_platform_data config;
293: int ret;
294:
295: /* No configuration for USB */
296: memset(&config, 0, sizeof(config));
297: config.reset_gpio = NULL;
298:
299: nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
300:
]
|
| 245/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:390 LineCount:100]
Results: map[Contents: 390: usb_free_urb(urb);
391: }
392:
393: /* Cleanup the rest deferred urbs. */
394: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
395: kfree(urb->setup_packet);
396: usb_free_urb(urb);
397: }
398: }
399:
400: static int nfcmrvl_resume(struct usb_interface *intf)
401: {
402: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
403: int err = 0;
404:
405: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
406:
407: if (--drv_data->suspend_count)
408: return 0;
409:
410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
411: goto done;
412:
413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
421: }
422:
423: spin_lock_irq(&drv_data->txlock);
424: nfcmrvl_play_deferred(drv_data);
425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
426: spin_unlock_irq(&drv_data->txlock);
427:
428: return 0;
429:
430: failed:
431: usb_scuttle_anchored_urbs(&drv_data->deferred);
432: done:
433: spin_lock_irq(&drv_data->txlock);
434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
435: spin_unlock_irq(&drv_data->txlock);
436:
437: return err;
438: }
439: #endif
440:
441: static struct usb_driver nfcmrvl_usb_driver = {
442: .name = "nfcmrvl",
443: .probe = nfcmrvl_probe,
444: .disconnect = nfcmrvl_disconnect,
445: #ifdef CONFIG_PM
446: .suspend = nfcmrvl_suspend,
447: .resume = nfcmrvl_resume,
448: .reset_resume = nfcmrvl_resume,
449: #endif
450: .id_table = nfcmrvl_table,
451: .supports_autosuspend = 1,
452: .disable_hub_initiated_lpm = 1,
453: .soft_unbind = 1,
454: };
455: module_usb_driver(nfcmrvl_usb_driver);
456:
457: MODULE_AUTHOR("Marvell International Ltd.");
458: MODULE_DESCRIPTION("Marvell NFC-over-USB driver");
459: MODULE_LICENSE("GPL v2");
]
|
| 246/4 |
2026/08/26 15:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21104
output: 42
thoughts: 43
Thoughts: Okay, let's dive into this.
**Examining the USB Driver's Probe and Functionality**
Right, I need to get a clear picture of how this NFC Marvell USB driver is initialized and functions within the kernel. I'll focus on lines 280 to 390 of `drivers/nfc/nfcmrvl/usb.c`. This section should contain the core probe function, likely `usb_probe()` or something similar, along with other essential routines directly involved in setting up and managing the USB communication. I'm especially interested in how it identifies the device, allocates resources, and registers itself with the USB subsystem. Also, I'll be looking for any hints about interrupt handling or other critical aspects of the data transfer process. I want to see if they're using a standard USB interface, or if they have custom control transfers. This area should also tell me if any device-specific quirks are handled here, especially concerning power management or any necessary vendor-specific initialization commands. I hope this section will show me the data structures being used.
|
| 247/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:280 LineCount:100]
Results: map[Contents: 280: if (err)
281: return;
282:
283: usb_autopm_put_interface(drv_data->intf);
284: }
285:
286: static int nfcmrvl_probe(struct usb_interface *intf,
287: const struct usb_device_id *id)
288: {
289: struct nfcmrvl_usb_drv_data *drv_data;
290: struct nfcmrvl_private *priv;
291: struct usb_device *udev = interface_to_usbdev(intf);
292: struct nfcmrvl_platform_data config;
293: int ret;
294:
295: /* No configuration for USB */
296: memset(&config, 0, sizeof(config));
297: config.reset_gpio = NULL;
298:
299: nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
300:
301: drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
302: if (!drv_data)
303: return -ENOMEM;
304:
305: ret = usb_find_common_endpoints(intf->cur_altsetting, &drv_data->bulk_rx_ep,
306: &drv_data->bulk_tx_ep, NULL, NULL);
307: if (ret)
308: return -ENODEV;
309:
310: drv_data->udev = udev;
311: drv_data->intf = intf;
312:
313: INIT_WORK(&drv_data->waker, nfcmrvl_waker);
314: spin_lock_init(&drv_data->txlock);
315:
316: init_usb_anchor(&drv_data->tx_anchor);
317: init_usb_anchor(&drv_data->bulk_anchor);
318: init_usb_anchor(&drv_data->deferred);
319:
320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
332:
333: static void nfcmrvl_disconnect(struct usb_interface *intf)
334: {
335: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
336:
337: if (!drv_data)
338: return;
339:
340: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
341:
342: nfcmrvl_nci_unregister_dev(drv_data->priv);
343:
344: usb_set_intfdata(drv_data->intf, NULL);
345: }
346:
347: #ifdef CONFIG_PM
348: static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
349: {
350: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
351:
352: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
353:
354: if (drv_data->suspend_count++)
355: return 0;
356:
357: spin_lock_irq(&drv_data->txlock);
358: if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
359: set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
360: spin_unlock_irq(&drv_data->txlock);
361: } else {
362: spin_unlock_irq(&drv_data->txlock);
363: drv_data->suspend_count--;
364: return -EBUSY;
365: }
366:
367: nfcmrvl_usb_stop_traffic(drv_data);
368: usb_kill_anchored_urbs(&drv_data->tx_anchor);
369:
370: return 0;
371: }
372:
373: static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
374: {
375: struct urb *urb;
376: int err;
377:
378: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
379: usb_anchor_urb(urb, &drv_data->tx_anchor);
]
|
| 248/4 |
2026/08/26 15:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 22991
output: 27
thoughts: 52
Thoughts: Here's my take on those files within the `drivers/nfc/nfcmrvl/` directory.
**Diving into the NFC Driver Codebase**
Okay, so I need to get a really solid grasp on this NFC driver. To understand how this thing *actually* functions, I'm going to start by pulling up a few key source files. Let's make sure I look at `spi.c`, `i2c.c`, and `uart.c` first. Those are the big ones. I'll need to see precisely how these communication protocols are handled because I assume the NFC controller is communicating with the host system via one (or more) of those interfaces. The details of the driver's underlying communication are crucial here. And, of course, I'll need to examine `main.c` to see how the whole thing bootstraps and where the entry points and initialization functions are. I expect to be able to follow the flow of control from system start to operational NFC functionality. And finally, the header, `nfcmrvl.h` will give me the structure definitions, and any essential configuration or API details of the underlying Marvell chip I am working with. From these, I should be able to get a pretty good mental model of how it all works. I'm hoping I can piece together the complete picture pretty quickly.
|
| 249/4 |
2026/08/26 15:11 |
tool |
codesearch-dir-index |
0m
Args: map[Dir:drivers/nfc/nfcmrvl]
Results: map[Files:[fw_dnld.c fw_dnld.h i2c.c main.c nfcmrvl.h spi.c uart.c usb.c] Subdirs:<nil>]
|
| 250/4 |
2026/08/26 15:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23428
output: 83
thoughts: 18
Thoughts: Here's a summary of my thought process:
**Diving into the Code**
Okay, time to get to work. My initial thought is to zero in on the core of this system. I'm going to start with the obvious: I need to thoroughly inspect both `nfcmrvl.h` and `main.c`. `nfcmrvl.h` probably holds the essential definitions and structures, the foundational elements for the NFC marvel functionality. Understanding that header is crucial. Then, `main.c` – well, that's where the program likely starts executing. That's the entry point, the main flow, and where I can gain insight into the high-level architecture. Analyzing these two files together should give me a solid grasp of how this whole thing hangs together, the interface, and the driver. I’m thinking top-down here; definitions and then the usage.
|
| 251/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:1 LineCount:100]
Results: map[Contents: 1: /* SPDX-License-Identifier: GPL-2.0-only */
2: /*
3: * Marvell NFC driver
4: *
5: * Copyright (C) 2014-2015, Marvell International Ltd.
6: */
7:
8: #ifndef _NFCMRVL_H_
9: #define _NFCMRVL_H_
10:
11: #include "fw_dnld.h"
12:
13: struct gpio_desc;
14:
15: /* Define private flags: */
16: #define NFCMRVL_NCI_RUNNING 1
17: #define NFCMRVL_PHY_ERROR 2
18:
19: #define NFCMRVL_EXT_COEX_ID 0xE0
20: #define NFCMRVL_NOT_ALLOWED_ID 0xE1
21: #define NFCMRVL_ACTIVE_ID 0xE2
22: #define NFCMRVL_EXT_COEX_ENABLE 1
23: #define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA
24: #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB
25: #define NFCMRVL_NCI_MAX_EVENT_SIZE 260
26:
27: /*
28: * NCI FW Parameters
29: */
30:
31: #define NFCMRVL_PB_BAIL_OUT 0x11
32: #define NFCMRVL_PROP_REF_CLOCK 0xF0
33: #define NFCMRVL_PROP_SET_HI_CONFIG 0xF1
34:
35: /*
36: * HCI defines
37: */
38:
39: #define NFCMRVL_HCI_EVENT_HEADER_SIZE 0x04
40: #define NFCMRVL_HCI_EVENT_CODE 0x04
41: #define NFCMRVL_HCI_NFC_EVENT_CODE 0xFF
42: #define NFCMRVL_HCI_COMMAND_CODE 0x01
43: #define NFCMRVL_HCI_OGF 0x81
44: #define NFCMRVL_HCI_OCF 0xFE
45:
46: enum nfcmrvl_phy {
47: NFCMRVL_PHY_USB = 0,
48: NFCMRVL_PHY_UART = 1,
49: NFCMRVL_PHY_I2C = 2,
50: NFCMRVL_PHY_SPI = 3,
51: };
52:
53: struct nfcmrvl_platform_data {
54: /*
55: * Generic
56: */
57:
58: /* GPIO that is wired to RESET_N signal */
59: struct gpio_desc *reset_gpio;
60: /* Tell if transport is muxed in HCI one */
61: bool hci_muxed;
62:
63: /*
64: * UART specific
65: */
66:
67: /* Tell if UART needs flow control at init */
68: bool flow_control;
69: /* Tell if firmware supports break control for power management */
70: bool break_control;
71:
72:
73: /*
74: * I2C specific
75: */
76:
77: unsigned int irq;
78: unsigned int irq_polarity;
79: };
80:
81: struct nfcmrvl_private {
82:
83: unsigned long flags;
84:
85: /* Platform configuration */
86: struct nfcmrvl_platform_data config;
87:
88: /* Parent dev */
89: struct nci_dev *ndev;
90:
91: /* FW download context */
92: struct nfcmrvl_fw_dnld fw_dnld;
93:
94: /* FW download support */
95: bool support_fw_dnld;
96:
97: /*
98: * PHY related information
99: */
100:
]
|
| 252/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC driver: major functions
4: *
5: * Copyright (C) 2014-2015 Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/gpio/consumer.h>
10: #include <linux/delay.h>
11: #include <linux/of.h>
12: #include <linux/nfc.h>
13: #include <net/nfc/nci.h>
14: #include <net/nfc/nci_core.h>
15: #include "nfcmrvl.h"
16:
17: static int nfcmrvl_nci_open(struct nci_dev *ndev)
18: {
19: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
20: int err;
21:
22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
23: return 0;
24:
25: /* Reset possible fault of previous session */
26: clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
27:
28: err = priv->if_ops->nci_open(priv);
29:
30: if (err)
31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
32:
33: return err;
34: }
35:
36: static int nfcmrvl_nci_close(struct nci_dev *ndev)
37: {
38: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
39:
40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
41: return 0;
42:
43: priv->if_ops->nci_close(priv);
44:
45: return 0;
46: }
47:
48: static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
49: {
50: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
51:
52: nfc_info(priv->dev, "send entry, len %d\n", skb->len);
53:
54: skb->dev = (void *)ndev;
55:
56: if (priv->config.hci_muxed) {
57: unsigned char *hdr;
58: unsigned char len = skb->len;
59:
60: hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
61: hdr[0] = NFCMRVL_HCI_COMMAND_CODE;
62: hdr[1] = NFCMRVL_HCI_OGF;
63: hdr[2] = NFCMRVL_HCI_OCF;
64: hdr[3] = len;
65: }
66:
67: return priv->if_ops->nci_send(priv, skb);
68: }
69:
70: static int nfcmrvl_nci_setup(struct nci_dev *ndev)
71: {
72: __u8 val = 1;
73:
74: nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val);
75: return 0;
76: }
77:
78: static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
79: const char *firmware_name)
80: {
81: return nfcmrvl_fw_dnld_start(ndev, firmware_name);
82: }
83:
84: static const struct nci_ops nfcmrvl_nci_ops = {
85: .open = nfcmrvl_nci_open,
86: .close = nfcmrvl_nci_close,
87: .send = nfcmrvl_nci_send,
88: .setup = nfcmrvl_nci_setup,
89: .fw_download = nfcmrvl_nci_fw_download,
90: };
91:
92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
]
|
| 253/4 |
2026/08/26 15:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 26420
output: 42
thoughts: 26
Thoughts: Okay, let's dive into this. I need to get a handle on the interaction between the NFC driver and the NCI interface. The place to start, as always, is the core registration function.
I'll go check out the `main.c` file and specifically home in on the region surrounding `nfcmrvl_nci_register_dev()`. I need to understand how the NFC device is initially set up and linked to the NCI layer. I'm looking for where the device gets instantiated, allocated memory, how its characteristics are declared and assigned, and finally the registration with the system. That function is probably the key to understanding all of that. It's the lynchpin, the place where everything should come together.
|
| 254/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:100 LineCount:100]
Results: map[Contents: 100: int headroom;
101: int tailroom;
102: u32 protocols;
103:
104: priv = kzalloc_obj(*priv);
105: if (!priv)
106: return ERR_PTR(-ENOMEM);
107:
108: priv->drv_data = drv_data;
109: priv->if_ops = ops;
110: priv->dev = dev;
111: priv->phy = phy;
112:
113: memcpy(&priv->config, pdata, sizeof(*pdata));
114:
115: if (!priv->config.reset_gpio) {
116: priv->config.reset_gpio =
117: devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
118: if (IS_ERR(priv->config.reset_gpio)) {
119: priv->config.reset_gpio = NULL;
120: nfc_err(dev, "failed to get reset gpio\n");
121: }
122: }
123:
124: if (phy == NFCMRVL_PHY_SPI) {
125: headroom = NCI_SPI_HDR_LEN;
126: tailroom = 1;
127: } else
128: headroom = tailroom = 0;
129:
130: if (priv->config.hci_muxed)
131: headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
132:
133: protocols = NFC_PROTO_JEWEL_MASK
134: | NFC_PROTO_MIFARE_MASK
135: | NFC_PROTO_FELICA_MASK
136: | NFC_PROTO_ISO14443_MASK
137: | NFC_PROTO_ISO14443_B_MASK
138: | NFC_PROTO_ISO15693_MASK
139: | NFC_PROTO_NFC_DEP_MASK;
140:
141: priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
142: headroom, tailroom);
143: if (!priv->ndev) {
144: nfc_err(dev, "nci_allocate_device failed\n");
145: rc = -ENOMEM;
146: goto error_free;
147: }
148:
149: rc = nfcmrvl_fw_dnld_init(priv);
150: if (rc) {
151: nfc_err(dev, "failed to initialize FW download %d\n", rc);
152: goto error_free_dev;
153: }
154:
155: nci_set_drvdata(priv->ndev, priv);
156:
157: rc = nci_register_device(priv->ndev);
158: if (rc) {
159: nfc_err(dev, "nci_register_device failed %d\n", rc);
160: goto error_fw_dnld_deinit;
161: }
162:
163: /* Ensure that controller is powered off */
164: nfcmrvl_chip_halt(priv);
165:
166: nfc_info(dev, "registered with nci successfully\n");
167: return priv;
168:
169: error_fw_dnld_deinit:
170: nfcmrvl_fw_dnld_deinit(priv);
171: error_free_dev:
172: nci_free_device(priv->ndev);
173: error_free:
174: kfree(priv);
175: return ERR_PTR(rc);
176: }
177: EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
178:
179: void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
180: {
181: struct nci_dev *ndev = priv->ndev;
182:
183: nci_unregister_device(ndev);
184: if (priv->ndev->nfc_dev->fw_download_in_progress)
185: nfcmrvl_fw_dnld_abort(priv);
186:
187: nfcmrvl_fw_dnld_deinit(priv);
188:
189: nci_free_device(ndev);
190: kfree(priv);
191: }
192: EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
193:
194: int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
195: {
196: if (priv->config.hci_muxed) {
197: if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE &&
198: skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) {
199: /* Data packet, let's extract NCI payload */
]
|
| 255/4 |
2026/08/26 15:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 28207
output: 82
thoughts: 90
Thoughts: Okay, let's dive into this SPI probe ordering issue, as the feedback on SPI raised some questions I need to address. I'm going to start by scrutinizing the `spi.c` and `i2c.c` files, specifically within the `nfcmrvl` NFC driver. I need to understand how the SPI probe is functioning and see if the proposed fixes are truly valid. I'll need to know if the suggested approach can be implemented directly, or if there's a reason to leave the behavior focused, or perhaps how the current probe design actually works. To achieve that, I'm going to examine the implementation of `drivers/nfc/nfcmrvl/spi.c` and `drivers/nfc/nfcmrvl/i2c.c` as the first step in this process.
|
| 256/4 |
2026/08/26 15:11 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-SPI driver: SPI interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/nfc.h>
11: #include <linux/of_irq.h>
12: #include <net/nfc/nci.h>
13: #include <net/nfc/nci_core.h>
14: #include <linux/spi/spi.h>
15: #include "nfcmrvl.h"
16:
17: #define SPI_WAIT_HANDSHAKE 1
18:
19: struct nfcmrvl_spi_drv_data {
20: unsigned long flags;
21: struct spi_device *spi;
22: struct nci_spi *nci_spi;
23: struct completion handshake_completion;
24: struct nfcmrvl_private *priv;
25: };
26:
27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
45: nfc_err(&drv_data->spi->dev, "failed to read spi packet");
46: return IRQ_HANDLED;
47: }
48:
49: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
50: nfc_err(&drv_data->spi->dev, "corrupted RX packet");
51:
52: return IRQ_HANDLED;
53: }
54:
55: static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
56: {
57: return 0;
58: }
59:
60: static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
61: {
62: return 0;
63: }
64:
65: static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
66: struct sk_buff *skb)
67: {
68: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
69: int err;
70:
71: /* Reinit completion for slave handshake */
72: reinit_completion(&drv_data->handshake_completion);
73: set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
74:
75: /*
76: * Append a dummy byte at the end of SPI frame. This is due to a
77: * specific DMA implementation in the controller
78: */
79: skb_put(skb, 1);
80:
81: /* Send the SPI packet */
82: err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
83: skb);
84: if (err)
85: nfc_err(priv->dev, "spi_send failed %d", err);
86:
87: return err;
88: }
89:
90: static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv,
91: const void *param)
92: {
93: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
94: const struct nfcmrvl_fw_spi_config *config = param;
95:
96: drv_data->nci_spi->xfer_speed_hz = config->clk;
97: }
98:
99: static const struct nfcmrvl_if_ops spi_ops = {
100: .nci_open = nfcmrvl_spi_nci_open,
]
|
| 257/4 |
2026/08/26 15:12 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:101 LineCount:100]
Results: map[Contents: 101: .nci_close = nfcmrvl_spi_nci_close,
102: .nci_send = nfcmrvl_spi_nci_send,
103: .nci_update_config = nfcmrvl_spi_nci_update_config,
104: };
105:
106: static int nfcmrvl_spi_parse_dt(struct device_node *node,
107: struct nfcmrvl_platform_data *pdata)
108: {
109: int ret;
110:
111: ret = nfcmrvl_parse_dt(node, pdata);
112: if (ret < 0) {
113: pr_err("Failed to get generic entries\n");
114: return ret;
115: }
116:
117: ret = irq_of_parse_and_map(node, 0);
118: if (!ret) {
119: pr_err("Unable to get irq\n");
120: return -EINVAL;
121: }
122: pdata->irq = ret;
123:
124: return 0;
125: }
126:
127: static int nfcmrvl_spi_probe(struct spi_device *spi)
128: {
129: const struct nfcmrvl_platform_data *pdata;
130: struct nfcmrvl_platform_data config;
131: struct nfcmrvl_spi_drv_data *drv_data;
132: int ret = 0;
133:
134: drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL);
135: if (!drv_data)
136: return -ENOMEM;
137:
138: drv_data->spi = spi;
139: drv_data->priv = NULL;
140: spi_set_drvdata(spi, drv_data);
141:
142: pdata = spi->dev.platform_data;
143:
144: if (!pdata && spi->dev.of_node)
145: if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0)
146: pdata = &config;
147:
148: if (!pdata)
149: return -EINVAL;
150:
151: ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
152: NULL, nfcmrvl_spi_int_irq_thread_fn,
153: IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
154: "nfcmrvl_spi_int", drv_data);
155: if (ret < 0) {
156: nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler");
157: return -ENODEV;
158: }
159:
160: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
161: drv_data, &spi_ops,
162: &drv_data->spi->dev,
163: pdata);
164: if (IS_ERR(drv_data->priv))
165: return PTR_ERR(drv_data->priv);
166:
167: drv_data->priv->support_fw_dnld = true;
168:
169: drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10,
170: drv_data->priv->ndev);
171:
172: /* Init completion for slave handshake */
173: init_completion(&drv_data->handshake_completion);
174: return 0;
175: }
176:
177: static void nfcmrvl_spi_remove(struct spi_device *spi)
178: {
179: struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi);
180:
181: nfcmrvl_nci_unregister_dev(drv_data->priv);
182: }
183:
184: static const struct of_device_id of_nfcmrvl_spi_match[] __maybe_unused = {
185: { .compatible = "marvell,nfc-spi", },
186: {},
187: };
188: MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match);
189:
190: static const struct spi_device_id nfcmrvl_spi_id_table[] = {
191: { "nfcmrvl_spi", 0 },
192: { }
193: };
194: MODULE_DEVICE_TABLE(spi, nfcmrvl_spi_id_table);
195:
196: static struct spi_driver nfcmrvl_spi_driver = {
197: .probe = nfcmrvl_spi_probe,
198: .remove = nfcmrvl_spi_remove,
199: .id_table = nfcmrvl_spi_id_table,
200: .driver = {
]
|
| 258/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 31610
output: 34
thoughts: 4283
Thoughts: **Analysis of NFC Marvell USB Driver Initialization and Data Structures**
Okay, I've dug into this code and I think I've got a handle on what's happening and the potential issues. I've focused on `drivers/nfc/nfcmrvl/usb.c` as the reviewer suggested. The core of the problem, as I see it, revolves around how data structures are initialized and used, particularly the `drv_data` and `priv` structs and the use of the `NFCMRVL_NCI_RUNNING` flag.
My primary focus right now is to ensure the integrity of the data structures and that the NCI state is correctly reflected to prevent race conditions during USB bulk operations and resume events.
First, let's examine `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()`:
* In `nfcmrvl_bulk_complete()`, the code checks `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)` to determine if a bulk transfer should be processed. However, `drv_data->flags` holds *USB* related flags (`NFCMRVL_USB_BULK_RUNNING` and `NFCMRVL_USB_SUSPENDING`). This is a potential bug because `NFCMRVL_NCI_RUNNING` represents the *NCI* state which is set in `priv->flags`! The same check is performed again later with the correct flag `test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)`, which is redundant.
* In `nfcmrvl_resume()`, it checks `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`. This is *correct* because `priv->flags` is where NCI operational status is stored. This makes sense because `priv` is properly initialized at this point through the device probe sequence.
The root of the issue is that `drv_data->priv` isn't initialized immediately when other structs are allocated, but is assigned later after `nfcmrvl_nci_register_dev()`.
Here is the general flow to summarize the state transitions:
1. In `nfcmrvl_probe()`, the `drv_data` struct (of type `nfcmrvl_usb_drv_data`) is allocated.
2. `nfcmrvl_nci_register_dev()` is called, which allocates `priv` (of type `nfcmrvl_private`). Inside `nfcmrvl_nci_register_dev()`, it sets `priv->drv_data = drv_data`. Then, `nci_register_device()` is called.
3. Inside `nci_register_device()`, the NFC device is available and can be brought up in userspace, and it can call `nfc_dev_up()`.
4. Eventually `nfc_dev_up()` calls `nfcmrvl_nci_open()`.
5. In `nfcmrvl_nci_open()`, `test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags)` is set.
6. In the `nfcmrvl_probe()` function, after `nfcmrvl_nci_register_dev()` returns, the statement `drv_data->priv = priv;` happens. This assignment is *correct* because `priv` has been initialized to store `priv->drv_data = drv_data` already.
This means that within `nfcmrvl_probe()`, if other code paths attempt to use `drv_data->priv` *before* the assignment, there could be a use-after-free issue.
Given the reviewer's feedback and the code's structure, the best course of action is to fix the redundant check in `nfcmrvl_bulk_complete()`, and also to update the context passed to the URB to be `priv` instead of `drv_data` to ensure the correct `priv->flags` is checked! This ensures proper use of data structures and avoids potential issues due to an uninitialized state.
|
| 259/4 |
2026/08/26 15:12 |
tool |
grepper |
0m
Args: map[Expression:priv PathPrefix:drivers/nfc/nfcmrvl/usb.c]
Results: map[Output:drivers/nfc/nfcmrvl/usb.c=26=struct nfcmrvl_usb_drv_data {
--
drivers/nfc/nfcmrvl/usb.c-39- int suspend_count;
drivers/nfc/nfcmrvl/usb.c:40: struct nfcmrvl_private *priv;
drivers/nfc/nfcmrvl/usb.c-41-};
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-70-
drivers/nfc/nfcmrvl/usb.c:71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
drivers/nfc/nfcmrvl/usb.c-72- GFP_ATOMIC);
--
drivers/nfc/nfcmrvl/usb.c-77- urb->actual_length);
drivers/nfc/nfcmrvl/usb.c:78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
drivers/nfc/nfcmrvl/usb.c-79- nfc_err(&drv_data->udev->dev,
--
drivers/nfc/nfcmrvl/usb.c=147=static void nfcmrvl_tx_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-150- struct nci_dev *ndev = (struct nci_dev *)skb->dev;
drivers/nfc/nfcmrvl/usb.c:151: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
drivers/nfc/nfcmrvl/usb.c:152: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-153- unsigned long flags;
drivers/nfc/nfcmrvl/usb.c-154-
drivers/nfc/nfcmrvl/usb.c:155: nfc_info(priv->dev, "urb %p status %d count %d\n",
drivers/nfc/nfcmrvl/usb.c-156- urb, urb->status, urb->actual_length);
--
drivers/nfc/nfcmrvl/usb.c-165-
drivers/nfc/nfcmrvl/usb.c:166:static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
drivers/nfc/nfcmrvl/usb.c-167-{
drivers/nfc/nfcmrvl/usb.c:168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-169- int err;
--
drivers/nfc/nfcmrvl/usb.c=192=static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
--
drivers/nfc/nfcmrvl/usb.c-196-
drivers/nfc/nfcmrvl/usb.c:197:static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
drivers/nfc/nfcmrvl/usb.c-198-{
drivers/nfc/nfcmrvl/usb.c:199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-200- int err;
--
drivers/nfc/nfcmrvl/usb.c-219-
drivers/nfc/nfcmrvl/usb.c:220:static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
drivers/nfc/nfcmrvl/usb.c-221- struct sk_buff *skb)
drivers/nfc/nfcmrvl/usb.c-222-{
drivers/nfc/nfcmrvl/usb.c:223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
drivers/nfc/nfcmrvl/usb.c-224- struct urb *urb;
--
drivers/nfc/nfcmrvl/usb.c=286=static int nfcmrvl_probe(struct usb_interface *intf,
--
drivers/nfc/nfcmrvl/usb.c-289- struct nfcmrvl_usb_drv_data *drv_data;
drivers/nfc/nfcmrvl/usb.c:290: struct nfcmrvl_private *priv;
drivers/nfc/nfcmrvl/usb.c-291- struct usb_device *udev = interface_to_usbdev(intf);
--
drivers/nfc/nfcmrvl/usb.c-319-
drivers/nfc/nfcmrvl/usb.c:320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
drivers/nfc/nfcmrvl/usb.c-321- &intf->dev, &config);
drivers/nfc/nfcmrvl/usb.c:322: if (IS_ERR(priv))
drivers/nfc/nfcmrvl/usb.c:323: return PTR_ERR(priv);
drivers/nfc/nfcmrvl/usb.c-324-
drivers/nfc/nfcmrvl/usb.c:325: drv_data->priv = priv;
drivers/nfc/nfcmrvl/usb.c:326: drv_data->priv->support_fw_dnld = false;
drivers/nfc/nfcmrvl/usb.c-327-
--
drivers/nfc/nfcmrvl/usb.c=333=static void nfcmrvl_disconnect(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-341-
drivers/nfc/nfcmrvl/usb.c:342: nfcmrvl_nci_unregister_dev(drv_data->priv);
drivers/nfc/nfcmrvl/usb.c-343-
]
|
| 260/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 38520
output: 70
thoughts: 1233
Thoughts: **Analyzing the Marvell NFC USB Driver's Data Flow and Synchronization**
Okay, let's break this down. First, I'm looking at `nfcmrvl_disconnect()` and `nfcmrvl_resume()`. I see that both use `drv_data->priv`. Since `drv_data->priv` is set after `probe()` completes, that should be initialized when these functions run. So, no problem there.
Now, the question is why `drv_data->priv` couldn't be accessed in `nfcmrvl_bulk_complete()`. Ah, that's because `nfcmrvl_bulk_complete()` can be triggered asynchronously, potentially before `drv_data->priv` is fully assigned within `nfcmrvl_probe()`. The solution here is clever: by passing `priv` as the URB context in `usb_fill_bulk_urb()`, `nfcmrvl_bulk_complete()` can directly access `priv` via `urb->context`. It's a clean way to ensure the correct context is available in the asynchronous callback and then `priv->drv_data` gets populated, which is good.
Next, I'm checking the initialization of `drv_data->priv->support_fw_dnld = false;` in `nfcmrvl_probe()`. Wait a minute... I see it's explicitly set to `false`. But, hold on, wasn't `priv` zero-initialized? Right. That means `support_fw_dnld` is already `false` by default, so setting it explicitly is redundant but not harmful.
Now, let's examine `nfcmrvl_resume()`. I'm going through the code. I need to understand how the `nfcmrvl_submit_bulk_urb()` call works. It's called only in two places:
1. In `nfcmrvl_usb_nci_open()`
2. And in `nfcmrvl_resume()`.
So, `nfcmrvl_submit_bulk_urb()` plays a central role in both opening the USB communication and resuming from suspend. It's crucial to the running of the device.
Let me use the search tool to ensure I haven't missed any other places it's being called. I need to be thorough.
|
| 261/4 |
2026/08/26 15:12 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c IncludeSnippetLines:5 Name:nfcmrvl_submit_bulk_urb SourceTreePrefix:drivers/nfc/nfcmrvl/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_resume SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:414 SourceSnippet: 409:
410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
411: goto done;
412:
413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_resume SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:420 SourceSnippet: 415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
421: }
422:
423: spin_lock_irq(&drv_data->txlock);
424: nfcmrvl_play_deferred(drv_data);
425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_usb_nci_open SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:177 SourceSnippet: 172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_usb_nci_open SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:182 SourceSnippet: 177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
]] TruncatedOutput:false]
|
| 262/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 41318
output: 39
thoughts: 60
Thoughts: **Digging Deeper into the NFC Subsystem**
Alright, let's get down to brass tacks. I need to understand this `NFCMRVL_NCI_RUNNING` macro and the associated `flags` better. My initial thought is to make absolutely sure there aren't any other instances of these being used anywhere else within the subsystem, or perhaps even broader than that. I'm thinking a thorough search is in order, a complete audit really. My go-to tool for this kind of investigation is `grepper` – it’s the best way to ensure I'm getting a holistic view across the entire repository. Let's see what it uncovers.
|
| 263/4 |
2026/08/26 15:12 |
tool |
grepper |
0m
Args: map[Expression:NFCMRVL_NCI_RUNNING PathPrefix:drivers/nfc/nfcmrvl]
Results: map[Output:drivers/nfc/nfcmrvl/main.c=17=static int nfcmrvl_nci_open(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-21-
drivers/nfc/nfcmrvl/main.c:22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-23- return 0;
--
drivers/nfc/nfcmrvl/main.c-30- if (err)
drivers/nfc/nfcmrvl/main.c:31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
drivers/nfc/nfcmrvl/main.c-32-
--
drivers/nfc/nfcmrvl/main.c=36=static int nfcmrvl_nci_close(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-39-
drivers/nfc/nfcmrvl/main.c:40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-41- return 0;
--
drivers/nfc/nfcmrvl/main.c=194=int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
--
drivers/nfc/nfcmrvl/main.c-212-
drivers/nfc/nfcmrvl/main.c:213: if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-214- nci_recv_frame(priv->ndev, skb);
--
drivers/nfc/nfcmrvl/nfcmrvl.h=13=struct gpio_desc;
--
drivers/nfc/nfcmrvl/nfcmrvl.h-15-/* Define private flags: */
drivers/nfc/nfcmrvl/nfcmrvl.h:16:#define NFCMRVL_NCI_RUNNING 1
drivers/nfc/nfcmrvl/nfcmrvl.h-17-#define NFCMRVL_PHY_ERROR 2
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-64-
drivers/nfc/nfcmrvl/usb.c:65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-66- return;
--
drivers/nfc/nfcmrvl/usb.c=400=static int nfcmrvl_resume(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-409-
drivers/nfc/nfcmrvl/usb.c:410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-411- goto done;
]
|
| 264/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 42428
output: 39
thoughts: 3878
Thoughts: **Reviewing the USB PHY Bug Fix**
Okay, here's the plan. I've analyzed the code in `drivers/nfc/nfcmrvl/usb.c` and identified the crucial bug: the inconsistent use of `priv->flags` versus `drv_data->flags` when checking the `NFCMRVL_NCI_RUNNING` bit. As I suspected, the checks were incorrectly using `&drv_data->flags` in a few key places. Since `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` happen to be the same value, it worked sometimes, but was fundamentally flawed.
My fix focuses *strictly* on addressing this issue within `usb.c`, as the reviewer and my instructions directed. I'm avoiding any changes to `spi.c` to minimize the scope and risk. The reviewer was very clear about this: "keep the patch focused strictly on the USB PHY bug."
I've carefully reviewed `usb.c` and made changes to resolve the issue:
1. In `nfcmrvl_bulk_complete()`, I've updated the function signature to take a `struct nfcmrvl_private *priv` as the context and access the flags via the `priv` pointer. This ensures that the correct flag is checked. I've fixed the double check on `NFCMRVL_USB_BULK_RUNNING` bit too. The urb context points to `priv`, so this change is very simple.
2. Similarly, in `nfcmrvl_submit_bulk_urb()`, I've adjusted the function signature and the call to `usb_fill_bulk_urb()` to use the `priv` context for correct flag access.
3. In `nfcmrvl_usb_nci_open()`, I updated the `nfcmrvl_submit_bulk_urb` calls to use the `priv` pointer as well.
4. Finally, in `nfcmrvl_resume()`, I've added a pointer to the private data, and corrected the flags usage in the bulk submit call.
I've reviewed the rest of `usb.c` to be sure I did not miss any other place this bug may have existed.
By making these consistent and targeted changes, I'm confident that the code now correctly checks and manipulates the relevant flags, resolving the reported bug. This approach adheres to the review comments and coding practices.
|
| 265/4 |
2026/08/26 15:12 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:1 LineCount:60]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-USB driver: USB interface related functions
4: *
5: * Copyright (C) 2014, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/usb.h>
10: #include <linux/nfc.h>
11: #include <net/nfc/nci.h>
12: #include <net/nfc/nci_core.h>
13: #include "nfcmrvl.h"
14:
15: static struct usb_device_id nfcmrvl_table[] = {
16: { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
17: USB_CLASS_VENDOR_SPEC, 4, 1) },
18: { } /* Terminating entry */
19: };
20:
21: MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
22:
23: #define NFCMRVL_USB_BULK_RUNNING 1
24: #define NFCMRVL_USB_SUSPENDING 2
25:
26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
30: struct work_struct waker;
31: struct usb_anchor tx_anchor;
32: struct usb_anchor bulk_anchor;
33: struct usb_anchor deferred;
34: int tx_in_flight;
35: /* protects tx_in_flight */
36: spinlock_t txlock;
37: struct usb_endpoint_descriptor *bulk_tx_ep;
38: struct usb_endpoint_descriptor *bulk_rx_ep;
39: int suspend_count;
40: struct nfcmrvl_private *priv;
41: };
42:
43: static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
44: {
45: unsigned long flags;
46: int rv;
47:
48: spin_lock_irqsave(&drv_data->txlock, flags);
49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
]
|
| 266/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 47742
output: 636
thoughts: 593
Thoughts: **Analyzing the nfcmrvl USB Driver's Data Structures**
Okay, let's break this down. My goal is to ensure data integrity and proper handling of USB transfers within this NFC driver. I'm focusing on how data is passed and accessed, specifically concerning `nfcmrvl_private` and `nfcmrvl_usb_drv_data`.
First, I examined `nfcmrvl_tx_complete`. This function is critical for handling the completion of a USB transfer. I see that the `skb` is used to retrieve the `nci_dev` (using `skb->dev`). From there, `nci_get_drvdata` fetches the `nfcmrvl_private` structure (`priv`). This `priv` structure contains the `drv_data` pointer to `nfcmrvl_usb_drv_data`. The code correctly and safely dereferences these pointers – no issues there. It retrieves `drv_data`, decrements the `tx_in_flight` counter, frees some memory, and then deallocates the `skb`. Everything looks clean: the context is set up properly in `nfcmrvl_nci_send` and all the data pointers are valid. `drv_data->priv` is not accessed here, which is as expected.
Next, I looked at `nfcmrvl_usb_nci_send` and `nfcmrvl_usb_nci_close`. In both functions, the `priv` structure is used, and then the driver-specific data is accessed through `priv->drv_data`. This makes perfect sense; the driver data is initialized in `nfcmrvl_nci_register_dev()`, so these functions should be able to safely use the `drv_data`. I am satisfied that all access is consistent and well-defined across these key functions.
The analysis validates the memory access in the driver. Therefore, I will now use the `codeeditor` to make the edit to `drivers/nfc/nfcmrvl/usb.c`.
|
| 267/4 |
2026/08/26 15:12 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_usb_drv_data *drv_data = urb->context;
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))
return;
if (!urb->status) {
struct sk_buff *skb;
skb = nci_skb_alloc(drv_data->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)
nfc_err(&drv_data->udev->dev,
"corrupted Rx packet\n");
}
} NewCode:static void nfcmrvl_bulk_complete(struct urb *urb)
{
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, &priv->flags))
return;
if (!urb->status) {
struct sk_buff *skb;
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(priv, skb) < 0)
nfc_err(&drv_data->udev->dev,
"corrupted Rx packet\n");
}
} SourceFile:drivers/nfc/nfcmrvl/usb.c]
Results:
|
| 268/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 49421
output: 1673
thoughts: 47
Thoughts: Okay, here's the summary, rendered as if I'm the one thinking it:
**Updating Critical NFC Driver Functions**
Alright, time to dive back into the `nfcmrvl` driver code, specifically within `drivers/nfc/nfcmrvl/usb.c`. I need to focus on two functions: `nfcmrvl_submit_bulk_urb()` and `nfcmrvl_usb_nci_open()`. These are key for handling USB communication with the NFC controller, and likely have implications across the USB interface for NFC. Let's get to work and make sure these functions are up to spec.
|
| 269/4 |
2026/08/26 15:12 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int
nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
{
struct urb *urb;
unsigned char *buf;
unsigned int pipe;
int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
if (!drv_data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(drv_data->udev,
drv_data->bulk_rx_ep->bEndpointAddress);
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, drv_data);
urb->transfer_flags |= URB_FREE_BUFFER;
usb_mark_last_busy(drv_data->udev);
usb_anchor_urb(urb, &drv_data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
}
static void nfcmrvl_tx_complete(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct nci_dev *ndev = (struct nci_dev *)skb->dev;
struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
unsigned long flags;
nfc_info(priv->dev, "urb %p status %d count %d\n",
urb, urb->status, urb->actual_length);
spin_lock_irqsave(&drv_data->txlock, flags);
drv_data->tx_in_flight--;
spin_unlock_irqrestore(&drv_data->txlock, flags);
kfree(urb->setup_packet);
kfree_skb(skb);
}
static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
drv_data->intf->needs_remote_wakeup = 1;
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); NewCode:static int
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;
int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
if (!drv_data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(drv_data->udev,
drv_data->bulk_rx_ep->bEndpointAddress);
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
urb->transfer_flags |= URB_FREE_BUFFER;
usb_mark_last_busy(drv_data->udev);
usb_anchor_urb(urb, &drv_data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
}
static void nfcmrvl_tx_complete(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct nci_dev *ndev = (struct nci_dev *)skb->dev;
struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
unsigned long flags;
nfc_info(priv->dev, "urb %p status %d count %d\n",
urb, urb->status, urb->actual_length);
spin_lock_irqsave(&drv_data->txlock, flags);
drv_data->tx_in_flight--;
spin_unlock_irqrestore(&drv_data->txlock, flags);
kfree(urb->setup_packet);
kfree_skb(skb);
}
static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
drv_data->intf->needs_remote_wakeup = 1;
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(priv, GFP_KERNEL); SourceFile:drivers/nfc/nfcmrvl/usb.c]
Results:
|
| 270/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 51285
output: 553
thoughts: 29
Thoughts: Okay, let's dive into this. Here's how I'd approach summarizing the prompt, keeping in mind my own expert-level understanding:
**Refactoring the NFC Driver**
Alright, time to get my hands dirty. I'm looking at `drivers/nfc/nfcmrvl/usb.c` and specifically, I need to update the `nfcmrvl_resume()` function. This likely means I'm working with the Marvell NFC driver, probably in the context of a USB implementation. My initial thought process is geared towards understanding the context of this resume function within the overall USB driver structure. What exactly happens during a system resume that warrants this update? Am I dealing with a specific power management issue? Are there potential race conditions I need to address? Or is this part of a larger refactoring effort to improve power efficiency or stability after a suspend/resume cycle? I'll need to carefully review the surrounding code to grasp the intent and impact of any proposed changes to this specific function. I'll need to consider how the USB bus is handled during a resume, any device-specific quirks of this Marvell NFC chip, and how this particular function interacts with other parts of the driver, the NFC stack, and even the higher-level power management framework. Let's see what's in store.
|
| 271/4 |
2026/08/26 15:12 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int nfcmrvl_resume(struct usb_interface *intf)
{
struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
int err = 0;
nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
if (--drv_data->suspend_count)
return 0;
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
goto done;
if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
if (err) {
clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
goto failed;
}
nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
} NewCode: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);
if (--drv_data->suspend_count)
return 0;
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(priv, GFP_NOIO);
if (err) {
clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
goto failed;
}
nfcmrvl_submit_bulk_urb(priv, GFP_NOIO);
} SourceFile:drivers/nfc/nfcmrvl/usb.c]
Results:
|
| 272/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 52151
output: 10
thoughts: 13
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert and using first-person perspective:
**Diving into the Code Change**
Right, let's get down to brass tacks. I've got a patch to review. Instead of just staring at it blindly, my first instinct is always to reach for the familiar tool, `patch-diff`. Gotta quickly assess the scope of the change, identify the key areas impacted, and mentally build a map of what's been tweaked. I want a clean, concise view of the differences – nothing fancy, just the facts, ma'am. Let's see what we're actually dealing with here. Time to get this diff up on screen and start dissecting.
|
| 273/4 |
2026/08/26 15:12 |
tool |
patch-diff |
0m
Error:
|
| 274/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 52355
output: 15
thoughts: 15
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of software development and dealing with a tricky file comparison:
**Analyzing the Call to `patch-diff`**
Alright, so I'm about to call `patch-diff`. This is usually a key utility for me, it's what I rely on to figure out the differences between two files, likely to apply patches or generate them. The real question is: why am I passing an empty string as the `File:` argument? Hmm, that's unusual. It's either a deliberate edge case I'm testing, a placeholder I'll replace later, or potentially something a bit more interesting like wanting to compare to an "empty" file in my mental model. I'll need to figure out what context necessitates this unusual argument. Given my experience, I'm expecting some specific behavior with an empty file name in this particular call. Depending on the library, it could return an error, a default diff, or something entirely context-dependent. Time to dig into the surrounding code and see what I can find!
|
| 275/4 |
2026/08/26 15:12 |
tool |
patch-diff |
0m
Args:
Results: map[Output: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
@@ -49,97 +49,99 @@ static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
if (!rv)
drv_data->tx_in_flight++;
spin_unlock_irqrestore(&drv_data->txlock, flags);
return rv;
}
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");
}
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
return;
usb_anchor_urb(urb, &drv_data->bulk_anchor);
usb_mark_last_busy(drv_data->udev);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
/* -EPERM: urb is being killed;
* -ENODEV: device got disconnected
*/
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p failed to resubmit (%d)\n", urb, -err);
usb_unanchor_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;
int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
if (!drv_data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(drv_data->udev,
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;
usb_mark_last_busy(drv_data->udev);
usb_anchor_urb(urb, &drv_data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
}
@@ -166,27 +168,27 @@ static void nfcmrvl_tx_complete(struct urb *urb)
static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
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;
failed:
usb_autopm_put_interface(drv_data->intf);
return err;
}
static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
@@ -393,47 +395,48 @@ static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
/* Cleanup the rest deferred urbs. */
while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
kfree(urb->setup_packet);
usb_free_urb(urb);
}
}
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);
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);
nfcmrvl_play_deferred(drv_data);
clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
spin_unlock_irq(&drv_data->txlock);
return 0;
failed:
usb_scuttle_anchored_urbs(&drv_data->deferred);
done:
spin_lock_irq(&drv_data->txlock);
clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
spin_unlock_irq(&drv_data->txlock);
return err;
}
#endif
]
|
| 276/4 |
2026/08/26 15:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 55051
output: 695
thoughts: 745
Thoughts: **Reviewing the NFC USB Driver Patch**
Okay, let's break this down. I'm looking at a diff for `drivers/nfc/nfcmrvl/usb.c`. The core of it seems to be addressing a potential USB PHY bug related to how the NFCMarvell driver interacts with the USB interface. It appears the original code had some issues with context passing and flag checks, which are now being corrected.
Specifically, it looks like there were instances where the wrong context was being passed to the USB completion routine (`nfcmrvl_bulk_complete`), and also incorrect flag checks. The fix updates these instances, swapping out uses of `drv_data` for `priv` where appropriate, ensuring that the driver uses the correct context and state information.
Let me walk through the specific changes:
* In `nfcmrvl_bulk_complete`, we see that the `urb->context` is now correctly set to `priv`, which contains all of the driver's private data, and the flag check has been corrected, so that the correct flags are used. This change is crucial for proper data handling and error checking within the USB completion path. Additionally, the updated function correctly re-uses the correct variables, like `priv->ndev`, where appropriate.
* The `nfcmrvl_submit_bulk_urb` function signature has been modified. It now accepts the `priv` pointer as a parameter and `priv` is also passed as context in the `usb_fill_bulk_urb()` call. This ensures that the bulk URB submission function receives the correct driver context.
* The `nfcmrvl_usb_nci_open` function has also been updated, so it passes the correct parameter (`priv`), and ensures that the USB URB submission is handled properly.
* Similarly, `nfcmrvl_resume` has been updated to use the appropriate `priv` parameter for flag checks and bulk URB submission.
This change is focused, and limited to only `usb.c`. No other files, like `spi.c`, are affected, which is good. This approach aligns with best practices - we don't fix things that aren't broken, keeping the scope of the fix constrained.
To be extra careful, I've checked all the relevant aspects. All the type assignments align, `urb->context` is properly handled, and the function signatures are consistent across the calls and the definitions. Everything looks type-safe and consistent with standard kernel NFC driver conventions. Nothing jumps out as immediately problematic.
So, in short, I'm confident that this diff fixes a context and state management issue in the Marvell NFC USB driver. The fix seems clean, focused, and type-safe. It's now ready for a thorough explanation and review.
|
| 277/3 |
2026/08/26 15:13 |
action |
test-patch |
6m
Results: map[PatchDiff: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);
TestError:]
|
| 278/3 |
2026/08/26 15:19 |
action |
If |
2m
Args:
|
| 279/4 |
2026/08/26 15:19 |
agent |
patch-reviewer |
2m
Model:
Results: map[ReviewApproved:true ReviewComments:[]]
Instruction: You are an experienced Linux kernel maintainer reviewing a proposed patch that compiles and passes reproducer tests.
Evaluate the patch diff against the crash report, root cause, and the architectural design guidelines below.
Scope Constraint (Single-Patch Submissions):
We only generate single, standalone patches (not patch series). If a comprehensive architectural redesign would require a multi-patch series touching many files, accept a localized single-patch fix as long as it correctly fixes the bug, does not introduce new issues, and avoids sentinel band-aids within its local scope.
Evaluation:
- Set ReviewApproved=true (and leave ReviewComments empty) if the patch is acceptable for upstream submission as a single-patch fix.
- Set ReviewApproved=false and list concise, actionable feedback in ReviewComments if the patch contains fixable "Kernel Architectural Design & Maintainer Taste" violations.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
---
# Kernel Architectural Design & Maintainer Taste
Evaluate kernel patches and fix proposals beyond binary bug presence. Enforce **maintainer taste**, **lifecycle symmetry**, **typestate soundness**, and **topological graph simplicity**.
---
## 1. Core Philosophy: Band-Aid vs. Architectural Design
Naive patches often introduce **defensive sentinel guards** (e.g., ad-hoc `if (!ptr) return;` checks, status flags, or suppressed warnings) directly at the point of failure. While locally avoiding crashes, they degrade subsystem architecture by leaving uninitialized states reachable.
Maintainer "good taste" solves the root cause by **shifting invariants** (making invalid states unrepresentable) and moving dynamic runtime checks into static typestate guarantees.
```
HACKY BAND-AID FIX (Monolithic Cleanup) ARCHITECTURAL DESIGN (Scoped RAII / Invariant Shift)
─────────────────────────────────────── ────────────────────────────────────────────────────
[ Init / Entry ] [ Init / Entry ]
│ │
(Register Global Cleanup) (Allocate Resource A)
│ │
[ Init Sub-B ] ──fail──┐ [ Init Sub-B ] ──fail──┐
│ (success) │ │ (success) │
▼ │ ▼ ▼
[ Setup Complete ] │ (Arm B Cleanup / RAII) (Unwind A Only:
│ │ │ B never armed)
(Deferred Teardown) │ ▼ │
│ │ [ Setup Complete ] ▼
▼ ▼ │ [ Error Exit ]
[ release_all ] ◄───────┘ (LIFO Scope Teardown)
│ │
(if (!ctx->b) return;) <-- Defensive guard! ▼
│ [ Clean Destruction ]
[ Unsafe State ] (Zero sentinel checks needed)
```
```c
// ANTI-PATTERN (Defensive Sentinel Guard): Teardown called on partial init -> callee needs guard
void driver_cleanup(struct ctx *ctx) {
if (!ctx->buf) return; // <-- Sentinel band-aid
free_buffer(ctx->buf);
}
// CANONICAL SHIFT (Granular Scoped Action): Registered only upon complete initialization
ctx->buf = alloc_buffer();
if (!ctx->buf) return -ENOMEM;
devm_add_action_or_reset(dev, free_buffer_action, ctx->buf);
```
---
## 2. Maintainer Taste as Graph Topology & Structural Simplicity
Software design quality maps directly to graph-theoretic properties across the Control-Flow Graph (CFG), Data-Flow Graph (DFG), and Object Lifecycle DAG:
### A. Control-Flow Graph (CFG) Simplification
* **Branch Minimization & Path Explosion (McCabe 1976):** Every defensive check added to a compound destructor or callback (`if (!ctx->buffer) return;`) adds a predicate node ($\pi$), increases cyclomatic complexity $v(G) = |E| - |V| + 2$, causes exponential path explosion ($O(2^k)$ paths), and enlarges explicit state spaces ($|S| = \prod |D_i|$). Good taste eliminates the branch by guaranteeing destructors are invoked only on initialized typestates. *(Note: Standard idempotent leaf deallocators like `kfree(NULL)` and public API input sanitizers are exempt).*
* **Single-Entry Single-Exit (SESE) Symmetry (Ferrante 1987, Johnson 1994, Dijkstra 1972):** Resource acquisition and release must form strict **nested dominator trees**. If resource $R_i$ is acquired at node $A$, the set of release actions $\{B_1, \dots, B_m\}$ must form a strict **post-dominating cut** relative to $A$ across all maximal exit paths.
* **Pointer Uniformity (Linus's "Good Taste" Rule):** Eliminate special-case conditional branches by operating on address indirection (e.g., indirect pointers `**curr` in linked list unlinking) to unify edge and interior cases into a branchless invariant.
* **Lexical Scope Invariants & Affine Lifecycles (Wadler 1990, RAII):** Enforce SESE symmetry and "consumed exactly once" affine invariants using compiler-backed scoped cleanup (`<linux/cleanup.h>` `guard()`, `scoped_guard()`, `__free()`).
### B. Ownership & Lifetime DAGs
* **Acyclic Lifecycles & Topological Teardown (Tarjan 1972, Kahn 1962):** Resource ownership must form a strict Directed Acyclic Graph (DAG) $G = (R, E)$. Teardown order must strictly follow reverse topological sort $\text{toposort}(G)^R$. Mixing conflicting lifetime paradigms (e.g., embedding a dynamic refcounted `kref`/socket struct inside a device-managed `devres` buffer or parent container) violates DAG acyclicity, creating synchronous blocking hacks (`wait_for_completion`), circular pins, and Use-After-Free hazards.
* **Three-Phase Concurrent Quiescence:** Multi-threaded and asynchronous teardown (networking, block layer, RCU) must strictly sequence: (1) **Deactivation/Delisting** (make unreachable) -> (2) **Quiescence & Draining** (`synchronize_rcu()`, `cancel_work_sync()`, `napi_disable()`) -> (3) **Physical Reclamation** (`kfree()`, `kmem_cache_destroy()`).
* **Typestate Validity (Strom & Yemini 1986, Aldrich et al. 2009):** A struct with $N$ fields should not use runtime boolean flags (`ctx->is_initialized`) to model incomplete typestates. Sub-resources must transition as a deterministic typestate automaton ($S_{uninit} \xrightarrow{\text{alloc}} S_{init} \xrightarrow{\text{publish}} S_{registered}$), and registration functions must accept only fully initialized typestates.
---
## 3. Deterministic Decision Trigger Matrix
| Code Symptom / Trigger (When you see X) | Anti-Pattern Band-Aid (DO NOT DO Z) | Canonical Invariant Shift (DO Y) |
| :--- | :--- | :--- |
| **Null deref in compound destructor / cleanup callback** | Add `if (!priv->buf) return;` in composite cleanup handler | Register granular cleanup immediately upon allocation via `devm_add_action_or_reset()`, `<linux/cleanup.h>` `__free()`, or discrete reverse LIFO labels |
| **UAF on dynamic object after container unbind** | Allocate with `devm_kzalloc()` and block on `wait_for_completion()` | Allocate with `kzalloc()`, manage lifetime via `kref_get()`/`kref_put()`, call unbind/delist on unbind, free in `kref` release callback |
| **Goto ladder lock leaks on early error exit** | Sprinkle manual `mutex_unlock()` across error returns | Use `guard(mutex)(&lock)` or `scoped_guard(spinlock, &lock)` from `<linux/cleanup.h>` |
| **Callback / IRQ / timer fires before full init** | Add `if (!priv->ready)` check inside IRQ/timer handler | Move `request_irq()`, `timer_setup()`, or `napi_enable()` strictly to the end of setup after all state structures are fully initialized |
| **Multi-step setup failure leaks resources** | Route all errors to a single `err:` label calling a monolithic `cleanup(priv)` with NULL checks | Use `cleanup.h` RAII, granular `devm` actions, or a strict reverse LIFO goto ladder (`err_free_b:` -> `err_free_a:`) |
| **Mixed ownership / asymmetric refcount drops** | Conditionally call `kref_put()` in caller based on error code | Enforce unconditional callee-cleans or caller-cleans ownership convention across all paths |
| **Ad-hoc state flag polling during teardown** | Add `priv->stopping = true` and spin/poll in callbacks | Use atomic typestate transitions and synchronous flush/drain APIs (`cancel_work_sync()`, `drain_workqueue()`) |
---
## 4. The Architectural Review Checklist
Before finalizing any kernel fix or review, audit against the following four criteria:
1. **The Sentinel Test (Caller vs. Callee Responsibility):**
* *Smell:* Adding a defensive guard (`if (!ptr)` or `if (flags & INITIALIZED)`) inside a compound teardown callback or destructor to mask partial initialization.
* *Invariant:* If a destructor executes on uninitialized data, the defect is at the **caller's registration/invocation point**, not the callee.
* *Note:* Standard C allocator no-ops (`kfree(NULL)`) and public API parameter validators (`if (WARN_ON(!ptr)) return -EINVAL;`) are exempt; this rule targets *internal subsystem lifecycle pipelines and teardown paths*.
* *Action:* Move registration to the point of complete initialization (`devm_add_action_or_reset()`, RAII/`__free()`, or discrete caller unwinding).
2. **LIFO Stack Unwinding Symmetry:**
* *Smell:* Releasing resources in arbitrary order or invoking destructors for partially initialized subsystems.
* *Invariant:* Resource release must strictly mirror acquisition order in reverse (LIFO) without monolithic cleanup guards.
* *Action:* Verify that partial setup/initialization failures (e.g., `probe()`, `mount()`, `open()`, `alloc_pool()`) unwind only already-allocated resources in reverse order via strict LIFO labels or `cleanup.h` scope exit.
3. **Ownership Decoupling:**
* *Smell:* Embedding dynamically refcounted objects (`kref`, `struct sock`, `struct inode`, `struct file`) directly inside hardware device-bound allocations (`devres`) or parent containers (`super_block`, `net_device`).
* *Invariant:* Dynamic consumer references must outlive parent unregistration without use-after-free, circular pins, or blocking hacks.
* *Action:* Decouple the refcounted core object from the container binding; container teardown must unbind/delist the object, but memory is freed exclusively by the final `kref_put()`.
4. **Codebase Idiom Alignment:**
* *Smell:* Using legacy custom rollback ladders, ad-hoc state flags, or manual lock unlocking on error paths.
* *Invariant:* Prefer standard, modern kernel abstractions with compiler-enforced safety guarantees.
* *Action (Modern >= 6.6):* Replace manual boilerplate with `cleanup.h` (`guard()`, `scoped_guard()`, `__free()`), `devm_add_action_or_reset()`, `refcount_t`, and standard `kref` helpers.
* *Action (Legacy / LTS / No-RAII Subsystems):* Enforce clean, branchless reverse LIFO goto ladders (`err_free_b:` -> `err_free_a:`).
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
Bug title:
general protection fault in nfcmrvl_bulk_complete
The crash report:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The root cause explanation:
The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
The proposed patch diff:
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);
Review this patch diff against the crash report, root cause explanation, and kernel architectural design guidelines.
|
| 280/5 |
2026/08/26 15:19 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 14496
output: 123
thoughts: 94
Thoughts: **Reviewing the nfcmrvl Crash Patch**
Okay, so we have a patch proposed to address a crash within `nfcmrvl_bulk_complete`. That immediately flags a potential issue in the USB communication path of the Marvell NFC driver, specifically in `drivers/nfc/nfcmrvl/usb.c`. My initial thought is to dive right into the code to understand the root cause. This likely involves inspecting the completion logic and any potential race conditions or buffer issues.
Beyond `usb.c`, I'll need to broaden the scope. I know the Marvell NFC driver is complex, so I'll also want to review `drivers/nfc/nfcmrvl/main.c` and any other related files, paying close attention to data structures, locking, and how the USB transfers are initiated and handled in relation to the main NFC functionality. I need to understand the full context of the bulk transfer completion to assess the validity and completeness of the proposed fix. We're looking for a crash, so the devil will be in the details of error handling, buffer management, and interrupt handling within those routines. I'll need to identify the exact cause of the crash before evaluating the patch's merit.
|
| 281/5 |
2026/08/26 15:19 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-USB driver: USB interface related functions
4: *
5: * Copyright (C) 2014, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/usb.h>
10: #include <linux/nfc.h>
11: #include <net/nfc/nci.h>
12: #include <net/nfc/nci_core.h>
13: #include "nfcmrvl.h"
14:
15: static struct usb_device_id nfcmrvl_table[] = {
16: { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
17: USB_CLASS_VENDOR_SPEC, 4, 1) },
18: { } /* Terminating entry */
19: };
20:
21: MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
22:
23: #define NFCMRVL_USB_BULK_RUNNING 1
24: #define NFCMRVL_USB_SUSPENDING 2
25:
26: struct nfcmrvl_usb_drv_data {
27: struct usb_device *udev;
28: struct usb_interface *intf;
29: unsigned long flags;
30: struct work_struct waker;
31: struct usb_anchor tx_anchor;
32: struct usb_anchor bulk_anchor;
33: struct usb_anchor deferred;
34: int tx_in_flight;
35: /* protects tx_in_flight */
36: spinlock_t txlock;
37: struct usb_endpoint_descriptor *bulk_tx_ep;
38: struct usb_endpoint_descriptor *bulk_rx_ep;
39: int suspend_count;
40: struct nfcmrvl_private *priv;
41: };
42:
43: static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
44: {
45: unsigned long flags;
46: int rv;
47:
48: spin_lock_irqsave(&drv_data->txlock, flags);
49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
100: }
]
|
| 282/5 |
2026/08/26 15:19 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC driver: major functions
4: *
5: * Copyright (C) 2014-2015 Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/gpio/consumer.h>
10: #include <linux/delay.h>
11: #include <linux/of.h>
12: #include <linux/nfc.h>
13: #include <net/nfc/nci.h>
14: #include <net/nfc/nci_core.h>
15: #include "nfcmrvl.h"
16:
17: static int nfcmrvl_nci_open(struct nci_dev *ndev)
18: {
19: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
20: int err;
21:
22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
23: return 0;
24:
25: /* Reset possible fault of previous session */
26: clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
27:
28: err = priv->if_ops->nci_open(priv);
29:
30: if (err)
31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
32:
33: return err;
34: }
35:
36: static int nfcmrvl_nci_close(struct nci_dev *ndev)
37: {
38: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
39:
40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
41: return 0;
42:
43: priv->if_ops->nci_close(priv);
44:
45: return 0;
46: }
47:
48: static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
49: {
50: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
51:
52: nfc_info(priv->dev, "send entry, len %d\n", skb->len);
53:
54: skb->dev = (void *)ndev;
55:
56: if (priv->config.hci_muxed) {
57: unsigned char *hdr;
58: unsigned char len = skb->len;
59:
60: hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
61: hdr[0] = NFCMRVL_HCI_COMMAND_CODE;
62: hdr[1] = NFCMRVL_HCI_OGF;
63: hdr[2] = NFCMRVL_HCI_OCF;
64: hdr[3] = len;
65: }
66:
67: return priv->if_ops->nci_send(priv, skb);
68: }
69:
70: static int nfcmrvl_nci_setup(struct nci_dev *ndev)
71: {
72: __u8 val = 1;
73:
74: nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val);
75: return 0;
76: }
77:
78: static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
79: const char *firmware_name)
80: {
81: return nfcmrvl_fw_dnld_start(ndev, firmware_name);
82: }
83:
84: static const struct nci_ops nfcmrvl_nci_ops = {
85: .open = nfcmrvl_nci_open,
86: .close = nfcmrvl_nci_close,
87: .send = nfcmrvl_nci_send,
88: .setup = nfcmrvl_nci_setup,
89: .fw_download = nfcmrvl_nci_fw_download,
90: };
91:
92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
]
|
| 283/5 |
2026/08/26 15:19 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:1 LineCount:100]
Results: map[Contents: 1: /* SPDX-License-Identifier: GPL-2.0-only */
2: /*
3: * Marvell NFC driver
4: *
5: * Copyright (C) 2014-2015, Marvell International Ltd.
6: */
7:
8: #ifndef _NFCMRVL_H_
9: #define _NFCMRVL_H_
10:
11: #include "fw_dnld.h"
12:
13: struct gpio_desc;
14:
15: /* Define private flags: */
16: #define NFCMRVL_NCI_RUNNING 1
17: #define NFCMRVL_PHY_ERROR 2
18:
19: #define NFCMRVL_EXT_COEX_ID 0xE0
20: #define NFCMRVL_NOT_ALLOWED_ID 0xE1
21: #define NFCMRVL_ACTIVE_ID 0xE2
22: #define NFCMRVL_EXT_COEX_ENABLE 1
23: #define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA
24: #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB
25: #define NFCMRVL_NCI_MAX_EVENT_SIZE 260
26:
27: /*
28: * NCI FW Parameters
29: */
30:
31: #define NFCMRVL_PB_BAIL_OUT 0x11
32: #define NFCMRVL_PROP_REF_CLOCK 0xF0
33: #define NFCMRVL_PROP_SET_HI_CONFIG 0xF1
34:
35: /*
36: * HCI defines
37: */
38:
39: #define NFCMRVL_HCI_EVENT_HEADER_SIZE 0x04
40: #define NFCMRVL_HCI_EVENT_CODE 0x04
41: #define NFCMRVL_HCI_NFC_EVENT_CODE 0xFF
42: #define NFCMRVL_HCI_COMMAND_CODE 0x01
43: #define NFCMRVL_HCI_OGF 0x81
44: #define NFCMRVL_HCI_OCF 0xFE
45:
46: enum nfcmrvl_phy {
47: NFCMRVL_PHY_USB = 0,
48: NFCMRVL_PHY_UART = 1,
49: NFCMRVL_PHY_I2C = 2,
50: NFCMRVL_PHY_SPI = 3,
51: };
52:
53: struct nfcmrvl_platform_data {
54: /*
55: * Generic
56: */
57:
58: /* GPIO that is wired to RESET_N signal */
59: struct gpio_desc *reset_gpio;
60: /* Tell if transport is muxed in HCI one */
61: bool hci_muxed;
62:
63: /*
64: * UART specific
65: */
66:
67: /* Tell if UART needs flow control at init */
68: bool flow_control;
69: /* Tell if firmware supports break control for power management */
70: bool break_control;
71:
72:
73: /*
74: * I2C specific
75: */
76:
77: unsigned int irq;
78: unsigned int irq_polarity;
79: };
80:
81: struct nfcmrvl_private {
82:
83: unsigned long flags;
84:
85: /* Platform configuration */
86: struct nfcmrvl_platform_data config;
87:
88: /* Parent dev */
89: struct nci_dev *ndev;
90:
91: /* FW download context */
92: struct nfcmrvl_fw_dnld fw_dnld;
93:
94: /* FW download support */
95: bool support_fw_dnld;
96:
97: /*
98: * PHY related information
99: */
100:
]
|
| 284/5 |
2026/08/26 15:19 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 19143
output: 127
thoughts: 56
Thoughts: Here's my summary, as if I were thinking these thoughts:
**Diving into the Marvell NFC Driver Code**
Alright, time to dig deeper. I need to get a better understanding of how this Marvell NFC driver works. I'm going to focus on three key files to start: `nfcmrvl.h`, `usb.c`, and `main.c`. `nfcmrvl.h` should give me the interface definitions, data structures, and perhaps some essential constants – the blueprint, really. `usb.c` is where I'll expect to find the USB-specific implementation details – how the driver communicates with the NFC controller over the USB bus. Finally, `main.c`… that's likely the core logic: initialization, event handling, and the overall driver entry points. By looking at these, I should be able to trace the control flow and understand the interaction between the driver and the hardware. Let's get to it. This will reveal the low-level nuances of the Marvell NFC implementation, and allow me to determine the best approach for further enhancements.
|
| 285/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/nfcmrvl.h FirstLine:100 LineCount:40]
Results: map[Contents: 100:
101: /* PHY driver context */
102: void *drv_data;
103: /* PHY device */
104: struct device *dev;
105: /* PHY type */
106: enum nfcmrvl_phy phy;
107: /* Low level driver ops */
108: const struct nfcmrvl_if_ops *if_ops;
109: };
110:
111: struct nfcmrvl_if_ops {
112: int (*nci_open) (struct nfcmrvl_private *priv);
113: int (*nci_close) (struct nfcmrvl_private *priv);
114: int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb);
115: void (*nci_update_config)(struct nfcmrvl_private *priv,
116: const void *param);
117: };
118:
119: void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
120: int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
121: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
122: void *drv_data,
123: const struct nfcmrvl_if_ops *ops,
124: struct device *dev,
125: const struct nfcmrvl_platform_data *pdata);
126:
127:
128: void nfcmrvl_chip_reset(struct nfcmrvl_private *priv);
129: void nfcmrvl_chip_halt(struct nfcmrvl_private *priv);
130:
131: int nfcmrvl_parse_dt(struct device_node *node,
132: struct nfcmrvl_platform_data *pdata);
133:
134: #endif
]
|
| 286/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:101 LineCount:100]
Results: map[Contents: 101:
102: static int
103: nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
104: {
105: struct urb *urb;
106: unsigned char *buf;
107: unsigned int pipe;
108: int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
109:
110: if (!drv_data->bulk_rx_ep)
111: return -ENODEV;
112:
113: urb = usb_alloc_urb(0, mem_flags);
114: if (!urb)
115: return -ENOMEM;
116:
117: buf = kmalloc(size, mem_flags);
118: if (!buf) {
119: usb_free_urb(urb);
120: return -ENOMEM;
121: }
122:
123: pipe = usb_rcvbulkpipe(drv_data->udev,
124: drv_data->bulk_rx_ep->bEndpointAddress);
125:
126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
127: nfcmrvl_bulk_complete, drv_data);
128:
129: urb->transfer_flags |= URB_FREE_BUFFER;
130:
131: usb_mark_last_busy(drv_data->udev);
132: usb_anchor_urb(urb, &drv_data->bulk_anchor);
133:
134: err = usb_submit_urb(urb, mem_flags);
135: if (err) {
136: if (err != -EPERM && err != -ENODEV)
137: nfc_err(&drv_data->udev->dev,
138: "urb %p submission failed (%d)\n", urb, -err);
139: usb_unanchor_urb(urb);
140: }
141:
142: usb_free_urb(urb);
143:
144: return err;
145: }
146:
147: static void nfcmrvl_tx_complete(struct urb *urb)
148: {
149: struct sk_buff *skb = urb->context;
150: struct nci_dev *ndev = (struct nci_dev *)skb->dev;
151: struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
152: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
153: unsigned long flags;
154:
155: nfc_info(priv->dev, "urb %p status %d count %d\n",
156: urb, urb->status, urb->actual_length);
157:
158: spin_lock_irqsave(&drv_data->txlock, flags);
159: drv_data->tx_in_flight--;
160: spin_unlock_irqrestore(&drv_data->txlock, flags);
161:
162: kfree(urb->setup_packet);
163: kfree_skb(skb);
164: }
165:
166: static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
167: {
168: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
169: int err;
170:
171: err = usb_autopm_get_interface(drv_data->intf);
172: if (err)
173: return err;
174:
175: drv_data->intf->needs_remote_wakeup = 1;
176:
177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
178: if (err)
179: goto failed;
180:
181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
183:
184: usb_autopm_put_interface(drv_data->intf);
185: return 0;
186:
187: failed:
188: usb_autopm_put_interface(drv_data->intf);
189: return err;
190: }
191:
192: static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
193: {
194: usb_kill_anchored_urbs(&drv_data->bulk_anchor);
195: }
196:
197: static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
198: {
199: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
200: int err;
]
|
| 287/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/main.c FirstLine:90 LineCount:100]
Results: map[Contents: 90: };
91:
92: struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
93: void *drv_data,
94: const struct nfcmrvl_if_ops *ops,
95: struct device *dev,
96: const struct nfcmrvl_platform_data *pdata)
97: {
98: struct nfcmrvl_private *priv;
99: int rc;
100: int headroom;
101: int tailroom;
102: u32 protocols;
103:
104: priv = kzalloc_obj(*priv);
105: if (!priv)
106: return ERR_PTR(-ENOMEM);
107:
108: priv->drv_data = drv_data;
109: priv->if_ops = ops;
110: priv->dev = dev;
111: priv->phy = phy;
112:
113: memcpy(&priv->config, pdata, sizeof(*pdata));
114:
115: if (!priv->config.reset_gpio) {
116: priv->config.reset_gpio =
117: devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
118: if (IS_ERR(priv->config.reset_gpio)) {
119: priv->config.reset_gpio = NULL;
120: nfc_err(dev, "failed to get reset gpio\n");
121: }
122: }
123:
124: if (phy == NFCMRVL_PHY_SPI) {
125: headroom = NCI_SPI_HDR_LEN;
126: tailroom = 1;
127: } else
128: headroom = tailroom = 0;
129:
130: if (priv->config.hci_muxed)
131: headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
132:
133: protocols = NFC_PROTO_JEWEL_MASK
134: | NFC_PROTO_MIFARE_MASK
135: | NFC_PROTO_FELICA_MASK
136: | NFC_PROTO_ISO14443_MASK
137: | NFC_PROTO_ISO14443_B_MASK
138: | NFC_PROTO_ISO15693_MASK
139: | NFC_PROTO_NFC_DEP_MASK;
140:
141: priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
142: headroom, tailroom);
143: if (!priv->ndev) {
144: nfc_err(dev, "nci_allocate_device failed\n");
145: rc = -ENOMEM;
146: goto error_free;
147: }
148:
149: rc = nfcmrvl_fw_dnld_init(priv);
150: if (rc) {
151: nfc_err(dev, "failed to initialize FW download %d\n", rc);
152: goto error_free_dev;
153: }
154:
155: nci_set_drvdata(priv->ndev, priv);
156:
157: rc = nci_register_device(priv->ndev);
158: if (rc) {
159: nfc_err(dev, "nci_register_device failed %d\n", rc);
160: goto error_fw_dnld_deinit;
161: }
162:
163: /* Ensure that controller is powered off */
164: nfcmrvl_chip_halt(priv);
165:
166: nfc_info(dev, "registered with nci successfully\n");
167: return priv;
168:
169: error_fw_dnld_deinit:
170: nfcmrvl_fw_dnld_deinit(priv);
171: error_free_dev:
172: nci_free_device(priv->ndev);
173: error_free:
174: kfree(priv);
175: return ERR_PTR(rc);
176: }
177: EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
178:
179: void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
180: {
181: struct nci_dev *ndev = priv->ndev;
182:
183: nci_unregister_device(ndev);
184: if (priv->ndev->nfc_dev->fw_download_in_progress)
185: nfcmrvl_fw_dnld_abort(priv);
186:
187: nfcmrvl_fw_dnld_deinit(priv);
188:
189: nci_free_device(ndev);
]
|
| 288/5 |
2026/08/26 15:20 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 23217
output: 126
thoughts: 22
Thoughts: Okay, let's dive into this code.
**Diving into `drivers/nfc/nfcmrvl/usb.c`**
Alright, time to get into the details of this `usb.c` file within the `nfcmrvl` NFC driver. I need to understand how this module handles the USB communication. Knowing this, I can figure out how the system interacts with the NFC controller over the USB bus. I'll need to look for initialization routines, any interrupt handlers it might use, the transaction mechanisms, and how the data packets are structured. Specifically, how does it manage the USB descriptors and endpoints to set up the connection and send/receive data? I'll be looking for how they are using libusb, of course. I'll also be curious about any power management considerations – does it handle USB suspend/resume correctly? Any error handling would be good to note also. Got to be prepared to trace through the code, map the USB functions to NFC transactions, and look for any potential bottlenecks or inefficiencies. This should give me a good understanding of the NFC USB interface.
|
| 289/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:200 LineCount:100]
Results: map[Contents: 200: int err;
201:
202: cancel_work_sync(&drv_data->waker);
203:
204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
205:
206: nfcmrvl_usb_stop_traffic(drv_data);
207: usb_kill_anchored_urbs(&drv_data->tx_anchor);
208: err = usb_autopm_get_interface(drv_data->intf);
209: if (err)
210: goto failed;
211:
212: drv_data->intf->needs_remote_wakeup = 0;
213: usb_autopm_put_interface(drv_data->intf);
214:
215: failed:
216: usb_scuttle_anchored_urbs(&drv_data->deferred);
217: return 0;
218: }
219:
220: static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
221: struct sk_buff *skb)
222: {
223: struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
224: struct urb *urb;
225: unsigned int pipe;
226: int err;
227:
228: if (!drv_data->bulk_tx_ep)
229: return -ENODEV;
230:
231: urb = usb_alloc_urb(0, GFP_ATOMIC);
232: if (!urb)
233: return -ENOMEM;
234:
235: pipe = usb_sndbulkpipe(drv_data->udev,
236: drv_data->bulk_tx_ep->bEndpointAddress);
237:
238: usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
239: nfcmrvl_tx_complete, skb);
240:
241: err = nfcmrvl_inc_tx(drv_data);
242: if (err) {
243: usb_anchor_urb(urb, &drv_data->deferred);
244: schedule_work(&drv_data->waker);
245: err = 0;
246: goto done;
247: }
248:
249: usb_anchor_urb(urb, &drv_data->tx_anchor);
250:
251: err = usb_submit_urb(urb, GFP_ATOMIC);
252: if (err) {
253: if (err != -EPERM && err != -ENODEV)
254: nfc_err(&drv_data->udev->dev,
255: "urb %p submission failed (%d)\n", urb, -err);
256: kfree(urb->setup_packet);
257: usb_unanchor_urb(urb);
258: } else {
259: usb_mark_last_busy(drv_data->udev);
260: }
261:
262: done:
263: usb_free_urb(urb);
264: return err;
265: }
266:
267: static const struct nfcmrvl_if_ops usb_ops = {
268: .nci_open = nfcmrvl_usb_nci_open,
269: .nci_close = nfcmrvl_usb_nci_close,
270: .nci_send = nfcmrvl_usb_nci_send,
271: };
272:
273: static void nfcmrvl_waker(struct work_struct *work)
274: {
275: struct nfcmrvl_usb_drv_data *drv_data =
276: container_of(work, struct nfcmrvl_usb_drv_data, waker);
277: int err;
278:
279: err = usb_autopm_get_interface(drv_data->intf);
280: if (err)
281: return;
282:
283: usb_autopm_put_interface(drv_data->intf);
284: }
285:
286: static int nfcmrvl_probe(struct usb_interface *intf,
287: const struct usb_device_id *id)
288: {
289: struct nfcmrvl_usb_drv_data *drv_data;
290: struct nfcmrvl_private *priv;
291: struct usb_device *udev = interface_to_usbdev(intf);
292: struct nfcmrvl_platform_data config;
293: int ret;
294:
295: /* No configuration for USB */
296: memset(&config, 0, sizeof(config));
297: config.reset_gpio = NULL;
298:
299: nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
]
|
| 290/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:300 LineCount:100]
Results: map[Contents: 300:
301: drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
302: if (!drv_data)
303: return -ENOMEM;
304:
305: ret = usb_find_common_endpoints(intf->cur_altsetting, &drv_data->bulk_rx_ep,
306: &drv_data->bulk_tx_ep, NULL, NULL);
307: if (ret)
308: return -ENODEV;
309:
310: drv_data->udev = udev;
311: drv_data->intf = intf;
312:
313: INIT_WORK(&drv_data->waker, nfcmrvl_waker);
314: spin_lock_init(&drv_data->txlock);
315:
316: init_usb_anchor(&drv_data->tx_anchor);
317: init_usb_anchor(&drv_data->bulk_anchor);
318: init_usb_anchor(&drv_data->deferred);
319:
320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
332:
333: static void nfcmrvl_disconnect(struct usb_interface *intf)
334: {
335: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
336:
337: if (!drv_data)
338: return;
339:
340: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
341:
342: nfcmrvl_nci_unregister_dev(drv_data->priv);
343:
344: usb_set_intfdata(drv_data->intf, NULL);
345: }
346:
347: #ifdef CONFIG_PM
348: static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
349: {
350: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
351:
352: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
353:
354: if (drv_data->suspend_count++)
355: return 0;
356:
357: spin_lock_irq(&drv_data->txlock);
358: if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
359: set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
360: spin_unlock_irq(&drv_data->txlock);
361: } else {
362: spin_unlock_irq(&drv_data->txlock);
363: drv_data->suspend_count--;
364: return -EBUSY;
365: }
366:
367: nfcmrvl_usb_stop_traffic(drv_data);
368: usb_kill_anchored_urbs(&drv_data->tx_anchor);
369:
370: return 0;
371: }
372:
373: static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
374: {
375: struct urb *urb;
376: int err;
377:
378: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
379: usb_anchor_urb(urb, &drv_data->tx_anchor);
380:
381: err = usb_submit_urb(urb, GFP_ATOMIC);
382: if (err) {
383: kfree(urb->setup_packet);
384: usb_unanchor_urb(urb);
385: usb_free_urb(urb);
386: break;
387: }
388:
389: drv_data->tx_in_flight++;
390: usb_free_urb(urb);
391: }
392:
393: /* Cleanup the rest deferred urbs. */
394: while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
395: kfree(urb->setup_packet);
396: usb_free_urb(urb);
397: }
398: }
399:
]
|
| 291/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:400 LineCount:100]
Results: map[Contents: 400: static int nfcmrvl_resume(struct usb_interface *intf)
401: {
402: struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
403: int err = 0;
404:
405: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
406:
407: if (--drv_data->suspend_count)
408: return 0;
409:
410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
411: goto done;
412:
413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
415: if (err) {
416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
417: goto failed;
418: }
419:
420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
421: }
422:
423: spin_lock_irq(&drv_data->txlock);
424: nfcmrvl_play_deferred(drv_data);
425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
426: spin_unlock_irq(&drv_data->txlock);
427:
428: return 0;
429:
430: failed:
431: usb_scuttle_anchored_urbs(&drv_data->deferred);
432: done:
433: spin_lock_irq(&drv_data->txlock);
434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
435: spin_unlock_irq(&drv_data->txlock);
436:
437: return err;
438: }
439: #endif
440:
441: static struct usb_driver nfcmrvl_usb_driver = {
442: .name = "nfcmrvl",
443: .probe = nfcmrvl_probe,
444: .disconnect = nfcmrvl_disconnect,
445: #ifdef CONFIG_PM
446: .suspend = nfcmrvl_suspend,
447: .resume = nfcmrvl_resume,
448: .reset_resume = nfcmrvl_resume,
449: #endif
450: .id_table = nfcmrvl_table,
451: .supports_autosuspend = 1,
452: .disable_hub_initiated_lpm = 1,
453: .soft_unbind = 1,
454: };
455: module_usb_driver(nfcmrvl_usb_driver);
456:
457: MODULE_AUTHOR("Marvell International Ltd.");
458: MODULE_DESCRIPTION("Marvell NFC-over-USB driver");
459: MODULE_LICENSE("GPL v2");
]
|
| 292/5 |
2026/08/26 15:20 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 27638
output: 122
thoughts: 54
Thoughts: Here's my thinking process:
**Diving into NFC Driver Implementation Details**
Okay, so I need to understand how this NFC driver interacts with the system, specifically concerning its initialization and control flow. I'm going to leverage my familiarity with the kernel driver structure. My initial thought is, let's look at other drivers in the `drivers/nfc/nfcmrvl/` directory – they likely share a similar architecture and handling of the NFC device lifecycle. I'm interested in how they've implemented the `probe`, `open`, and flag management routines. This will probably quickly reveal how to structure my approach. I'll be looking for common patterns and best practices.
Then, to get a broader view of how these devices generally interface with the hardware, I'll branch out and examine the I2C, SPI, and UART drivers, specifically `i2c.c`, `spi.c`, and `uart.c`. They are basic communication protocols, so there might be helpful patterns there regarding resource allocation, error handling, and message passing that can inform my implementation. The goal here is to establish a solid grounding in the standard practices within the driver ecosystem.
|
| 293/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-I2C driver: I2C interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/i2c.h>
11: #include <linux/nfc.h>
12: #include <linux/delay.h>
13: #include <linux/of_irq.h>
14: #include <net/nfc/nci.h>
15: #include <net/nfc/nci_core.h>
16: #include "nfcmrvl.h"
17:
18: struct nfcmrvl_i2c_drv_data {
19: unsigned long flags;
20: struct device *dev;
21: struct i2c_client *i2c;
22: struct nfcmrvl_private *priv;
23: };
24:
25: static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data,
26: struct sk_buff **skb)
27: {
28: int ret;
29: struct nci_ctrl_hdr nci_hdr;
30:
31: /* Read NCI header to know the payload size */
32: ret = i2c_master_recv(drv_data->i2c, (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE);
33: if (ret != NCI_CTRL_HDR_SIZE) {
34: nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n");
35: return -EBADMSG;
36: }
37:
38: *skb = nci_skb_alloc(drv_data->priv->ndev,
39: nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL);
40: if (!*skb)
41: return -ENOMEM;
42:
43: /* Copy NCI header into the SKB */
44: skb_put_data(*skb, &nci_hdr, NCI_CTRL_HDR_SIZE);
45:
46: if (nci_hdr.plen) {
47: /* Read the NCI payload */
48: ret = i2c_master_recv(drv_data->i2c,
49: skb_put(*skb, nci_hdr.plen),
50: nci_hdr.plen);
51:
52: if (ret != nci_hdr.plen) {
53: nfc_err(&drv_data->i2c->dev,
54: "Invalid frame payload length: %u (expected %u)\n",
55: ret, nci_hdr.plen);
56: kfree_skb(*skb);
57: return -EBADMSG;
58: }
59: }
60:
61: return 0;
62: }
63:
64: static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
65: {
66: struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
67: struct sk_buff *skb = NULL;
68: int ret;
69:
70: if (!drv_data->priv)
71: return IRQ_HANDLED;
72:
73: if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
74: return IRQ_HANDLED;
75:
76: ret = nfcmrvl_i2c_read(drv_data, &skb);
77:
78: switch (ret) {
79: case -EREMOTEIO:
80: set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
81: break;
82: case -ENOMEM:
83: case -EBADMSG:
84: nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
85: break;
86: default:
87: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
88: nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
89: break;
90: }
91: return IRQ_HANDLED;
92: }
93:
94: static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
95: {
96: struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
97:
98: if (!drv_data)
99: return -ENODEV;
100:
]
|
| 294/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-SPI driver: SPI interface related functions
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/module.h>
9: #include <linux/interrupt.h>
10: #include <linux/nfc.h>
11: #include <linux/of_irq.h>
12: #include <net/nfc/nci.h>
13: #include <net/nfc/nci_core.h>
14: #include <linux/spi/spi.h>
15: #include "nfcmrvl.h"
16:
17: #define SPI_WAIT_HANDSHAKE 1
18:
19: struct nfcmrvl_spi_drv_data {
20: unsigned long flags;
21: struct spi_device *spi;
22: struct nci_spi *nci_spi;
23: struct completion handshake_completion;
24: struct nfcmrvl_private *priv;
25: };
26:
27: static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
28: {
29: struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
30: struct sk_buff *skb;
31:
32: /*
33: * Special case where we are waiting for SPI_INT deassertion to start a
34: * transfer.
35: */
36: if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
37: complete(&drv_data->handshake_completion);
38: return IRQ_HANDLED;
39: }
40:
41: /* Normal case, SPI_INT deasserted by slave to trigger a master read */
42:
43: skb = nci_spi_read(drv_data->nci_spi);
44: if (!skb) {
45: nfc_err(&drv_data->spi->dev, "failed to read spi packet");
46: return IRQ_HANDLED;
47: }
48:
49: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
50: nfc_err(&drv_data->spi->dev, "corrupted RX packet");
51:
52: return IRQ_HANDLED;
53: }
54:
55: static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
56: {
57: return 0;
58: }
59:
60: static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
61: {
62: return 0;
63: }
64:
65: static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
66: struct sk_buff *skb)
67: {
68: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
69: int err;
70:
71: /* Reinit completion for slave handshake */
72: reinit_completion(&drv_data->handshake_completion);
73: set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
74:
75: /*
76: * Append a dummy byte at the end of SPI frame. This is due to a
77: * specific DMA implementation in the controller
78: */
79: skb_put(skb, 1);
80:
81: /* Send the SPI packet */
82: err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
83: skb);
84: if (err)
85: nfc_err(priv->dev, "spi_send failed %d", err);
86:
87: return err;
88: }
89:
90: static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv,
91: const void *param)
92: {
93: struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
94: const struct nfcmrvl_fw_spi_config *config = param;
95:
96: drv_data->nci_spi->xfer_speed_hz = config->clk;
97: }
98:
99: static const struct nfcmrvl_if_ops spi_ops = {
100: .nci_open = nfcmrvl_spi_nci_open,
]
|
| 295/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/uart.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-only
2: /*
3: * Marvell NFC-over-UART driver
4: *
5: * Copyright (C) 2015, Marvell International Ltd.
6: */
7:
8: #include <linux/delay.h>
9: #include <linux/device.h>
10: #include <linux/err.h>
11: #include <linux/gpio/consumer.h>
12: #include <linux/module.h>
13: #include <linux/of.h>
14: #include <linux/printk.h>
15:
16: #include <net/nfc/nci.h>
17: #include <net/nfc/nci_core.h>
18:
19: #include "nfcmrvl.h"
20:
21: static unsigned int hci_muxed;
22: static unsigned int flow_control;
23: static unsigned int break_control;
24:
25: /*
26: * NFCMRVL NCI OPS
27: */
28:
29: static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv)
30: {
31: return 0;
32: }
33:
34: static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv)
35: {
36: return 0;
37: }
38:
39: static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
40: struct sk_buff *skb)
41: {
42: struct nci_uart *nu = priv->drv_data;
43:
44: return nu->ops.send(nu, skb);
45: }
46:
47: static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
48: const void *param)
49: {
50: struct nci_uart *nu = priv->drv_data;
51: const struct nfcmrvl_fw_uart_config *config = param;
52:
53: nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
54: config->flow_control);
55: }
56:
57: static const struct nfcmrvl_if_ops uart_ops = {
58: .nci_open = nfcmrvl_uart_nci_open,
59: .nci_close = nfcmrvl_uart_nci_close,
60: .nci_send = nfcmrvl_uart_nci_send,
61: .nci_update_config = nfcmrvl_uart_nci_update_config
62: };
63:
64: static int nfcmrvl_uart_parse_dt(struct device_node *node,
65: struct nfcmrvl_platform_data *pdata,
66: struct device *dev)
67: {
68: struct device_node *matched_node;
69: struct gpio_desc *reset_gpio;
70: int ret;
71:
72: matched_node = of_get_compatible_child(node, "marvell,nfc-uart");
73: if (!matched_node) {
74: matched_node = of_get_compatible_child(node, "mrvl,nfc-uart");
75: if (!matched_node)
76: return -ENODEV;
77: }
78:
79: ret = nfcmrvl_parse_dt(matched_node, pdata);
80: if (ret < 0) {
81: pr_err("Failed to get generic entries\n");
82: of_node_put(matched_node);
83: return ret;
84: }
85:
86: pdata->flow_control = of_property_read_bool(matched_node, "flow-control");
87: pdata->break_control = of_property_read_bool(matched_node, "break-control");
88:
89: reset_gpio = devm_fwnode_gpiod_get_optional(dev,
90: of_fwnode_handle(matched_node),
91: "reset", GPIOD_OUT_HIGH,
92: "nfcmrvl_reset_n");
93: if (IS_ERR(reset_gpio)) {
94: of_node_put(matched_node);
95: return PTR_ERR(reset_gpio);
96: }
97: pdata->reset_gpio = reset_gpio;
98:
99: of_node_put(matched_node);
100:
]
|
| 296/5 |
2026/08/26 15:20 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 32445
output: 84
thoughts: 47
Thoughts: Okay, let's dive into this. Here's how I'd approach this task, thinking through it myself:
**Probing Device Drivers: A Quick Investigation**
Alright, I'm thinking I need to understand what's happening during the probe phase across different bus drivers. My initial thought is to zero in on `usb.c`, `i2c.c`, and `spi.c`. I need to figure out how these drivers interact with devices during their initialization.
So, first things first, I'll go straight into the `spi.c` and `i2c.c` files and have a look at their respective probe functions. What initialization steps are they taking? What are they looking for to match with a device? Are there any obvious differences in their approaches? I'll probably be focusing on the key structures and functions related to device registration and driver binding. I'm looking for the essential logic that makes the hardware connection. I bet I'll see some common patterns, even if the specifics vary. Understanding those differences is key.
|
| 297/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/i2c.c FirstLine:150 LineCount:80]
Results: map[Contents: 150: {
151: }
152:
153: static const struct nfcmrvl_if_ops i2c_ops = {
154: .nci_open = nfcmrvl_i2c_nci_open,
155: .nci_close = nfcmrvl_i2c_nci_close,
156: .nci_send = nfcmrvl_i2c_nci_send,
157: .nci_update_config = nfcmrvl_i2c_nci_update_config,
158: };
159:
160: static int nfcmrvl_i2c_parse_dt(struct device_node *node,
161: struct nfcmrvl_platform_data *pdata)
162: {
163: int ret;
164:
165: ret = nfcmrvl_parse_dt(node, pdata);
166: if (ret < 0) {
167: pr_err("Failed to get generic entries\n");
168: return ret;
169: }
170:
171: if (of_property_read_bool(node, "i2c-int-falling"))
172: pdata->irq_polarity = IRQF_TRIGGER_FALLING;
173: else
174: pdata->irq_polarity = IRQF_TRIGGER_RISING;
175:
176: ret = irq_of_parse_and_map(node, 0);
177: if (!ret) {
178: pr_err("Unable to get irq\n");
179: return -EINVAL;
180: }
181: pdata->irq = ret;
182:
183: return 0;
184: }
185:
186: static int nfcmrvl_i2c_probe(struct i2c_client *client)
187: {
188: const struct nfcmrvl_platform_data *pdata;
189: struct nfcmrvl_i2c_drv_data *drv_data;
190: struct nfcmrvl_platform_data config;
191: int ret;
192:
193: if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
194: nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
195: return -ENODEV;
196: }
197:
198: drv_data = devm_kzalloc(&client->dev, sizeof(*drv_data), GFP_KERNEL);
199: if (!drv_data)
200: return -ENOMEM;
201:
202: drv_data->i2c = client;
203: drv_data->dev = &client->dev;
204: drv_data->priv = NULL;
205:
206: i2c_set_clientdata(client, drv_data);
207:
208: pdata = client->dev.platform_data;
209:
210: if (!pdata && client->dev.of_node)
211: if (nfcmrvl_i2c_parse_dt(client->dev.of_node, &config) == 0)
212: pdata = &config;
213:
214: if (!pdata)
215: return -EINVAL;
216:
217: /* Request the read IRQ */
218: ret = devm_request_threaded_irq(&drv_data->i2c->dev, pdata->irq,
219: NULL, nfcmrvl_i2c_int_irq_thread_fn,
220: pdata->irq_polarity | IRQF_ONESHOT,
221: "nfcmrvl_i2c_int", drv_data);
222: if (ret < 0) {
223: nfc_err(&drv_data->i2c->dev,
224: "Unable to register IRQ handler\n");
225: return ret;
226: }
227:
228: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
229: drv_data, &i2c_ops,
]
|
| 298/5 |
2026/08/26 15:20 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/spi.c FirstLine:110 LineCount:80]
Results: map[Contents: 110:
111: ret = nfcmrvl_parse_dt(node, pdata);
112: if (ret < 0) {
113: pr_err("Failed to get generic entries\n");
114: return ret;
115: }
116:
117: ret = irq_of_parse_and_map(node, 0);
118: if (!ret) {
119: pr_err("Unable to get irq\n");
120: return -EINVAL;
121: }
122: pdata->irq = ret;
123:
124: return 0;
125: }
126:
127: static int nfcmrvl_spi_probe(struct spi_device *spi)
128: {
129: const struct nfcmrvl_platform_data *pdata;
130: struct nfcmrvl_platform_data config;
131: struct nfcmrvl_spi_drv_data *drv_data;
132: int ret = 0;
133:
134: drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL);
135: if (!drv_data)
136: return -ENOMEM;
137:
138: drv_data->spi = spi;
139: drv_data->priv = NULL;
140: spi_set_drvdata(spi, drv_data);
141:
142: pdata = spi->dev.platform_data;
143:
144: if (!pdata && spi->dev.of_node)
145: if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0)
146: pdata = &config;
147:
148: if (!pdata)
149: return -EINVAL;
150:
151: ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
152: NULL, nfcmrvl_spi_int_irq_thread_fn,
153: IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
154: "nfcmrvl_spi_int", drv_data);
155: if (ret < 0) {
156: nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler");
157: return -ENODEV;
158: }
159:
160: drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
161: drv_data, &spi_ops,
162: &drv_data->spi->dev,
163: pdata);
164: if (IS_ERR(drv_data->priv))
165: return PTR_ERR(drv_data->priv);
166:
167: drv_data->priv->support_fw_dnld = true;
168:
169: drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10,
170: drv_data->priv->ndev);
171:
172: /* Init completion for slave handshake */
173: init_completion(&drv_data->handshake_completion);
174: return 0;
175: }
176:
177: static void nfcmrvl_spi_remove(struct spi_device *spi)
178: {
179: struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi);
180:
181: nfcmrvl_nci_unregister_dev(drv_data->priv);
182: }
183:
184: static const struct of_device_id of_nfcmrvl_spi_match[] __maybe_unused = {
185: { .compatible = "marvell,nfc-spi", },
186: {},
187: };
188: MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match);
189:
]
|
| 299/5 |
2026/08/26 15:20 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 35358
output: 55
thoughts: 1431
Thoughts: **Tracing the Probe and Registration Process**
Okay, let's break this down step-by-step to understand the crash. I'm focusing on the interaction between the probe, registration, opening, and completion phases, particularly looking at the data structures.
First, within `nfcmrvl_probe` in `usb.c`, the critical part is `nfcmrvl_nci_register_dev`. This function allocates `priv`, which is our private data structure. Inside this function we can see the initialization and setup of `priv`, importantly:
1. `priv` is allocated.
2. `priv->drv_data = drv_data;`
3. `priv->if_ops = ops;`
4. `priv->dev = dev;`
5. `priv->phy = phy;`
6. `priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, ...)`
7. `nci_set_drvdata(priv->ndev, priv);`
8. `rc = nci_register_device(priv->ndev);`
9. `nfcmrvl_chip_halt(priv);`
10. returns `priv`.
Notice the critical `nci_register_device(priv->ndev)` call. This registers the NFC device with the NFC core, effectively making it visible to userspace, exposed through netlink. This is where the race begins! Userspace *could* immediately call `nfc_dev_up()`, leading to `nci_dev_up()`, `nci_open_device()`, and finally `nfcmrvl_nci_open(ndev)`. This path gets us `priv` through `nci_get_drvdata(ndev)` which is all set up. Inside `nfcmrvl_nci_open`, `nfcmrvl_usb_nci_open` is called.
Inside `nfcmrvl_usb_nci_open`, the USB interface is taken, URBs are submitted, and `nfcmrvl_bulk_complete` will be called on completion.
Now, consider `nfcmrvl_bulk_complete`. Inside this completion routine, `drv_data` is retrieved from `urb->context` as expected. Here we can see the critical point:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_usb_drv_data *drv_data = urb->context;
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))
return;
if (!urb->status) {
struct sk_buff *skb;
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
GFP_ATOMIC);
...
```
The problem is immediately obvious: in `nfcmrvl_probe`, `drv_data->priv = priv;` happens *after* `nfcmrvl_nci_register_dev()`. The crash happens because the userspace can call `nfc_dev_up` *while* `nci_register_device` is executing (or immediately after it exposes the device), *before* `nfcmrvl_nci_register_dev` returns, and thus before `drv_data->priv = priv;` executes. So, when `nfcmrvl_bulk_complete` is called, `drv_data->priv` is still NULL!
We have a race condition! Even though `priv` is properly initialized *within* `nfcmrvl_nci_register_dev`, and then registered with the NFC core, the critical link `drv_data->priv` isn't established until *after* the registration call returns to the probe function. The userspace part, however, can act as soon as the device is registered.
Okay, let's now confirm my understanding and make sure it has not been used somewhere else in the codebase and is not being set at all.
|
| 300/5 |
2026/08/26 15:20 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nfc/nfcmrvl/usb.c IncludeSnippetLines:5 Name:nfcmrvl_usb_drv_data::priv]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_bulk_complete SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:71 SourceSnippet: 66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_bulk_complete SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:78 SourceSnippet: 73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_disconnect SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:342 SourceSnippet: 337: if (!drv_data)
338: return;
339:
340: nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
341:
342: nfcmrvl_nci_unregister_dev(drv_data->priv);
343:
344: usb_set_intfdata(drv_data->intf, NULL);
345: }
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_probe SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:325 SourceSnippet: 320: priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:nfcmrvl_probe SourceFile:drivers/nfc/nfcmrvl/usb.c SourceLine:326 SourceSnippet: 321: &intf->dev, &config);
322: if (IS_ERR(priv))
323: return PTR_ERR(priv);
324:
325: drv_data->priv = priv;
326: drv_data->priv->support_fw_dnld = false;
327:
328: usb_set_intfdata(intf, drv_data);
329:
330: return 0;
331: }
]] TruncatedOutput:false]
|
| 301/5 |
2026/08/26 15:20 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 38915
output: 24
thoughts: 401
Thoughts: **Analyzing `usb.c` and Related Flags**
Okay, let's dive into this `usb.c` code. My immediate goal is to understand the usage of `priv` and `drv_data` within it. I want to see how these data structures are being used in relation to the USB interface.
I've zeroed in on the `nfcmrvl_resume` function. Specifically, I'm examining this block of code:
```c
static int nfcmrvl_resume(struct usb_interface *intf)
{
struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
int err = 0;
nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
if (--drv_data->suspend_count)
return 0;
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
goto done;
if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
if (err) {
clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
goto failed;
}
nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
}
...
```
I'm paying close attention to the `drv_data` structure retrieved using `usb_get_intfdata`. Then, I'm really focusing on the `if` statement, and in particular, `!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)`. The flag `NFCMRVL_NCI_RUNNING` is being used here, and it's important to understand what it signals.
Aha! Now I see something significant, line 410, is the same condition I just described.
The use of this `NFCMRVL_NCI_RUNNING` flag and its relationship to `priv->flags` is significant. My understanding is that `NFCMRVL_NCI_RUNNING` is actually a flag defined somewhere and stored within `priv->flags`.
So, it's time to perform a broader search across the entire kernel to see where `NFCMRVL_NCI_RUNNING` is defined, used, set, and cleared, and therefore understand its impact within the driver. That should give me the full context of what's going on here.
|
| 302/5 |
2026/08/26 15:20 |
tool |
grepper |
0m
Args: map[Expression:NFCMRVL_NCI_RUNNING]
Results: map[Output:drivers/nfc/nfcmrvl/main.c=17=static int nfcmrvl_nci_open(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-21-
drivers/nfc/nfcmrvl/main.c:22: if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-23- return 0;
--
drivers/nfc/nfcmrvl/main.c-30- if (err)
drivers/nfc/nfcmrvl/main.c:31: clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
drivers/nfc/nfcmrvl/main.c-32-
--
drivers/nfc/nfcmrvl/main.c=36=static int nfcmrvl_nci_close(struct nci_dev *ndev)
--
drivers/nfc/nfcmrvl/main.c-39-
drivers/nfc/nfcmrvl/main.c:40: if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-41- return 0;
--
drivers/nfc/nfcmrvl/main.c=194=int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
--
drivers/nfc/nfcmrvl/main.c-212-
drivers/nfc/nfcmrvl/main.c:213: if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
drivers/nfc/nfcmrvl/main.c-214- nci_recv_frame(priv->ndev, skb);
--
drivers/nfc/nfcmrvl/nfcmrvl.h=13=struct gpio_desc;
--
drivers/nfc/nfcmrvl/nfcmrvl.h-15-/* Define private flags: */
drivers/nfc/nfcmrvl/nfcmrvl.h:16:#define NFCMRVL_NCI_RUNNING 1
drivers/nfc/nfcmrvl/nfcmrvl.h-17-#define NFCMRVL_PHY_ERROR 2
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-64-
drivers/nfc/nfcmrvl/usb.c:65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-66- return;
--
drivers/nfc/nfcmrvl/usb.c=400=static int nfcmrvl_resume(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-409-
drivers/nfc/nfcmrvl/usb.c:410: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-411- goto done;
]
|
| 303/5 |
2026/08/26 15:20 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 40809
output: 21
thoughts: 31
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Checking USB Bulk Transfer States**
Okay, so I'm diving back into this USB bulk transfer implementation. First, I need to make sure I'm crystal clear on the current operational state. I'm going to quickly verify the state of both `NFCMRVL_USB_BULK_RUNNING` and `NFCMRVL_USB_SUSPENDING`. This is fundamental – understanding whether the bulk transfers are actively running is key, as is knowing if the system is currently in the process of suspending. The interaction of these two flags is likely critical to preventing race conditions or data corruption. I want to rule out any immediate problems before I dig deeper into the specifics.
|
| 304/5 |
2026/08/26 15:20 |
tool |
grepper |
0m
Args: map[Expression:NFCMRVL_USB_]
Results: map[Output:drivers/nfc/nfcmrvl/usb.c=21=MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
drivers/nfc/nfcmrvl/usb.c-22-
drivers/nfc/nfcmrvl/usb.c:23:#define NFCMRVL_USB_BULK_RUNNING 1
drivers/nfc/nfcmrvl/usb.c:24:#define NFCMRVL_USB_SUSPENDING 2
drivers/nfc/nfcmrvl/usb.c-25-
--
drivers/nfc/nfcmrvl/usb.c=43=static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
--
drivers/nfc/nfcmrvl/usb.c-48- spin_lock_irqsave(&drv_data->txlock, flags);
drivers/nfc/nfcmrvl/usb.c:49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-50- if (!rv)
--
drivers/nfc/nfcmrvl/usb.c=57=static void nfcmrvl_bulk_complete(struct urb *urb)
--
drivers/nfc/nfcmrvl/usb.c-83-
drivers/nfc/nfcmrvl/usb.c:84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
drivers/nfc/nfcmrvl/usb.c-85- return;
--
drivers/nfc/nfcmrvl/usb.c=166=static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/usb.c-180-
drivers/nfc/nfcmrvl/usb.c:181: set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-182- nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
--
drivers/nfc/nfcmrvl/usb.c=197=static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/usb.c-203-
drivers/nfc/nfcmrvl/usb.c:204: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-205-
--
drivers/nfc/nfcmrvl/usb.c=348=static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
--
drivers/nfc/nfcmrvl/usb.c-358- if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
drivers/nfc/nfcmrvl/usb.c:359: set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-360- spin_unlock_irq(&drv_data->txlock);
--
drivers/nfc/nfcmrvl/usb.c=400=static int nfcmrvl_resume(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-412-
drivers/nfc/nfcmrvl/usb.c:413: if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
drivers/nfc/nfcmrvl/usb.c-414- err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
drivers/nfc/nfcmrvl/usb.c-415- if (err) {
drivers/nfc/nfcmrvl/usb.c:416: clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-417- goto failed;
--
drivers/nfc/nfcmrvl/usb.c-424- nfcmrvl_play_deferred(drv_data);
drivers/nfc/nfcmrvl/usb.c:425: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-426- spin_unlock_irq(&drv_data->txlock);
--
drivers/nfc/nfcmrvl/usb.c-433- spin_lock_irq(&drv_data->txlock);
drivers/nfc/nfcmrvl/usb.c:434: clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c-435- spin_unlock_irq(&drv_data->txlock);
]
|
| 305/5 |
2026/08/26 15:20 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 42346
output: 25
thoughts: 6101
Thoughts: **Analyzing the NFCMRVL USB Driver's Concurrency and Data Structures**
Okay, here's what I've been thinking about this NFCMRVL driver code. I've spotted a significant bug: the code was incorrectly checking the `drv_data->flags` field in `usb.c` against `NFCMRVL_NCI_RUNNING`, when it should have been checking `priv->flags`. This misdirected bit test was occurring in `nfcmrvl_bulk_complete` and `nfcmrvl_resume`. This is a pretty straightforward fix – just swap the variables.
The fix I see in the diff does seem correct - it addresses the key issue. Instead of passing the `drv_data` structure as the `urb->context` when submitting the bulk URB, we're passing the `priv` pointer. This makes a lot of sense. Previously, there was a race condition in `nfcmrvl_probe`. The `drv_data->priv` member was not initialized until *after* the `nci_register_device` call. If userspace triggered a device open *during* the probe, the URB completion callback was using an uninitialized `drv_data->priv`. By passing `priv` directly as the `urb->context`, the completion callback will get the correct `priv` pointer *regardless* of when the device is opened.
I was initially a bit concerned about other potential race conditions, especially around the `nfcmrvl_tx_complete` function. But after stepping through that code again, I've confirmed that this function already correctly uses `priv` retrieved from `ndev`. It doesn't rely on `drv_data->priv`. So, that part is fine.
I also looked at the close and resume paths. In `nfcmrvl_nci_close`, the code first clears the `NFCMRVL_NCI_RUNNING` flag and then kills the USB traffic. The urb completion code, now, correctly checks the flag using the `priv->flags`. This means it's safe - if the device is closing, we're not going to be processing URB completions that should have been discarded.
In `nfcmrvl_resume`, the fix makes things safer. There's no longer any possibility of a race condition using the uninitialized `drv_data->priv`. I also confirm that the `drv_data->priv` is fully initialized during resume and disconnect, so there's no way that `drv_data->priv` can be NULL, since `usb_set_intfdata(intf, drv_data)` is only called after `drv_data->priv = priv;` in `nfcmrvl_probe`.
The main advantage of passing `priv` as `urb->context` is to avoid the race condition in the probe and to simplify code. There might be a small performance cost of dereferencing `drv_data` again in the bulk complete callback, but this should be negligible. This approach is more robust because it ensures that the critical `priv` pointer, containing all the NFC device information, is always available to the URB completion callback, irrespective of the timing of the probe and device open.
I did a quick scan through the git log for `drivers/nfc/nfcmrvl/usb.c` and related files to look for similar concurrency bug fixes. There are certainly fixes, and it's always good to be thorough and check whether this is a pattern.
Overall, the patch looks like a clean and effective fix. It addresses a real race condition, doesn't introduce any new obvious problems, and simplifies the data flow in the URB completion path. It also makes the code more robust. I'm satisfied.
|
| 306/5 |
2026/08/26 15:21 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/nfc/nfcmrvl]
Results: map[Output:567ab7727adb nfc: Use named initializers for struct i2c_device_id
dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
c49a9eb650d5 NFC: nfcmrvl: Replace strcpy() with strscpy()
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
41cb08555c41 treewide, timers: Rename from_timer() to timer_container_of()
8fa7292fee5c treewide: Switch/rename to timer_delete[_sync]()
a26892ee1297 nfc: mrvl: Don't use "proxy" headers
5f60d5f6bbc1 move asm/unaligned.h to linux/unaligned.h
]
|
| 307/5 |
2026/08/26 15:21 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 49550
output: 34
thoughts: 26
Thoughts: Here's a summary of my thought process, as requested:
**Diving Deep into the NFC-MRVL USB Driver**
Okay, so we need to understand a potential regression or issue in the `drivers/nfc/nfcmrvl/usb.c` file. My initial thought is to go straight to the source and examine the Git history. I'm going to dig into the version control, specifically focusing on the `drivers/nfc/nfcmrvl/usb.c` file. I need to go back further than just the recent commits, to see the evolution of this driver over time. Understanding its past will help me pinpoint *when* a change might have introduced this problem, or if it's the cumulative effect of a series of modifications. This historical analysis is crucial. I need to trace the lineage and see what's been altered in the past so I can figure out why things aren't working as expected.
|
| 308/5 |
2026/08/26 15:21 |
tool |
git-log |
0m
Args: map[Count:20 PathPrefix:drivers/nfc/nfcmrvl/usb.c]
Results: map[Output:dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
8a4d480702b7 nfc: nfcmrvl: Fix memory leak in nfcmrvl_play_deferred
2695503729da nfc: mrvl: constify static nfcmrvl_if_ops
2c95e6c7e558 nfc: mrvl: reduce the scope of local variables
a58224040f2d nfc: mrvl: remove useless "continue" at end of loop
be3d162ad023 nfc: mrvl: use SPDX-License-Identifier
4dd649d130c6 NFC: nfcmrvl: fix kernel-doc syntax in file headers
c3953a3c2d31 NFC: nfcmrvl: fix gpio-handling regression
24b2068e26c6 NFC: nfcmrvl_usb: use irqsave() in USB's complete callback
0d1ca88bbfdf NFC: nfcmrvl_usb: use interface as phy device
59ae1d127ac0 networking: introduce and use skb_put_data()
58d34aa677ec NFC: nfcmrvl: configure head/tail room values per low level drivers
3194c6870158 NFC: nfcmrvl: add firmware download support
fb101c0e9cc6 NFC: nfcmrvl: remove unneeded version defines
43465b7af2e6 NFC: nfcmrvl: small fix in USB driver
dc14bdef8762 NFC: nfcmrvl: add platform_data and DT configuration
8a81a96bd116 NFC: nfcmrvl: update USB device id
e1bf80c2a598 NFC: nfcmrvl: update nci recv frame API
f1f1a7da2b38 NFC: nfcmrvl: add support of HCI-based transport
]
|
| 309/5 |
2026/08/26 15:21 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 50338
output: 22
thoughts: 32
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating NFC USB Driver Implementations**
Alright, so I'm thinking about the best approach for this NFC USB driver situation. Before I reinvent the wheel, I should really see what existing solutions are out there and how they've handled similar challenges. The logical place to start digging is probably within the Linux kernel itself. Specifically, I should be looking at the `drivers/nfc/` and `net/nfc/` directories. That's where I'd expect to find examples of other NFC driver implementations, perhaps for different hardware, but likely dealing with analogous USB interaction paradigms. I need to understand how they manage things like initialization, data transfer, error handling, and power management in the context of USB. Looking at their code and design decisions will give me a solid foundation and hopefully, spark some insight for my work. This'll help me avoid common pitfalls and leverage proven techniques. Ultimately, I'm aiming for a clean, efficient, and robust driver.
|
| 310/5 |
2026/08/26 15:21 |
tool |
codesearch-dir-index |
0m
Args:
Results: map[Files:[mei_phy.c mei_phy.h nfcsim.c port100.c trf7970a.c virtual_ncidev.c] Subdirs:[fdp microread nfcmrvl nxp-nci pn533 pn544 s3fwrn5 st-nci st21nfca st95hf]]
|
| 311/5 |
2026/08/26 15:21 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 50755
output: 24
thoughts: 3453
Thoughts: **Patch Analysis and Architectural Review**
Okay, let's break this down. My initial assessment is that this patch aims to address a potential NULL pointer dereference and a flag management bug within the `nfcmrvl` USB driver. The core strategy is to shift how context is passed around and how critical data is accessed, and I think I understand the rationale.
First, I see the proposal cleverly avoids the classic sentinel guard `if (!drv_data->priv) return;`. Instead of adding that as a band-aid, it fundamentally changes how data is made accessible. URBs are now submitted with `priv` as `urb->context` in `nfcmrvl_usb_nci_open()`, and `nfcmrvl_bulk_complete` receives `priv` directly via `urb->context`. This removes the need to assume `drv_data->priv` is already valid and shifts to a guaranteed valid pointer. Within `nfcmrvl_bulk_complete`, we safely access `drv_data` via `priv->drv_data`. The old bug that was causing a NULL dereference is now fixed.
The most important part of this patch is the correction of a flag management bug. It seems that `NFCMRVL_NCI_RUNNING` was always meant to be a flag on `priv->flags`, but due to a coding error, it was being checked against `drv_data->flags`. Specifically, `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)` was actually testing `NFCMRVL_USB_BULK_RUNNING`. This patch fixes this by directly accessing `priv->flags` to check `NFCMRVL_NCI_RUNNING`, which is correct according to the `nfcmrvl.h` header file. The `priv->flags` were always the correct location to check!
Let me verify that the `priv` pointer is always correctly set up. Looking at `nfcmrvl_probe`, `drv_data->priv` is indeed set after the `nfcmrvl_nci_register_dev` call. Importantly, even if the probe succeeds, the `disconnect` and `resume` callbacks still rely on the `drv_data` pointer retrieved through `usb_get_intfdata`. So, this `priv` assignment and the subsequent use of `drv_data->priv` are correct. This is good because `drv_data->priv` needs to be valid across these callback functions.
Regarding the note about the SPI driver, it's clear that this patch *only* touches `usb.c`. The note suggests a similar race condition might exist in the SPI driver, which is worth a separate investigation. However, this patch itself doesn't address that, and I don't see any changes related to SPI in the diff.
Now, let's systematically review the changes in the patch diff. It appears that there are four key changes. The first change, in `nfcmrvl_bulk_complete`, modifies the signature to use `priv` directly, and we check `priv->flags` instead of `drv_data->flags`. This is good, and it fixes the original problem. The second change, in `nfcmrvl_submit_bulk_urb`, changes to passing `priv` rather than `drv_data` and accesses `priv->drv_data`. The third change updates the call site in `nfcmrvl_usb_nci_open` to pass `priv`. The fourth change updates the call sites in `nfcmrvl_resume` to pass `priv`. All of these changes correctly modify the passing of `priv` and `drv_data` and remove the bug that was in place.
I am satisfied that this patch looks clean, correct, and architecturally sound, specifically for the USB driver. It addresses the identified NULL pointer dereference issue, fixes the flag management bug, and maintains the necessary functionality without introducing any new issues. I don't see any red flags here, so it looks like it's ready to go.
|
| 312/5 |
2026/08/26 15:22 |
tool |
grepper |
0m
Args: map[Expression:nfcmrvl_submit_bulk_urb]
Results: map[Output:drivers/nfc/nfcmrvl/usb.c=102=static int
drivers/nfc/nfcmrvl/usb.c:103:nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
drivers/nfc/nfcmrvl/usb.c-104-{
--
drivers/nfc/nfcmrvl/usb.c=166=static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
--
drivers/nfc/nfcmrvl/usb.c-176-
drivers/nfc/nfcmrvl/usb.c:177: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
drivers/nfc/nfcmrvl/usb.c-178- if (err)
--
drivers/nfc/nfcmrvl/usb.c-181- set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
drivers/nfc/nfcmrvl/usb.c:182: nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
drivers/nfc/nfcmrvl/usb.c-183-
--
drivers/nfc/nfcmrvl/usb.c=400=static int nfcmrvl_resume(struct usb_interface *intf)
--
drivers/nfc/nfcmrvl/usb.c-413- if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
drivers/nfc/nfcmrvl/usb.c:414: err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
drivers/nfc/nfcmrvl/usb.c-415- if (err) {
--
drivers/nfc/nfcmrvl/usb.c-419-
drivers/nfc/nfcmrvl/usb.c:420: nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
drivers/nfc/nfcmrvl/usb.c-421- }
]
|
| 313/5 |
2026/08/26 15:22 |
llm |
patch-reviewer |
0m
Model:
Tokens:
input: 55674
output: 19
thoughts: 831
Thoughts: **Code Review Assessment: Refactoring `nfcmrvl_submit_bulk_urb`**
Okay, so I've just updated all four call sites of `nfcmrvl_submit_bulk_urb`. Now, I want to make sure I haven't introduced any subtle regressions or issues related to flag usage. My initial thought is to cross-check `usb.c` for any remaining references to `NFCMRVL_NCI_RUNNING`, and confirm they've been updated to use `priv->flags`. Lines 65 and 410 check out, both using the correct `&priv->flags`. Good start.
Next, I need to thoroughly audit the usage of `drv_data->flags` and `priv->flags` throughout `usb.c` to prevent any potential mixing of concerns. Let's step through each instance of `drv_data->flags`:
1. `test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags)` (line 49) - Correct!
2. `if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)` (line 84) - Correct!
3. `set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)` (line 181) - Correct!
4. `clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)` (line 204) - Correct!
5. `set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags)` (line 359) - Correct!
6. `if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)` (line 413) - Correct!
7. `clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)` (line 416) - Correct!
8. `clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags)` (line 425) - Correct!
9. `clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags)` (line 434) - Correct!
Alright, it's looking like everything is strictly segregated now. `drv_data->flags` *only* handles `NFCMRVL_USB_*` related flags (`BULK_RUNNING` and `SUSPENDING`), and `priv->flags` is strictly responsible for `NFCMRVL_NCI_*` flags (`NFCMRVL_NCI_RUNNING`). This is exactly what I was aiming for.
Now, let's step back and look at the larger architectural implications. No quick-fix sentinel band-aids here, which is good; the core invariant is more robust. This patch shifts the burden of ensuring the validity of `priv` onto the URB context, guaranteeing the necessary pointers (`priv`, `priv->ndev`, `priv->drv_data`) are ready when URB completion happens. No change to LIFO unwinding symmetry. And, I don't see any use-after-free or lifecycle leaks. `priv` and `drv_data` have matching lifecycles; created during probe, freed during unregister/disconnect.
Finally, does it align well with the existing subsystem? Yes, this change actually aligns `nfcmrvl_bulk_complete` with how `nfcmrvl_tx_complete` already works by getting the `priv` and accessing `priv->drv_data` from that. So it's idiomatically consistent.
After this comprehensive review against all the criteria, it's time to give the go-ahead. My review is approved. No comments required.
Time to call the `set-results` tool with `ReviewApproved: true` and `ReviewComments: []`.
|
| 314/5 |
2026/08/26 15:22 |
tool |
set-results |
0m
Args: map[ReviewApproved:true ReviewComments:[]]
Results: map[ReviewApproved:true ReviewComments:[]]
|
| 315/4 |
2026/08/26 15:22 |
action |
evaluate-patch-review |
0m
Results: map[NeedsIteration:false ReviewFeedback:]
|
| 316/1 |
2026/08/26 15:22 |
action |
apply-patch |
0m
Results:
|
| 317/1 |
2026/08/26 15:22 |
action |
init-style-items |
0m
Results:
|
| 318/1 |
2026/08/26 15:22 |
loop |
|
0m
|
| 319/2 |
2026/08/26 15:22 |
iteration |
0 |
0m
|
| 320/3 |
2026/08/26 15:22 |
agent |
patch-formatter |
0m
Model:
Instruction:
You are an expert Linux kernel developer tasked with formatting a kernel patch.
Your objective is purely formatting: you must ensure the patch complies with the kernel's coding style,
conforms to the surrounding code rules, and passes checkpatch.pl, while preserving the code logic exactly as it is.
You should stop once the requested formatting changes are done and checkpatch.pl is happy.
Do not question the requested changes unless they are obviously wrong.
If the code already conforms to the requested changes, surrounding code rules,
and checkpatch.pl is happy, you should just finish your task.
WARNING: The clang-format tool may break the formatting of the surrounding code (like manual alignment).
Use it with caution. We want to make the change fit into the existing formatting as much as possible.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The current patch diff is:
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);
Use the provided tools to format the patch.
Reply: The patch has been verified and already conforms to the Linux kernel coding style and surrounding code conventions. `checkpatch.pl` reports 0 errors and 0 warnings. No formatting changes are needed.
|
| 321/4 |
2026/08/26 15:22 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 5957
output: 19
thoughts: 29
Thoughts: Okay, let's get down to brass tacks.
**Reviewing the Patch: A Quick Sanity Check**
Alright, before I dive too deep, my instinct is to start with a quick assessment of this patch and the `checkpatch` output. We all know how important it is to keep things clean and consistent. So, I'm going to fire up the `checkpatch` tool and see what it has to say. Then, I'll take a look at the `patch-diff` output to get a clean view of the changes. This'll give me a preliminary feel for the overall quality and direction of the patch, and flag any immediate red flags. This is just the initial scan, of course, but it helps set the stage before I get into the real nitty-gritty.
|
| 322/4 |
2026/08/26 15:22 |
tool |
checkpatch |
0m
Results: map[Output:total: 0 errors, 0 warnings, 90 lines checked
Your patch has no obvious style problems and is ready for submission.
]
|
| 323/4 |
2026/08/26 15:22 |
tool |
patch-diff |
0m
Error:
|
| 324/4 |
2026/08/26 15:22 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 6233
output: 15
thoughts: 16
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Investigating a Code Change with `patch-diff`**
Alright, I'm diving into this code change. First, the plan of attack is clear: I need to quickly grasp the nature of this "patch-diff". I'll start with the most basic method. I'm going to call `patch-diff` with the specified file to generate the diff output. Knowing the file will allow me to assess the changes directly. No fancy abstractions, just a clear view of the delta. This will give me a baseline, a raw look at what's been altered. This raw diff will be the starting point of my investigations. From there I can begin to assess risk, stability and any integration problems.
|
| 325/4 |
2026/08/26 15:22 |
tool |
patch-diff |
0m
Args:
Results: map[Output: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
@@ -49,97 +49,99 @@ static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
if (!rv)
drv_data->tx_in_flight++;
spin_unlock_irqrestore(&drv_data->txlock, flags);
return rv;
}
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");
}
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
return;
usb_anchor_urb(urb, &drv_data->bulk_anchor);
usb_mark_last_busy(drv_data->udev);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
/* -EPERM: urb is being killed;
* -ENODEV: device got disconnected
*/
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p failed to resubmit (%d)\n", urb, -err);
usb_unanchor_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;
int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
if (!drv_data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(drv_data->udev,
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;
usb_mark_last_busy(drv_data->udev);
usb_anchor_urb(urb, &drv_data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
"urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
}
@@ -166,27 +168,27 @@ static void nfcmrvl_tx_complete(struct urb *urb)
static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
int err;
err = usb_autopm_get_interface(drv_data->intf);
if (err)
return err;
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;
failed:
usb_autopm_put_interface(drv_data->intf);
return err;
}
static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
@@ -393,47 +395,48 @@ static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
/* Cleanup the rest deferred urbs. */
while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
kfree(urb->setup_packet);
usb_free_urb(urb);
}
}
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);
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);
nfcmrvl_play_deferred(drv_data);
clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
spin_unlock_irq(&drv_data->txlock);
return 0;
failed:
usb_scuttle_anchored_urbs(&drv_data->deferred);
done:
spin_lock_irq(&drv_data->txlock);
clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
spin_unlock_irq(&drv_data->txlock);
return err;
}
#endif
]
|
| 326/4 |
2026/08/26 15:22 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 8876
output: 41
thoughts: 65
Thoughts: **Code Style Review in nfcmrvl/usb.c**
Alright, let's dive in. I need to get a grip on the recent changes in `drivers/nfc/nfcmrvl/usb.c`. Before I even start thinking about the functionality, I'll need to assess the surrounding code style and conventions of the modified lines to guarantee consistency. The file is, obviously, a core driver component. So, to ensure maintainability, especially for my colleagues and any future contributions, I should ensure the changes adhere to the established style. Let me read through lines 45 to 145 and then jump down to lines 390 through 440. Those segments will likely highlight the pertinent changes I'm looking for. Time to assess the implementation details.
|
| 327/4 |
2026/08/26 15:22 |
tool |
read-file |
0m
Args: map[File:drivers/nfc/nfcmrvl/usb.c FirstLine:45 LineCount:100]
Results: map[Contents: 45: unsigned long flags;
46: int rv;
47:
48: spin_lock_irqsave(&drv_data->txlock, flags);
49: rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
50: if (!rv)
51: drv_data->tx_in_flight++;
52: spin_unlock_irqrestore(&drv_data->txlock, flags);
53:
54: return rv;
55: }
56:
57: static void nfcmrvl_bulk_complete(struct urb *urb)
58: {
59: struct nfcmrvl_usb_drv_data *drv_data = urb->context;
60: int err;
61:
62: dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
63: urb, urb->status, urb->actual_length);
64:
65: if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
66: return;
67:
68: if (!urb->status) {
69: struct sk_buff *skb;
70:
71: skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
72: GFP_ATOMIC);
73: if (!skb) {
74: nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
75: } else {
76: skb_put_data(skb, urb->transfer_buffer,
77: urb->actual_length);
78: if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
79: nfc_err(&drv_data->udev->dev,
80: "corrupted Rx packet\n");
81: }
82: }
83:
84: if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
85: return;
86:
87: usb_anchor_urb(urb, &drv_data->bulk_anchor);
88: usb_mark_last_busy(drv_data->udev);
89:
90: err = usb_submit_urb(urb, GFP_ATOMIC);
91: if (err) {
92: /* -EPERM: urb is being killed;
93: * -ENODEV: device got disconnected
94: */
95: if (err != -EPERM && err != -ENODEV)
96: nfc_err(&drv_data->udev->dev,
97: "urb %p failed to resubmit (%d)\n", urb, -err);
98: usb_unanchor_urb(urb);
99: }
100: }
101:
102: static int
103: nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
104: {
105: struct urb *urb;
106: unsigned char *buf;
107: unsigned int pipe;
108: int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
109:
110: if (!drv_data->bulk_rx_ep)
111: return -ENODEV;
112:
113: urb = usb_alloc_urb(0, mem_flags);
114: if (!urb)
115: return -ENOMEM;
116:
117: buf = kmalloc(size, mem_flags);
118: if (!buf) {
119: usb_free_urb(urb);
120: return -ENOMEM;
121: }
122:
123: pipe = usb_rcvbulkpipe(drv_data->udev,
124: drv_data->bulk_rx_ep->bEndpointAddress);
125:
126: usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
127: nfcmrvl_bulk_complete, drv_data);
128:
129: urb->transfer_flags |= URB_FREE_BUFFER;
130:
131: usb_mark_last_busy(drv_data->udev);
132: usb_anchor_urb(urb, &drv_data->bulk_anchor);
133:
134: err = usb_submit_urb(urb, mem_flags);
135: if (err) {
136: if (err != -EPERM && err != -ENODEV)
137: nfc_err(&drv_data->udev->dev,
138: "urb %p submission failed (%d)\n", urb, -err);
139: usb_unanchor_urb(urb);
140: }
141:
142: usb_free_urb(urb);
143:
144: return err;
]
|
| 328/4 |
2026/08/26 15:22 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 10641
output: 40
thoughts: 1851
Thoughts: **Code Review and Patch Analysis**
Okay, first things first, I needed to confirm that I'm looking at the *original* file contents via `read-file` and not a pre-edited version in the workspace. The output from `read-file` definitely showed the original file contents. Specifically, line 59 confirmed this. Then, using `patch-diff`, I checked the output of that against the user provided diff. They match! Good, I'm working with the correct data.
Now, let's carefully go over the diff and the lines that are modified. It looks like the changes focus on altering the function signatures and uses of `struct nfcmrvl_usb_drv_data` to incorporate an intermediate `struct nfcmrvl_private` pointer. The intent is likely to streamline access to the `drv_data` within the existing functions, which makes the code cleaner.
Here's the breakdown of what I see in the patch, line by line:
1. In `nfcmrvl_bulk_complete`, the code is changing from using `drv_data` directly from the `urb->context` to retrieving it from the `priv` pointer: `struct nfcmrvl_private *priv = urb->context;` and then `struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;`. All subsequent uses of `drv_data` now derive from `priv`. The conditional `if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))` also changes to use `priv->flags`. And the calls to the `nci_skb_alloc` and `nfcmrvl_nci_recv_frame` function have been updated to use the `priv` pointer.
2. In `nfcmrvl_submit_bulk_urb`, the function signature changes from taking `struct nfcmrvl_usb_drv_data *drv_data` to `struct nfcmrvl_private *priv`. Inside the function, `drv_data` is re-derived from `priv` and used in the code. `usb_fill_bulk_urb` has updated the function parameters using the newly introduced `priv`.
3. In `nfcmrvl_usb_nci_open`, the call to `nfcmrvl_submit_bulk_urb` is updated to pass `priv` instead of `drv_data`.
4. In `nfcmrvl_resume`, the code also incorporates `struct nfcmrvl_private *priv = drv_data->priv;` and the calls to `nfcmrvl_submit_bulk_urb` now take `priv` as input. The conditional `if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))` also changes to use `priv->flags`.
After checking for style, formatting, and other potential issues, and running `checkpatch`, the patch looks clean. No errors or warnings reported. The code conforms to Linux kernel coding style. The changes are logically sound. It's safe to proceed.
|
| 329/3 |
2026/08/26 15:22 |
action |
test-patch-inplace |
0m
Results: map[PatchDiff: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);
TestError:]
|
| 330/3 |
2026/08/26 15:22 |
action |
run-checkpatch |
0m
Results: map[CheckpatchOutput:total: 0 errors, 0 warnings, 90 lines checked
Your patch has no obvious style problems and is ready for submission.
NeedRefinement:false]
|
| 331/1 |
2026/08/26 15:22 |
agent |
fixes-finder |
0m
Model:
Results: map[FixesHash:f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa]
Instruction:
You are an experienced Linux kernel developer tasked with identifying the commit
that introduced the bug being fixed. Identifying the correct buggy commit is crucial
for proper kernel maintenance (backporting to stable trees, etc.).
Your investigation strategy:
1. Examine the patch that fixes the bug. Use git tools (like git-log or git-blame)
to trace the history of the lines or functions modified by the patch.
2. Analyze the stack trace in the crash report. Identify the key files and functions
involved in the crash and investigate their history to see when the problematic
logic was introduced.
3. Compare the bug explanation with the commit history to find the point where
the described logic error first appeared.
A bug is typically introduced when a piece of code is first written, or when
a refactoring changed its logic in a way that introduced the bug.
Trace the history of relevant symbols or find when specific code patterns were introduced/removed.
You must provide exactly one bug-introducing commit hash.
If you are unable to confidently determine the bug-introducing commit after investigation,
return an empty string rather than guessing.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The crash is:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The explanation of the root cause is:
The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
The patch that fixes the bug is:
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);
Search for the commit(s) that introduced this bug.
|
| 332/2 |
2026/08/26 15:22 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 11871
output: 29
thoughts: 92
Thoughts: **Analyzing the NFC Driver's USB Interaction**
Okay, let's dive into this. My initial move is to examine the patch itself, then immediately cross-reference it with the `git log` and `git blame` outputs for `drivers/nfc/nfcmrvl/usb.c`. I need to pinpoint the history behind this file, especially focusing on key functions related to the USB bulk transfers within this NFC driver. I'm thinking specifically about `nfcmrvl_submit_bulk_urb`, `nfcmrvl_bulk_complete`, and how they've evolved over time. The best tools for that, of course, are `git-log` and `git-blame`. I'll use those to quickly identify when these specific components, and the file as a whole, were introduced, changed, and by whom. Understanding the commit history will give me crucial context for this patch.
|
| 333/2 |
2026/08/26 15:22 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/nfc/nfcmrvl/usb.c]
Results: map[Output:dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
8a4d480702b7 nfc: nfcmrvl: Fix memory leak in nfcmrvl_play_deferred
2695503729da nfc: mrvl: constify static nfcmrvl_if_ops
2c95e6c7e558 nfc: mrvl: reduce the scope of local variables
a58224040f2d nfc: mrvl: remove useless "continue" at end of loop
be3d162ad023 nfc: mrvl: use SPDX-License-Identifier
4dd649d130c6 NFC: nfcmrvl: fix kernel-doc syntax in file headers
c3953a3c2d31 NFC: nfcmrvl: fix gpio-handling regression
24b2068e26c6 NFC: nfcmrvl_usb: use irqsave() in USB's complete callback
]
|
| 334/2 |
2026/08/26 15:22 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 12470
output: 39
thoughts: 27
Thoughts: Here's my thinking process:
**Diving into the NFC Driver's USB Implementation**
Okay, so I need to understand what's been going on with `drivers/nfc/nfcmrvl/usb.c`. My immediate thought is to check the commit history. I want to see how this file has evolved over time, which changes were made, and by whom. Using `git blame` or looking at earlier commits will give me a timeline of modifications. This is usually the first step to understand any issues or recent changes that might be relevant to a problem. This should quickly give me an overview of the code's history and any potentially contentious areas.
|
| 335/2 |
2026/08/26 15:22 |
tool |
git-blame |
0m
Args: map[End:80 File:drivers/nfc/nfcmrvl/usb.c Start:50]
Results: map[Output:f26e30cc6b50b 50) if (!rv)
f26e30cc6b50b 51) drv_data->tx_in_flight++;
f26e30cc6b50b 52) spin_unlock_irqrestore(&drv_data->txlock, flags);
f26e30cc6b50b 53)
f26e30cc6b50b 54) return rv;
f26e30cc6b50b 55) }
f26e30cc6b50b 56)
f26e30cc6b50b 57) static void nfcmrvl_bulk_complete(struct urb *urb)
f26e30cc6b50b 58) {
f26e30cc6b50b 59) struct nfcmrvl_usb_drv_data *drv_data = urb->context;
f26e30cc6b50b 60) int err;
f26e30cc6b50b 61)
e1bf80c2a5989 62) dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
f26e30cc6b50b 63) urb, urb->status, urb->actual_length);
f26e30cc6b50b 64)
f26e30cc6b50b 65) if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
f26e30cc6b50b 66) return;
f26e30cc6b50b 67)
f26e30cc6b50b 68) if (!urb->status) {
2c95e6c7e558f 69) struct sk_buff *skb;
2c95e6c7e558f 70)
e1bf80c2a5989 71) skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
e1bf80c2a5989 72) GFP_ATOMIC);
e1bf80c2a5989 73) if (!skb) {
e1bf80c2a5989 74) nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
e1bf80c2a5989 75) } else {
59ae1d127ac0a 76) skb_put_data(skb, urb->transfer_buffer,
59ae1d127ac0a 77) urb->actual_length);
e1bf80c2a5989 78) if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
e1bf80c2a5989 79) nfc_err(&drv_data->udev->dev,
e1bf80c2a5989 80) "corrupted Rx packet\n");
]
|
| 336/2 |
2026/08/26 15:22 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 13553
output: 27
thoughts: 38
Thoughts: Here's my analysis of the provided prompt, summarized in the first person, assuming a high level of expertise:
**Diving into the Code**
Alright, let's get down to it. The initial instruction clearly indicates a focus on specific code fragments, which I'll assume are identifiers or references, probably hashes or IDs, such as `f26e30cc6b50b` and `e1bf80c2a5989`. My immediate goal is to understand their function and relationship to one another, likely to diagnose a problem, understand a new feature, or refactor existing code. I need to get intimate with these. The use of "probe functions" suggests a hands-on, exploratory approach. I'll need to use tools to inspect these functions. This means tracing execution, examining call stacks, perhaps looking at input/output parameters, and stepping through the code line by line. I’m thinking dynamic analysis is the name of the game here. From this I can build a mental model of their operational behavior.
|
| 337/2 |
2026/08/26 15:22 |
tool |
git-show |
0m
Args: map[Commit:f26e30cc6b50b]
Results: map[Output:commit f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa
Author: Amitkumar Karwar <akarwar@marvell.com>
Date: Mon Jan 6 12:58:19 2014 -0800
NFC: nfcmrvl: Initial commit for Marvell NFC driver
This patch adds NFC support for Marvell 8897 NFC-over-USB chipset.
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index c1fb20603338..fe20e1cc0545 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -58,5 +58,6 @@ config NFC_PORT100
source "drivers/nfc/pn544/Kconfig"
source "drivers/nfc/microread/Kconfig"
+source "drivers/nfc/nfcmrvl/Kconfig"
endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index c715fe8582a8..56ab822ba03d 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -9,5 +9,6 @@ obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o
obj-$(CONFIG_NFC_SIM) += nfcsim.o
obj-$(CONFIG_NFC_PORT100) += port100.o
+obj-$(CONFIG_NFC_MRVL) += nfcmrvl/
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig
new file mode 100644
index 000000000000..5e18afd9abe2
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/Kconfig
@@ -0,0 +1,23 @@
+config NFC_MRVL
+ tristate "Marvell NFC driver support"
+ depends on NFC_NCI
+ help
+ The core driver to support Marvell NFC devices.
+
+ This driver is required if you want to support
+ Marvell NFC device 8897.
+
+ Say Y here to compile Marvell NFC driver into the kernel or
+ say M to compile it as module.
+
+config NFC_MRVL_USB
+ tristate "Marvell NFC-over-USB driver"
+ depends on NFC_MRVL && USB
+ help
+ Marvell NFC-over-USB driver.
+
+ This driver provides support for Marvell NFC-over-USB devices:
+ 8897.
+
+ Say Y here to compile support for Marvell NFC-over-USB driver
+ into the kernel or say M to compile it as module.
diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile
new file mode 100644
index 000000000000..97a0de72dc01
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for NFCMRVL NCI based NFC driver
+#
+
+nfcmrvl-y += main.o
+obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o
+
+nfcmrvl_usb-y += usb.o
+obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
new file mode 100644
index 000000000000..396fc270ffc3
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -0,0 +1,145 @@
+/*
+ * Marvell NFC driver: major functions
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+#define VERSION "1.0"
+
+static int nfcmrvl_nci_open(struct nci_dev *ndev)
+{
+ struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+ int err;
+
+ if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+ return 0;
+
+ err = priv->if_ops->nci_open(priv);
+
+ if (err)
+ clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
+
+ return err;
+}
+
+static int nfcmrvl_nci_close(struct nci_dev *ndev)
+{
+ struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+
+ if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+ return 0;
+
+ priv->if_ops->nci_close(priv);
+
+ return 0;
+}
+
+static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+
+ nfc_info(priv->dev, "send entry, len %d\n", skb->len);
+
+ skb->dev = (void *)ndev;
+
+ if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+ return -EBUSY;
+
+ return priv->if_ops->nci_send(priv, skb);
+}
+
+static struct nci_ops nfcmrvl_nci_ops = {
+ .open = nfcmrvl_nci_open,
+ .close = nfcmrvl_nci_close,
+ .send = nfcmrvl_nci_send,
+};
+
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
+ struct nfcmrvl_if_ops *ops,
+ struct device *dev)
+{
+ struct nfcmrvl_private *priv;
+ int rc;
+ u32 protocols;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ priv->drv_data = drv_data;
+ priv->if_ops = ops;
+ priv->dev = dev;
+
+ protocols = NFC_PROTO_JEWEL_MASK
+ | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
+ | NFC_PROTO_ISO14443_MASK
+ | NFC_PROTO_ISO14443_B_MASK
+ | NFC_PROTO_NFC_DEP_MASK;
+
+ priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0);
+ if (!priv->ndev) {
+ nfc_err(dev, "nci_allocate_device failed");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nci_set_drvdata(priv->ndev, priv);
+
+ rc = nci_register_device(priv->ndev);
+ if (rc) {
+ nfc_err(dev, "nci_register_device failed %d", rc);
+ nci_free_device(priv->ndev);
+ return ERR_PTR(rc);
+ }
+
+ nfc_info(dev, "registered with nci successfully\n");
+ return priv;
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
+
+void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
+{
+ struct nci_dev *ndev = priv->ndev;
+
+ nci_unregister_device(ndev);
+ nci_free_device(ndev);
+ kfree(priv);
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
+
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count)
+{
+ struct sk_buff *skb;
+
+ skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb_put(skb, count), data, count);
+ nci_recv_frame(priv->ndev, skb);
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h
new file mode 100644
index 000000000000..a007175f77fe
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/nfcmrvl.h
@@ -0,0 +1,42 @@
+/**
+ * Marvell NFC driver
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+/* Define private flags: */
+#define NFCMRVL_NCI_RUNNING 1
+
+#define NFCMRVL_NCI_MAX_EVENT_SIZE 260
+
+struct nfcmrvl_private {
+ struct nci_dev *ndev;
+ unsigned long flags;
+ void *drv_data;
+ struct device *dev;
+ struct nfcmrvl_if_ops *if_ops;
+};
+
+struct nfcmrvl_if_ops {
+ int (*nci_open) (struct nfcmrvl_private *priv);
+ int (*nci_close) (struct nfcmrvl_private *priv);
+ int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb);
+};
+
+void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count);
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
+ struct nfcmrvl_if_ops *ops,
+ struct device *dev);
diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c
new file mode 100644
index 000000000000..3221ca37d6c9
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/usb.c
@@ -0,0 +1,459 @@
+/**
+ * Marvell NFC-over-USB driver: USB interface related functions
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+#define VERSION "1.0"
+
+static struct usb_device_id nfcmrvl_table[] = {
+ { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
+
+#define NFCMRVL_USB_BULK_RUNNING 1
+#define NFCMRVL_USB_SUSPENDING 2
+
+struct nfcmrvl_usb_drv_data {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ unsigned long flags;
+ struct work_struct waker;
+ struct usb_anchor tx_anchor;
+ struct usb_anchor bulk_anchor;
+ struct usb_anchor deferred;
+ int tx_in_flight;
+ /* protects tx_in_flight */
+ spinlock_t txlock;
+ struct usb_endpoint_descriptor *bulk_tx_ep;
+ struct usb_endpoint_descriptor *bulk_rx_ep;
+ int suspend_count;
+ struct nfcmrvl_private *priv;
+};
+
+static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
+{
+ unsigned long flags;
+ int rv;
+
+ spin_lock_irqsave(&drv_data->txlock, flags);
+ rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+ if (!rv)
+ drv_data->tx_in_flight++;
+ spin_unlock_irqrestore(&drv_data->txlock, flags);
+
+ return rv;
+}
+
+static void nfcmrvl_bulk_complete(struct urb *urb)
+{
+ struct nfcmrvl_usb_drv_data *drv_data = urb->context;
+ int err;
+
+ dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d",
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
+ return;
+
+ if (!urb->status) {
+ if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer,
+ urb->actual_length) < 0)
+ nfc_err(&drv_data->udev->dev, "corrupted Rx packet");
+ }
+
+ if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
+ return;
+
+ usb_anchor_urb(urb, &drv_data->bulk_anchor);
+ usb_mark_last_busy(drv_data->udev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ nfc_err(&drv_data->udev->dev,
+ "urb %p failed to resubmit (%d)", urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int
+nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
+{
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
+
+ if (!drv_data->bulk_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(drv_data->udev,
+ drv_data->bulk_rx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
+ nfcmrvl_bulk_complete, drv_data);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_mark_last_busy(drv_data->udev);
+ usb_anchor_urb(urb, &drv_data->bulk_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err) {
+ if (err != -EPERM && err != -ENODEV)
+ nfc_err(&drv_data->udev->dev,
+ "urb %p submission failed (%d)", urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void nfcmrvl_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct nci_dev *ndev = (struct nci_dev *)skb->dev;
+ struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+ struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+
+ nfc_info(priv->dev, "urb %p status %d count %d",
+ urb, urb->status, urb->actual_length);
+
+ spin_lock(&drv_data->txlock);
+ drv_data->tx_in_flight--;
+ spin_unlock(&drv_data->txlock);
+
+ kfree(urb->setup_packet);
+ kfree_skb(skb);
+}
+
+static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
+{
+ struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+ int err;
+
+ err = usb_autopm_get_interface(drv_data->intf);
+ if (err)
+ return err;
+
+ drv_data->intf->needs_remote_wakeup = 1;
+
+ err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
+ if (err)
+ goto failed;
+
+ set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+ nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
+
+ usb_autopm_put_interface(drv_data->intf);
+ return 0;
+
+failed:
+ usb_autopm_put_interface(drv_data->intf);
+ return err;
+}
+
+static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
+{
+ usb_kill_anchored_urbs(&drv_data->bulk_anchor);
+}
+
+static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
+{
+ struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+ int err;
+
+ cancel_work_sync(&drv_data->waker);
+
+ clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+
+ nfcmrvl_usb_stop_traffic(drv_data);
+ usb_kill_anchored_urbs(&drv_data->tx_anchor);
+ err = usb_autopm_get_interface(drv_data->intf);
+ if (err)
+ goto failed;
+
+ drv_data->intf->needs_remote_wakeup = 0;
+ usb_autopm_put_interface(drv_data->intf);
+
+failed:
+ usb_scuttle_anchored_urbs(&drv_data->deferred);
+ return 0;
+}
+
+static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
+ struct sk_buff *skb)
+{
+ struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+ struct urb *urb;
+ unsigned int pipe;
+ int err;
+
+ if (!drv_data->bulk_tx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(drv_data->udev,
+ drv_data->bulk_tx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
+ nfcmrvl_tx_complete, skb);
+
+ err = nfcmrvl_inc_tx(drv_data);
+ if (err) {
+ usb_anchor_urb(urb, &drv_data->deferred);
+ schedule_work(&drv_data->waker);
+ err = 0;
+ goto done;
+ }
+
+ usb_anchor_urb(urb, &drv_data->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ if (err != -EPERM && err != -ENODEV)
+ nfc_err(&drv_data->udev->dev,
+ "urb %p submission failed (%d)", urb, -err);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ } else {
+ usb_mark_last_busy(drv_data->udev);
+ }
+
+done:
+ usb_free_urb(urb);
+ return err;
+}
+
+static struct nfcmrvl_if_ops usb_ops = {
+ .nci_open = nfcmrvl_usb_nci_open,
+ .nci_close = nfcmrvl_usb_nci_close,
+ .nci_send = nfcmrvl_usb_nci_send,
+};
+
+static void nfcmrvl_waker(struct work_struct *work)
+{
+ struct nfcmrvl_usb_drv_data *drv_data =
+ container_of(work, struct nfcmrvl_usb_drv_data, waker);
+ int err;
+
+ err = usb_autopm_get_interface(drv_data->intf);
+ if (err)
+ return;
+
+ usb_autopm_put_interface(drv_data->intf);
+}
+
+static int nfcmrvl_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ struct nfcmrvl_usb_drv_data *drv_data;
+ struct nfcmrvl_private *priv;
+ int i;
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ nfc_info(&udev->dev, "intf %p id %p", intf, id);
+
+ drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
+ if (!drv_data)
+ return -ENOMEM;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!drv_data->bulk_tx_ep &&
+ usb_endpoint_is_bulk_out(ep_desc)) {
+ drv_data->bulk_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!drv_data->bulk_rx_ep &&
+ usb_endpoint_is_bulk_in(ep_desc)) {
+ drv_data->bulk_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep)
+ return -ENODEV;
+
+ drv_data->udev = udev;
+ drv_data->intf = intf;
+
+ INIT_WORK(&drv_data->waker, nfcmrvl_waker);
+ spin_lock_init(&drv_data->txlock);
+
+ init_usb_anchor(&drv_data->tx_anchor);
+ init_usb_anchor(&drv_data->bulk_anchor);
+ init_usb_anchor(&drv_data->deferred);
+
+ priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops,
+ &drv_data->udev->dev);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
+
+ drv_data->priv = priv;
+ priv->dev = &drv_data->udev->dev;
+
+ usb_set_intfdata(intf, drv_data);
+
+ return 0;
+}
+
+static void nfcmrvl_disconnect(struct usb_interface *intf)
+{
+ struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+
+ if (!drv_data)
+ return;
+
+ nfc_info(&drv_data->udev->dev, "intf %p", intf);
+
+ nfcmrvl_nci_unregister_dev(drv_data->priv);
+
+ usb_set_intfdata(drv_data->intf, NULL);
+}
+
+#ifdef CONFIG_PM
+static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+
+ nfc_info(&drv_data->udev->dev, "intf %p", intf);
+
+ if (drv_data->suspend_count++)
+ return 0;
+
+ spin_lock_irq(&drv_data->txlock);
+ if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
+ set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+ spin_unlock_irq(&drv_data->txlock);
+ } else {
+ spin_unlock_irq(&drv_data->txlock);
+ drv_data->suspend_count--;
+ return -EBUSY;
+ }
+
+ nfcmrvl_usb_stop_traffic(drv_data);
+ usb_kill_anchored_urbs(&drv_data->tx_anchor);
+
+ return 0;
+}
+
+static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
+{
+ struct urb *urb;
+ int err;
+
+ while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err)
+ break;
+
+ drv_data->tx_in_flight++;
+ }
+ usb_scuttle_anchored_urbs(&drv_data->deferred);
+}
+
+static int nfcmrvl_resume(struct usb_interface *intf)
+{
+ struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+ int err = 0;
+
+ nfc_info(&drv_data->udev->dev, "intf %p", intf);
+
+ if (--drv_data->suspend_count)
+ return 0;
+
+ if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
+ goto done;
+
+ if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
+ err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
+ if (err) {
+ clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+ goto failed;
+ }
+
+ nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
+ }
+
+ spin_lock_irq(&drv_data->txlock);
+ nfcmrvl_play_deferred(drv_data);
+ clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+ spin_unlock_irq(&drv_data->txlock);
+
+ return 0;
+
+failed:
+ usb_scuttle_anchored_urbs(&drv_data->deferred);
+done:
+ spin_lock_irq(&drv_data->txlock);
+ clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+ spin_unlock_irq(&drv_data->txlock);
+
+ return err;
+}
+#endif
+
+static struct usb_driver nfcmrvl_usb_driver = {
+ .name = "nfcmrvl",
+ .probe = nfcmrvl_probe,
+ .disconnect = nfcmrvl_disconnect,
+#ifdef CONFIG_PM
+ .suspend = nfcmrvl_suspend,
+ .resume = nfcmrvl_resume,
+ .reset_resume = nfcmrvl_resume,
+#endif
+ .id_table = nfcmrvl_table,
+ .supports_autosuspend = 1,
+ .disable_hub_initiated_lpm = 1,
+ .soft_unbind = 1,
+};
+module_usb_driver(nfcmrvl_usb_driver);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
]
|
| 338/2 |
2026/08/26 15:22 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 22385
output: 27
thoughts: 179
Thoughts: **Analyzing Potential Race Conditions in nfcmrvl Driver Initialization**
Okay, let's break this down. My initial concern stems from the sequence of operations within that `f26e30cc6b50b` commit. Specifically, the assignment of `drv_data->priv = priv;` *after* `nfcmrvl_nci_register_dev()`. Then, the `nfcmrvl_bulk_complete()` function does a check with `!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)`. And finally, the `nfcmrvl_nci_recv_frame(drv_data->priv, ...)` function attempts to dereference `drv_data->priv`. This smells like a potential race condition. If the device registration completes and `nfcmrvl_nci_recv_frame()` is called *before* `drv_data->priv` is assigned, we have a problem – a null pointer dereference, to be precise.
I need to confirm if this timing is possible. To do that I need to look at commit `e1bf80c2a5989` and other commits in the `drivers/nfc/nfcmrvl/` directory to see if there is any other related code or if it resolves this. Let's see what that commit says.
|
| 339/2 |
2026/08/26 15:22 |
tool |
git-show |
0m
Args: map[Commit:e1bf80c2a5989]
Results: map[Output:commit e1bf80c2a59895f12f596608b91856bab1c562bb
Author: Vincent Cuissard <cuissard@marvell.com>
Date: Thu Jun 11 11:25:44 2015 +0200
NFC: nfcmrvl: update nci recv frame API
Update internal nci recv frame API to use skbuff phy management
to generic part of the driver.
Signed-off-by: Vincent Cuissard <cuissard@marvell.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
index 48d8b00744df..e7f579b2d987 100644
--- a/drivers/nfc/nfcmrvl/main.c
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -153,16 +153,8 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
}
EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
-int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count)
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
{
- struct sk_buff *skb;
-
- skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
-
- memcpy(skb_put(skb, count), data, count);
-
if (priv->hci_muxed) {
if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE &&
skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) {
@@ -175,9 +167,15 @@ int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count)
}
}
- nci_recv_frame(priv->ndev, skb);
+ if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+ nci_recv_frame(priv->ndev, skb);
+ else {
+ /* Drop this packet since nobody wants it */
+ kfree_skb(skb);
+ return 0;
+ }
- return count;
+ return 0;
}
EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h
index b04cddd57388..7a10dabaf1b2 100644
--- a/drivers/nfc/nfcmrvl/nfcmrvl.h
+++ b/drivers/nfc/nfcmrvl/nfcmrvl.h
@@ -58,7 +58,7 @@ struct nfcmrvl_if_ops {
};
void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
-int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count);
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
struct nfcmrvl_if_ops *ops,
struct device *dev,
diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c
index df534b90468b..f81861af5b25 100644
--- a/drivers/nfc/nfcmrvl/usb.c
+++ b/drivers/nfc/nfcmrvl/usb.c
@@ -69,18 +69,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 sk_buff *skb;
int err;
- dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d",
+ 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))
return;
if (!urb->status) {
- if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer,
- urb->actual_length) < 0)
- nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n");
+ skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
+ GFP_ATOMIC);
+ if (!skb) {
+ nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
+ } else {
+ memcpy(skb_put(skb, urb->actual_length),
+ urb->transfer_buffer, urb->actual_length);
+ if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
+ nfc_err(&drv_data->udev->dev,
+ "corrupted Rx packet\n");
+ }
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
]
|
| 340/2 |
2026/08/26 15:22 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 24510
output: 26
thoughts: 2407
Thoughts: **Analysis of the nfcmrvl Driver's Race Condition and Macro Collision**
Okay, here's the breakdown of this issue. My initial thought process has led me to identify a crucial race condition combined with a subtle macro collision in the Marvell NFC driver. Let me walk you through my analysis step-by-step.
Firstly, I noticed that in the initial commit `f26e30cc6b50b`, the `drv_data->priv = priv;` assignment, which sets the private data pointer, occurs *after* the call to `nfcmrvl_nci_register_dev()`. This function, crucially, calls `nci_register_device()`, which exposes the device to userspace. This creates a race window: userspace interaction can happen before the `priv` pointer is fully initialized.
Then, there's the `nfcmrvl_bulk_complete()` function. In `f26e30cc6b50b`, this handler is designed to process the completion of USB URBs. Within it, I see this:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
If `drv_data->priv` is `NULL`, any dereference of it will immediately trigger a crash. The critical point is this: due to the race condition, there's a window where `drv_data->priv` *could* be NULL in the completion handler. This is caused by `nfcmrvl_nci_register_dev()` registering the device *before* `drv_data->priv` is assigned.
Now, let's zoom in on the macro collision part. The code checks `drv_data->flags` against `NFCMRVL_NCI_RUNNING`, which is defined as `1`. However, `NFCMRVL_USB_BULK_RUNNING` is also defined as `1`. Effectively, there is a typo in the check: The code is actually checking `drv_data->flags` against `NFCMRVL_USB_BULK_RUNNING` instead of the intended `priv->flags` to check if the device is actually ready to receive data. This means that a condition that should protect against a crash has a "false positive".
Furthermore, in `nfcmrvl_usb_nci_open()`, the code submits two bulk URBs and sets the `NFCMRVL_USB_BULK_RUNNING` flag between them. Therefore, if the *first* URB completes immediately before the `NFCMRVL_USB_BULK_RUNNING` flag has been set, the check accidentally protects against the dereference because the "typo" check succeeds, and it returns safely. However, when the *second* URB completes, the flag *is* set, the "typo" check passes, and the code dereferences the `NULL` pointer `drv_data->priv`, triggering the crash.
I've confirmed that this exact sequence, including the race and the macro collision, existed in the initial commit `f26e30cc6b50b`. I also ran a git log search to see if there were any later modifications that created or removed any part of this logic; none were present. So, this bug was introduced in the initial code, and it remained there, latent, until triggered by specific timing conditions or external factors, like a fuzzer or specific hardware setups.
|
| 341/2 |
2026/08/26 15:22 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/nfc/nfcmrvl/]
Results: map[Output:567ab7727adb nfc: Use named initializers for struct i2c_device_id
dba69cba4a5d nfc: nfcmrvl: refactor endpoint lookup
253350dbf3e7 nfc: nfcmrvl: convert to gpio descriptors
c49a9eb650d5 NFC: nfcmrvl: Replace strcpy() with strscpy()
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
41cb08555c41 treewide, timers: Rename from_timer() to timer_container_of()
8fa7292fee5c treewide: Switch/rename to timer_delete[_sync]()
a26892ee1297 nfc: mrvl: Don't use "proxy" headers
5f60d5f6bbc1 move asm/unaligned.h to linux/unaligned.h
]
|
| 342/2 |
2026/08/26 15:23 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 27970
output: 51
thoughts: 271
Thoughts: **Analyzing the Potential Bug Introduction**
Okay, let's see if this commit, `f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa`, is the culprit behind this driver issue. I need to confirm its role, specifically if it's the one that introduced the bug.
First, let's confirm the commit details. Based on the data, `f26e30cc6b50b` is part of the full hash. So, I see that the author is Amitkumar Karwar, and the commit date is January 6, 2014. The subject is "NFC: nfcmrvl: Initial commit for Marvell NFC driver." Seems right.
Now, just to be absolutely sure, I'll quickly verify that `f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa` is indeed a valid commit hash, specifically that it has 12 or more hexadecimal characters. It does!
Now that I've confirmed that this commit is the right one, I need to call the `set-results` function and pass the commit hash `f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa` as an argument.
|
| 343/2 |
2026/08/26 15:23 |
tool |
set-results |
0m
Args: map[FixesHash:f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa]
Results: map[FixesHash:f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa]
|
| 344/1 |
2026/08/26 15:23 |
action |
format-fixes |
0m
Results: map[Fixes:map[AuthorEmail:akarwar@marvell.com AuthorName:Amitkumar Karwar Hash:f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa Title:NFC: nfcmrvl: Initial commit for Marvell NFC driver]]
|
| 345/1 |
2026/08/26 15:23 |
action |
get-maintainers |
0m
Results: map[Recipients:[map[Email:bartosz.golaszewski@oss.qualcomm.com Name:Bartosz Golaszewski To:false] map[Email:david@ixit.cz Name:David Heidelberg To:true] map[Email:johan@kernel.org Name:Johan Hovold To:false] map[Email:kuba@kernel.org Name:Jakub Kicinski To:false] map[Email:linusw@kernel.org Name:Linus Walleij To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:oe-linux-nfc@lists.linux.dev Name: To:true] map[Email:xujialu@vimux.org Name:Jialu Xu To:false] map[Email:akarwar@marvell.com Name:Amitkumar Karwar To:true]]]
|
| 346/1 |
2026/08/26 15:23 |
action |
get-recent-commits |
0m
Results: map[RecentCommits:nfc: nfcmrvl: refactor endpoint lookup
nfc: nfcmrvl: convert to gpio descriptors
nfc: nfcmrvl: Fix memory leak in nfcmrvl_play_deferred
nfc: mrvl: constify static nfcmrvl_if_ops
nfc: mrvl: reduce the scope of local variables
nfc: mrvl: remove useless "continue" at end of loop
nfc: mrvl: use SPDX-License-Identifier
NFC: nfcmrvl: fix kernel-doc syntax in file headers
NFC: nfcmrvl: fix gpio-handling regression
NFC: nfcmrvl_usb: use irqsave() in USB's complete callback
NFC: nfcmrvl_usb: use interface as phy device
networking: introduce and use skb_put_data()
NFC: nfcmrvl: configure head/tail room values per low level drivers
NFC: nfcmrvl: add firmware download support
NFC: nfcmrvl: remove unneeded version defines
NFC: nfcmrvl: small fix in USB driver
NFC: nfcmrvl: add platform_data and DT configuration
NFC: nfcmrvl: update USB device id
NFC: nfcmrvl: update nci recv frame API
NFC: nfcmrvl: add support of HCI-based transport
]
|
| 347/1 |
2026/08/26 15:23 |
agent |
description-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.
The one-line summary should describe the change being made, rather than mention the tool that
detected the bug.
The description must not contain lines starting with '#' because they will dropped by git as comments.
The description must not contain lines starting with '--' or '---' (including inline code diffs)
because they may confuse git/patch utilities.
Your final reply should contain only the text of the commit description.
The one-line summary must be not longer than 72 characters.
IMPORTANT: Do not wrap lines manually (e.g., at 80 characters); we will reformat the text
automatically, so keep paragraphs as single lines without newlines.
Generally try to phrase the description without mentioning syzkaller
(avoid phrases like "the bug was triggered by syzkaller" or "the bug was triggered by fuzzer", etc).
How the bug was triggered is generally an irrelevant detail.
Any bug triggered by a fuzzer can also be triggered by a malicious user, or a buggy program.
If the crash is reported by a sanitizer (e.g., KASAN, KMSAN, lockdep), include the relevant
parts of the sanitizer output to illustrate the problem. Exclude less relevant sections,
as the stack trace can be very long. Describe the execution path that leads to the manifestation
of the kernel bug.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000004: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
CPU: 1 UID: 0 PID: 5853 Comm: syz-executor410 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
Call Trace:
<IRQ>
__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
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:bytes_is_nonzero mm/kasan/generic.c:98 [inline]
RIP: 0010:memory_is_nonzero mm/kasan/generic.c:115 [inline]
RIP: 0010:memory_is_poisoned_n mm/kasan/generic.c:140 [inline]
RIP: 0010:memory_is_poisoned mm/kasan/generic.c:172 [inline]
RIP: 0010:check_region_inline mm/kasan/generic.c:191 [inline]
RIP: 0010:kasan_check_range+0x97/0x2c0 mm/kasan/generic.c:200
Code: 00 fc ff df 4d 8d 34 19 4d 89 f4 4d 29 dc 49 83 fc 10 7f 29 4d 85 e4 0f 84 3d 01 00 00 4c 89 cb 48 f7 d3 4c 01 fb 41 80 3b 00 <0f> 85 9e 01 00 00 49 ff c3 48 ff c3 75 ee e9 1d 01 00 00 44 89 dd
RSP: 0000:ffffc9000391ed18 EFLAGS: 00000246
RAX: ffff8881fc5fca01 RBX: fffffffffffffff4 RCX: ffffffff8176bd26
RDX: 0000000000000001 RSI: 0000000000000060 RDI: ffffc9000391edc8
RBP: 0000000000000000 R08: ffffc9000391ee27 R09: 1ffff92000723dc4
R10: dffffc0000000000 R11: fffff52000723db9 R12: 000000000000000c
R13: ffff8881fc5fca80 R14: fffff52000723dc5 R15: 1ffff92000723db9
__asan_memset+0x22/0x50 mm/kasan/shadow.c:84
__unwind_start+0x36/0x660 arch/x86/kernel/unwind_orc.c:715
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x375/0x720 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x46/0x150 drivers/usb/core/urb.c:75
nfcmrvl_submit_bulk_urb+0x55/0x410 drivers/nfc/nfcmrvl/usb.c:113
nfcmrvl_usb_nci_open+0x120/0x150 drivers/nfc/nfcmrvl/usb.c:182
nfcmrvl_nci_open+0xc5/0x110 drivers/nfc/nfcmrvl/main.c:28
nci_open_device net/nfc/nci/core.c:490 [inline]
nci_dev_up+0x17a/0xdc0 net/nfc/nci/core.c:643
nfc_dev_up+0x165/0x350 net/nfc/core.c:118
nfc_genl_dev_up+0x89/0xe0 net/nfc/netlink.c:775
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f237aed199e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f2379e79008 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2379e796c0 RCX: 00007f237aed199e
RDX: 0000000000000000 RSI: 00007f2379e79090 RDI: 0000000000000005
RBP: 000000000000001f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000000 R14: 00007ffc68e7c6e0 R15: 00007ffc68e7c7c8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nfcmrvl_bulk_complete+0x107/0x600 drivers/nfc/nfcmrvl/usb.c:71
Code: e8 03 48 89 44 24 28 42 80 3c 28 00 74 08 4c 89 e7 e8 1d fd fe fb 4c 89 64 24 30 4d 8b 24 24 49 83 c4 20 4c 89 e0 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 e7 e8 fa fc fe fb 49 8b 2c 24 48 89 5c
RSP: 0000:ffffc90000a08a68 EFLAGS: 00010002
RAX: 0000000000000004 RBX: ffff888181a9b600 RCX: 0000000000000100
RDX: ffff8881fc5fca80 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff88811117404f R09: 1ffff1102222e809
R10: dffffc0000000000 R11: ffffed102222e80a R12: 0000000000000020
R13: dffffc0000000000 R14: ffff888111174048 R15: 1ffff1102222e809
FS: 00007f2379e796c0(0000) GS:ffff8882e86de000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe7fde2e1f CR3: 000000018a40c000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
0: e8 03 48 89 44 call 0x44894808
5: 24 28 and $0x28,%al
7: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1)
c: 74 08 je 0x16
e: 4c 89 e7 mov %r12,%rdi
11: e8 1d fd fe fb call 0xfbfefd33
16: 4c 89 64 24 30 mov %r12,0x30(%rsp)
1b: 4d 8b 24 24 mov (%r12),%r12
1f: 49 83 c4 20 add $0x20,%r12
23: 4c 89 e0 mov %r12,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 28 00 cmpb $0x0,(%rax,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 fa fc fe fb call 0xfbfefd33
39: 49 8b 2c 24 mov (%r12),%rbp
3d: 48 rex.W
3e: 89 .byte 0x89
3f: 5c pop %rsp
The explanation of the root cause of the bug is:
The crash is caused by a race condition during the device initialization in `nfcmrvl_probe()`, combined with a macro collision bug that masks the issue on the first URB completion but triggers it on the second.
### 1. The Race Condition (Null-ptr-deref)
In `drivers/nfc/nfcmrvl/usb.c`, the `nfcmrvl_probe()` function initializes the device by calling `nfcmrvl_nci_register_dev()`. This function allocates the `priv` structure and crucially calls `nci_register_device()`, which immediately exposes the NCI device to userspace (via netlink).
However, `nfcmrvl_probe()` assigns the returned `priv` pointer to `drv_data->priv` **after** `nfcmrvl_nci_register_dev()` returns:
```c
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
&intf->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv; // <--- Race window: device is already exposed to userspace
```
If userspace (or a fuzzer) immediately sends a netlink command to bring the device up (`nfc_dev_up`), it triggers `nfcmrvl_usb_nci_open()`, which submits bulk URBs. If an URB completes immediately (e.g., because `dummy_hcd` is used or an error occurs), the completion handler `nfcmrvl_bulk_complete()` is invoked.
In `nfcmrvl_bulk_complete()`, the code attempts to allocate an skb using `drv_data->priv->ndev`:
```c
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, GFP_ATOMIC);
```
Because `nfcmrvl_probe()` hasn't reached the `drv_data->priv = priv;` assignment yet, `drv_data->priv` is `NULL`, resulting in the observed General Protection Fault.
### 2. The Macro Collision (Why it crashes on the *second* URB)
You might wonder why the early return check in `nfcmrvl_bulk_complete()` doesn't prevent the crash:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
```
This check contains a typo: it checks `drv_data->flags` instead of `priv->flags`.
By coincidence, `NFCMRVL_NCI_RUNNING` is defined as `1` (in `nfcmrvl.h`), and `NFCMRVL_USB_BULK_RUNNING` is also defined as `1` (in `usb.c`). Thus, the code is accidentally checking if `NFCMRVL_USB_BULK_RUNNING` is set.
In `nfcmrvl_usb_nci_open()`, two URBs are submitted:
```c
err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // First URB
if (err)
goto failed;
set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); // Second URB
```
If the **first** URB completes immediately, `NFCMRVL_USB_BULK_RUNNING` is not yet set, so the completion handler safely returns. However, when the **second** URB is submitted, the flag is now set. If the second URB completes immediately, the check passes, the code proceeds to dereference `drv_data->priv`, and the kernel crashes.
### Proposed Fix
To fix this cleanly without relying on `drv_data->priv` being set early, we can pass `priv` as the URB context instead of `drv_data`. `priv` is always valid when `nfcmrvl_usb_nci_open()` is called.
1. Modify `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` instead of `drv_data`, and pass `priv` as the context to `usb_fill_bulk_urb()`:
```c
static int nfcmrvl_submit_bulk_urb(struct nfcmrvl_private *priv, gfp_t mem_flags)
{
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
nfcmrvl_bulk_complete, priv);
// ...
}
```
2. In `nfcmrvl_bulk_complete()`, retrieve `priv` from `urb->context`, completely avoiding the `NULL` dereference:
```c
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_private *priv = urb->context;
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
// ...
```
3. Fix the typo in `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to correctly check `priv->flags` for the NCI running state:
```c
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return;
```
*(Note: A similar race condition exists in `drivers/nfc/nfcmrvl/spi.c` where `nfcmrvl_spi_int_irq_thread_fn` can fire before `drv_data->priv` is set. Adding `if (!drv_data->priv) return IRQ_HANDLED;` at the top of the IRQ handler—just like `i2c.c` does—will fix the SPI PHY as well).*
The diff of the bug fix is:
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);
Additional description of the patch:
### Cause of the Bug
The crash occurs due to an initialization race condition in `drivers/nfc/nfcmrvl/usb.c`:
1. **Race Condition (`NULL` pointer dereference)**:
In `nfcmrvl_probe()`, the driver calls `nfcmrvl_nci_register_dev()`, which allocates the core `struct nfcmrvl_private` (`priv`) structure and registers the device with NCI via `nci_register_device()`. Calling `nci_register_device()` immediately exposes the NFC interface to userspace.
However, `nfcmrvl_probe()` assigns `drv_data->priv = priv` **after** `nfcmrvl_nci_register_dev()` returns. If userspace brings up the interface immediately (via Netlink `nfc_dev_up`), `nfcmrvl_usb_nci_open()` is executed. It submits bulk RX URBs passing `drv_data` as the URB context (`urb->context`). When an URB completes before `nfcmrvl_probe()` assigns `drv_data->priv`, `nfcmrvl_bulk_complete()` attempts to dereference `drv_data->priv` (`NULL`), causing a General Protection Fault / NULL pointer dereference.
2. **Flag Typo Masking Initial Completion**:
In `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()`, the driver tested `test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)` instead of `&priv->flags`. Because `NFCMRVL_NCI_RUNNING` and `NFCMRVL_USB_BULK_RUNNING` both share value `1`, the check inadvertently tested `drv_data->flags` (for `NFCMRVL_USB_BULK_RUNNING`), causing the first URB completion in `nfcmrvl_usb_nci_open()` to return early before `NFCMRVL_USB_BULK_RUNNING` was set, while crashing on subsequent completions.
---
### Solution
1. **Pass `priv` as URB Context**:
Changed `nfcmrvl_submit_bulk_urb()` to take `struct nfcmrvl_private *priv` and set `priv` as `urb->context` in `usb_fill_bulk_urb()`. Since `priv` is already fully allocated and initialized when `nfcmrvl_usb_nci_open()` is invoked, `nfcmrvl_bulk_complete()` obtains `priv` directly from `urb->context`, eliminating any dependency on `drv_data->priv`.
2. **Fix `NFCMRVL_NCI_RUNNING` Flag Checks**:
Updated `nfcmrvl_bulk_complete()` and `nfcmrvl_resume()` to check `test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)`.
3. **Keep Patch Focused**:
Avoided adding ad-hoc defensive sentinels in unrelated PHY drivers (such as `spi.c`), keeping the fix strictly focused and clean for the USB driver.
Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc.
nfc: nfcmrvl: refactor endpoint lookup
nfc: nfcmrvl: convert to gpio descriptors
nfc: nfcmrvl: Fix memory leak in nfcmrvl_play_deferred
nfc: mrvl: constify static nfcmrvl_if_ops
nfc: mrvl: reduce the scope of local variables
nfc: mrvl: remove useless "continue" at end of loop
nfc: mrvl: use SPDX-License-Identifier
NFC: nfcmrvl: fix kernel-doc syntax in file headers
NFC: nfcmrvl: fix gpio-handling regression
NFC: nfcmrvl_usb: use irqsave() in USB's complete callback
NFC: nfcmrvl_usb: use interface as phy device
networking: introduce and use skb_put_data()
NFC: nfcmrvl: configure head/tail room values per low level drivers
NFC: nfcmrvl: add firmware download support
NFC: nfcmrvl: remove unneeded version defines
NFC: nfcmrvl: small fix in USB driver
NFC: nfcmrvl: add platform_data and DT configuration
NFC: nfcmrvl: update USB device id
NFC: nfcmrvl: update nci recv frame API
NFC: nfcmrvl: add support of HCI-based transport
Reply: NFC: nfcmrvl_usb: fix NULL pointer dereference in bulk completion
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:
<IRQ>
__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
</IRQ>
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.
|
| 348/2 |
2026/08/26 15:23 |
llm |
description-generator |
0m
Model:
Tokens:
input: 12884
output: 770
thoughts: 4519
Thoughts: **NFC: nfcmrvl_usb: fix NULL pointer dereference in bulk completion**
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.
```
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:
<IRQ>
__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
</IRQ>
```
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`.
|