From db3995a8fe307d878b72e9d88d4e0dc7f54225dc Mon Sep 17 00:00:00 2001 From: "Hang Zhao (QCT)" Date: Mon, 13 Apr 2026 14:02:51 -0700 Subject: [PATCH 1/3] wdfserial: add NULL pipe guard in QCPNP_ResetUsbPipe QCPNP_ResetUsbPipe() calls WdfUsbTargetPipeGetIoTarget() and WdfIoTargetStop() without validating that the pipe handle is non-NULL. On multi-interface USB composite devices, certain interface functions may lack bulk IN or bulk OUT endpoints, leaving BulkIN/BulkOUT as NULL in the device context. When callers such as EvtDevicePrepareHardware or EvtFileCreate pass these NULL handles to QCPNP_ResetUsbPipe, the WDF framework raises a WDF_VIOLATION (bugcheck 0x10D) because the WDFUSBPIPE handle is invalid. Add a NULL check at the entry of QCPNP_ResetUsbPipe to return STATUS_SUCCESS early when the pipe handle is NULL, preventing the crash for all callers. Signed-off-by: Hang Zhao (QCT) Signed-off-by: hangz --- src/windows/wdfserial/QCPNP.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/windows/wdfserial/QCPNP.c b/src/windows/wdfserial/QCPNP.c index c8ed894..99fa4a4 100644 --- a/src/windows/wdfserial/QCPNP.c +++ b/src/windows/wdfserial/QCPNP.c @@ -3112,6 +3112,18 @@ NTSTATUS QCPNP_ResetUsbPipe ) { NTSTATUS status = STATUS_SUCCESS; + + if (usbPipe == NULL) + { + QCSER_DbgPrint + ( + QCSER_DBG_MASK_CIRP, + QCSER_DBG_LEVEL_ERROR, + ("<%ws> QCPNP_ResetUsbPipe called with NULL pipe, skipping\n", pDevContext->PortName) + ); + return STATUS_SUCCESS; + } + WDFIOTARGET ioTarget = WdfUsbTargetPipeGetIoTarget(usbPipe); WDF_REQUEST_SEND_OPTIONS syncReqOptions; WDF_REQUEST_SEND_OPTIONS_INIT(&syncReqOptions, 0); From bd0edbed473aa6cf7262b039ee9144630608ccd6 Mon Sep 17 00:00:00 2001 From: "Hang Zhao (QCT)" Date: Thu, 16 Apr 2026 11:48:28 -0700 Subject: [PATCH 2/3] fix: guard against divide-by-zero when wMaxPktSize is 0 in write thread Add a zero-check for pDevContext->wMaxPktSize before the modulo operation in QCWT_WriteRequestHandlerThread. When the USB bulk-OUT pipe is not yet configured (or during surprise removal / power transition), wMaxPktSize can be 0, causing a SYSTEM_SERVICE_EXCEPTION (0xc0000094 integer divide-by-zero) bugcheck. The short-circuit evaluation skips the zero-length-packet logic entirely when there is no valid pipe configuration. Fixes: QUD-1832 Signed-off-by: hangz --- src/windows/wdfserial/QCWT.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows/wdfserial/QCWT.c b/src/windows/wdfserial/QCWT.c index c60ee18..0b707d6 100644 --- a/src/windows/wdfserial/QCWT.c +++ b/src/windows/wdfserial/QCWT.c @@ -403,7 +403,7 @@ void QCWT_WriteRequestHandlerThread WDF_REQUEST_PARAMETERS_INIT(&writeParam); WdfRequestGetParameters(request, &writeParam); QCWT_EvtIoWrite(pDevContext->WriteQueue, request, writeParam.Parameters.Write.Length); - if (gVendorConfig.EnableZeroLengthPacket && (writeParam.Parameters.Write.Length % pDevContext->wMaxPktSize == 0)) + if (gVendorConfig.EnableZeroLengthPacket && (pDevContext->wMaxPktSize != 0) && (writeParam.Parameters.Write.Length % pDevContext->wMaxPktSize == 0)) { #ifdef QCUSB_MUX_PROTOCOL if (pDevContext->DeviceFunction != QCUSB_DEV_FUNC_LPC) From ed302678f066fce5f7205a8dc408a44e382b0d74 Mon Sep 17 00:00:00 2001 From: "Hang Zhao (QCT)" Date: Thu, 16 Apr 2026 12:14:43 -0700 Subject: [PATCH 3/3] fix: guard against NULL BulkIN pipe in read handler thread Add a NULL check for pDevContext->BulkIN at the start of QCRD_ReadRequestHandlerThread. When the USB device has no bulk-IN endpoint (e.g., control-only or output-only interfaces), the read thread would dereference a NULL WDFUSBPIPE handle via WdfUsbTargetPipeGetIoTarget, causing a BSOD. The thread now signals its started event and exits gracefully when BulkIN is NULL, matching the existing NULL guard pattern in the write path (QCWT_EvtIoWrite). Signed-off-by: hangz --- src/windows/wdfserial/QCRD.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/windows/wdfserial/QCRD.c b/src/windows/wdfserial/QCRD.c index 6f6699c..979b271 100644 --- a/src/windows/wdfserial/QCRD.c +++ b/src/windows/wdfserial/QCRD.c @@ -132,8 +132,8 @@ void QCRD_ReadRequestHandlerThread PREQUEST_CONTEXT pReqContext; PDEVICE_CONTEXT pDevContext = pContext; ULONG devErrCnt = 0; - WDFIOTARGET ioTarget = WdfUsbTargetPipeGetIoTarget(pDevContext->BulkIN); - PKWAIT_BLOCK pWaitBlock = ExAllocatePoolUninitialized(NonPagedPoolNx, (READ_THREAD_RESUME_EVENT_COUNT) * sizeof(KWAIT_BLOCK), '3gaT'); + WDFIOTARGET ioTarget = NULL; + PKWAIT_BLOCK pWaitBlock = NULL; BOOLEAN bRunning = TRUE; BOOLEAN bDeviceOpened = FALSE; BOOLEAN bDeviceAwaken = FALSE; @@ -144,6 +144,22 @@ void QCRD_ReadRequestHandlerThread WDF_REQUEST_PARAMETERS requestParam; // for application requests PRING_BUFFER rxBuffer = &pDevContext->ReadRingBuffer; + if (pDevContext->BulkIN == NULL) + { + QCSER_DbgPrint + ( + QCSER_DBG_MASK_READ, + QCSER_DBG_LEVEL_ERROR, + ("<%ws> RIRP: QCRD_ReadRequestHandlerThread ERROR no USB pipe for read, exiting\n", pDevContext->PortName) + ); + KeSetEvent(&pDevContext->ReadThreadStartedEvent, IO_NO_INCREMENT, FALSE); + PsTerminateSystemThread(STATUS_SUCCESS); + return; + } + + ioTarget = WdfUsbTargetPipeGetIoTarget(pDevContext->BulkIN); + pWaitBlock = ExAllocatePoolUninitialized(NonPagedPoolNx, (READ_THREAD_RESUME_EVENT_COUNT) * sizeof(KWAIT_BLOCK), '3gaT'); + if (pWaitBlock == NULL) { WdfDeviceSetFailed(pDevContext->Device, WdfDeviceFailedNoRestart);