Skip to content

wdfserial: fix NULL pipe guards and divide-by-zero in read/write threads#21

Merged
5656hcx merged 3 commits intoqualcomm:mainfrom
hangzqcom:fix/null-pipe-guard-reset-usb-pipe
Apr 24, 2026
Merged

wdfserial: fix NULL pipe guards and divide-by-zero in read/write threads#21
5656hcx merged 3 commits intoqualcomm:mainfrom
hangzqcom:fix/null-pipe-guard-reset-usb-pipe

Conversation

@hangzqcom
Copy link
Copy Markdown
Contributor

@hangzqcom hangzqcom commented Apr 13, 2026

Bug 1 — NULL pipe 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.

Fix

Add a NULL check at the entry of QCPNP_ResetUsbPipe to return STATUS_SUCCESS early when the pipe handle is NULL.

Bug 2 — Integer divide-by-zero in write thread (QUD-1832)

QCWT_WriteRequestHandlerThread performs writeParam.Parameters.Write.Length % pDevContext->wMaxPktSize to decide whether a zero-length USB packet is needed. When the bulk-OUT pipe is not yet configured (or during surprise removal / power transition), wMaxPktSize is 0, causing a SYSTEM_SERVICE_EXCEPTION (bugcheck 0xC0000094 — integer divide-by-zero).

Crash signature: qcwdfserial+0x1370f, rax=0x9 (write length), r8=0x0 (wMaxPktSize).

Fix

Add pDevContext->wMaxPktSize != 0 guard before the modulo in QCWT_WriteRequestHandlerThread. C short-circuit evaluation skips the division when the packet size is zero.

Bug 3 — NULL BulkIN dereference in read handler thread

QCRD_ReadRequestHandlerThread unconditionally calls WdfUsbTargetPipeGetIoTarget(pDevContext->BulkIN) at thread start. When the USB device has no bulk-IN endpoint (e.g., control-only or output-only interfaces), BulkIN is NULL and the WDF framework dereferences an invalid handle, causing a BSOD.

Fix

Add a NULL check for pDevContext->BulkIN at the top of the read handler thread. If NULL, signal the thread-started event and exit gracefully via PsTerminateSystemThread, matching the existing NULL guard pattern in the write path.

Testing

  • Verified on a composite USB device with missing bulk endpoints — no BSOD on device attach/open.
  • Existing paths with valid pipe handles are unaffected.
  • Zero-length-packet logic is correctly skipped when wMaxPktSize is 0.
  • Read thread exits cleanly when BulkIN pipe is absent.

Fixes: QUD-1832

@hangzqcom hangzqcom force-pushed the fix/null-pipe-guard-reset-usb-pipe branch from af1a888 to a65c5b0 Compare April 13, 2026 21:15
@5656hcx
Copy link
Copy Markdown
Contributor

5656hcx commented Apr 14, 2026

  1. Looks good. Will merge after user confirms the fix.
  2. I will check other places to make sure driver does not crash when BULK_IN or BULK_OUT is missing.

@hangzqcom hangzqcom changed the title wdfserial: add NULL pipe guard in QCPNP_ResetUsbPipe wdfserial: add NULL pipe guard and fix divide-by-zero in write thread Apr 16, 2026
@hangzqcom hangzqcom changed the title wdfserial: add NULL pipe guard and fix divide-by-zero in write thread wdfserial: fix NULL pipe guards and divide-by-zero in read/write threads Apr 16, 2026
@hangzqcom
Copy link
Copy Markdown
Contributor Author

Added follow-up commit to address a Code 10 (device cannot start) yellow-bang observed by the customer on the QDSS MDM Data 90E7 (0000) interface after the crash fixes in this PR were applied.

Root cause

QDSS MDM Data has no bulk-IN endpoint, so after bug 3's guard the read thread took the new early-exit path and terminated without signaling ReadThreadD0EntryReadyEvent, ReadThreadD0ExitReadyEvent, or ReadThreadFileCloseReadyEvent. QCPNP_CreateWorkerThread still publishes a non-NULL ReadRequestHandlerThread reference, so the subsequent PnP/power callbacks hit if (ReadRequestHandlerThread != NULL) and blocked on events that no one will ever set:

  • QCPNP_EvtDeviceD0Entry → infinite KeWaitForSingleObject(&ReadThreadD0EntryReadyEvent, ... NULL) on a dead thread → PnP start timeout → Code 10
  • QCPNP_EvtDeviceD0Exit → same deadlock on ReadThreadD0ExitReadyEvent
  • QCPNP_EvtFileClose → 1 s stall on ReadThreadFileCloseReadyEvent

Fix

Signal all three handshake events (plus the existing ReadThreadStartedEvent) before PsTerminateSystemThread in the early-exit path, so the PnP/power/file-close callers fall through their waits immediately.

Existing paths are unaffected: when a bulk-IN pipe exists, the events are cleared/reset by the normal thread body as before. QCPNP_EvtDeviceReleaseHardware is already safe — KeWaitForSingleObject on an already-terminated thread returns immediately.

@hangzqcom hangzqcom force-pushed the fix/null-pipe-guard-reset-usb-pipe branch from a13ea3f to 0bb6156 Compare April 24, 2026 18:19
@hangzqcom
Copy link
Copy Markdown
Contributor Author

Correction on my previous comment — I was wrong. Please disregard the proposed follow-up; the PR branch has been force-pushed back to the three original commits (HEAD is 0bb6156).

QDSS interfaces on PID 90E7 (MI_04 = QDSS MSM Data, MI_05 = QDSS MDM Data, MI_07 = QTI DPL Data) bind to qdbusb.sys (qdbusb.inf), not to qcwdfserial.sys. This PR only touches the wdfserial driver, which on PID 90E7 serves MI_02 / MI_03 (and the modem driver on MI_06). The read-thread early-exit handshake analysis I described earlier is therefore not reachable for the yellow-banged QDSS MDM Data device — the wdfserial code paths are not loaded against that interface.

So the customer's Code 10 on QDSS MDM Data 90E7 (0000) is unrelated to this PR. Possible causes to investigate separately (in qdbusb, not wdfserial):

  • Missing/broken INF binding or service image for qdbusb.sys on that build (e.g., unsigned/mismatched driver, stale DriverStore entry).
  • A defect inside qdbusb.sys itself (PnP/Start failure, resource allocation, etc.) — check the Device Properties → Events tab and System event log for Service Control Manager / PnP errors on that device instance, and capture !devnode / !devstack from a kernel debugger if available.
  • Old inbox usbser.sys or a previous driver taking precedence — confirm which service the device is bound to in Device Manager → Details → "Service".

Happy to help triage the QDSS side separately once we have the event log / driver binding info, but this PR can be reviewed on its own merits.

@5656hcx 5656hcx self-requested a review April 24, 2026 18:34
@hangzqcom hangzqcom force-pushed the fix/null-pipe-guard-reset-usb-pipe branch from 0bb6156 to be5bf40 Compare April 24, 2026 18:37
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) <hangz@qti.qualcomm.com>
Signed-off-by: hangz <hangz@qti.qualcomm.com>
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 <hangz@qti.qualcomm.com>
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 <hangz@qti.qualcomm.com>
@hangzqcom hangzqcom force-pushed the fix/null-pipe-guard-reset-usb-pipe branch from be5bf40 to ed30267 Compare April 24, 2026 19:14
@5656hcx 5656hcx merged commit 0fd924f into qualcomm:main Apr 24, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants