{"schema":"threatline.record/2","record":{"id":"GHSA-9qqc-m43j-g4r2","aliases":["GHSA-9qqc-m43j-g4r2"],"summary":"urbdrc: missing OutputBufferSize validation in urb_isoch_transfer IN path - reachable assertion DoS (CWE-617)","details":"### Summary\r\n`urb_isoch_transfer()` in `channels/urbdrc/client/data_transfer.c` reads `OutputBufferSize` from the network stream (controlled by the RDP server) but validates it only for OUT-direction transfers. For IN-direction, the value flows unchecked into `libusb_udev_isoch_transfer()`, where `WINPR_ASSERTING_INT_CAST(int, BufferSize)` fires a reachable assertion when `BufferSize > INT32_MAX`, terminating the FreeRDP client process. Same class as CVE-2026-91961 (control_transfer path), different function.\r\n\r\n### Details\r\n\r\n**File 1:** `channels/urbdrc/client/data_transfer.c` ~line 994  \r\n**File 2:** `channels/urbdrc/client/libusb/libusb_udevice.c` ~line 1421  \r\n**Tested on commit:** `7c3b4bd`  \r\n**CWE:** CWE-617 (Reachable Assertion)\r\n\r\n**Vulnerable code — missing IN-direction check:**\r\n```c\r\nStream_Read_UINT32(s, OutputBufferSize);\r\n\r\nif (transferDir == USBD_TRANSFER_DIRECTION_OUT)\r\n{\r\n    if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))\r\n        return ERROR_INVALID_DATA;\r\n}\r\n// IN direction: OutputBufferSize is never range-checked before use\r\n```\r\n\r\n**Assertion fires in libusb backend:**\r\n```c\r\nlibusb_fill_iso_transfer(\r\n    iso_transfer, pdev->libusb_handle,\r\n    WINPR_ASSERTING_INT_CAST(uint8_t, EndpointAddress),\r\n    Stream_Pointer(user_data->data),\r\n    WINPR_ASSERTING_INT_CAST(int, BufferSize),      // ← assert fires if BufferSize > INT32_MAX\r\n    WINPR_ASSERTING_INT_CAST(int, NumberOfPackets),\r\n    func_iso_callback, user_data, Timeout);\r\n```\r\n\r\nWhen `BufferSize = 0x80000001`: cast to `int` flips sign positive→negative, `WINPR_ASSERTING_INT_CAST` sign-check assert fires → `winpr_int_assert()` (WINPR_NORETURN) → **process terminated**.\r\n\r\n**Contrast — patched `libusb_udev_control_transfer` has the guard (line ~1456):**\r\n```c\r\nif (*BufferSize > UINT16_MAX)   // ← explicit check present here\r\n{\r\n    WLog_Print(...);\r\n    return FALSE;\r\n}\r\n```\r\n`libusb_udev_isoch_transfer` has no equivalent check.\r\n\r\n### PoC\r\n\r\nMalicious RDP server sends `TS_URB_ISOCH_TRANSFER` with:\r\n- `TransferDirection = USBD_TRANSFER_DIRECTION_IN`\r\n- `NumberOfPackets = 1` (valid, passes stream length check)\r\n- `OutputBufferSize = 0x80000001`\r\n\r\nClient reads `OutputBufferSize` without bounds check (IN path skips validation), passes it to `libusb_udev_isoch_transfer`, `WINPR_ASSERTING_INT_CAST(int, 0x80000001)` sign-check fails → assertion → client crashes.\r\n\r\n### Impact\r\n\r\nA malicious or compromised RDP server can crash the FreeRDP client via the USB redirection channel (URBDRC). Denial of Service. No user interaction required beyond connecting to the server.\r\n\r\n### Proposed Fix\r\n\r\nAdd bounds check in `libusb_udev_isoch_transfer` before the cast:\r\n\r\n```c\r\nif (BufferSize > (UINT32)INT32_MAX)\r\n{\r\n    WLog_Print(urbdrc->log, WLOG_ERROR,\r\n               \"BufferSize %\" PRIu32 \" > INT32_MAX, rejecting isoch transfer\", BufferSize);\r\n    async_transfer_user_data_free(user_data);\r\n    return -1;\r\n}\r\n```","categories":["Unclassified"],"product":"FreeRDP","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-9qqc-m43j-g4r2","source_modified_at":"2026-09-28T08:08:35Z","fetched_at":"2026-10-25T11:14:20.449793Z","package":{"ecosystem":"c","name":"FreeRDP"},"range":"<= 3.32.0"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-9qqc-m43j-g4r2","source_modified_at":"2026-09-28T08:08:35Z","fetched_at":"2026-10-25T11:14:20.449793Z","package":{"ecosystem":"c","name":"FreeRDP"},"version":"3.32.1"}],"cvss":5.3,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-09-28T08:08:35Z","modified_at":"2026-09-28T08:08:35Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-9qqc-m43j-g4r2","fetched_at":"2026-10-25T11:14:20.449793Z","source_modified_at":"2026-09-28T08:08:35Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-09-28T08:08:35Z","content_hash":"d410bacc2fa6a204b7b18dda0593f9ec6a151787d1a06b9adcfb2232416e247a"}}
