{"schema":"threatline.record/2","record":{"id":"GHSA-72qv-gcph-rfwf","aliases":["GHSA-72qv-gcph-rfwf"],"summary":"Fix bypass of CVE-2026-91962: UINT32 overflow in audin Apple backends (audin_mac.m, audin_ios.m)","details":"## Summary\r\n\r\nThe fix for CVE-2026-91962 (commit `3bf07d44`, \"[channels,audin] limit FramesPerPacket\") rejects `FramesPerPacket >= INT32_MAX` in `audin_main.c:500`. However, the Apple backends compute the audio queue buffer size as `FramesPerPacket * 2 * nChannels` in **UINT32 arithmetic** at `audin_mac.m:277` and `audin_ios.m:245`. Values that pass the guard still wrap the multiplication to 0 or tiny sizes, yielding undersized `AudioQueueAllocateBuffer` allocations and the same heap out-of-bounds write the original CVE described.\r\n\r\n## Details\r\n\r\n**Fix in `audin_main.c:500` (incomplete):**\r\n```c\r\n/* allows FramesPerPacket from 0 to 2,147,483,646 */\r\nif (FramesPerPacket >= INT32_MAX)\r\n    return ERROR_INVALID_DATA;\r\n```\r\n\r\n**Vulnerable expression — unchanged by the fix — in `audin_mac.m:277` and `audin_ios.m:245`:**\r\n```c\r\ndevStat = AudioQueueAllocateBuffer(mac->audioQueue,\r\n    mac->FramesPerPacket * 2 * mac->format.nChannels,  /* UINT32 — no overflow check */\r\n    &mac->audioBuffers[index]);\r\n```\r\n\r\n`FramesPerPacket` is declared `UINT32` (audin_mac.m:71, audin_ios.m:59). The multiplication stays in 32-bit unsigned arithmetic; any product above 4,294,967,295 wraps modulo 2³².\r\n\r\nThe Mac backend enforces `nChannels == 2` (`audin_mac_format_supported`, line 121), so the effective expression is `FramesPerPacket × 4`:\r\n\r\n- `FramesPerPacket = 2^30 = 1,073,741,824` — **passes** the INT32_MAX check (`< 2,147,483,647`)\r\n- `1,073,741,824 × 2 × 2 = 4,294,967,296` → UINT32 wraps to **0 bytes**\r\n- `FramesPerPacket = 2^30 + 4 = 1,073,741,828` → wraps to **16 bytes**; real audio ≈ 4.29 GB per callback\r\n\r\n## PoC\r\n\r\nCompiled and executed on macOS Darwin 25.3.0 arm64 (Apple clang 21.0):\r\n\r\n```c\r\nuint32_t FramesPerPacket = (1u << 30); /* 2^30 — passes INT32_MAX check */\r\nuint16_t nChannels = 2;\r\nuint32_t buf_size = FramesPerPacket * 2 * (uint32_t)nChannels; /* wraps to 0 */\r\n\r\nvoid *p = malloc(0);\r\n/* macOS returns non-NULL: p = 0x1048718d0            */\r\n/* AudioQueueAllocateBuffer(..., 0, &buf) => noErr     */\r\n/* audio capture callback writes ~GB into 0-byte buf  */\r\n/* => heap out-of-bounds write                        */\r\n```\r\n\r\nLive output:\r\n```\r\nCase A: FramesPerPacket = 1073741824 (2^30)\r\n  Passes INT32_MAX check: YES (bypass!)\r\n  UINT32: 1073741824 * 2 * 2 = 4294967296 -> wraps to 0 bytes\r\n\r\nCase B: FramesPerPacket = 1073741828 (2^30+4)\r\n  Passes INT32_MAX check: YES (bypass!)\r\n  UINT32: 1073741828 * 2 * 2 = 4294967312 -> wraps to 16 bytes\r\n  Real audio data per callback: ~4.29 GB written into 16 bytes\r\n\r\nmalloc(0) = 0x1048718d0  (non-NULL — heap pointer returned on macOS)\r\n=> AudioQueueAllocateBuffer succeeds, error check passes, OOB write follows\r\n```\r\n\r\nSuggested fix:\r\n```c\r\n/* Option A: widen to UINT64 in each Apple backend */\r\nconst UINT64 bufSize = (UINT64)mac->FramesPerPacket * 2U\r\n                       * (UINT64)mac->format.nChannels;\r\nif (bufSize == 0 || bufSize > UINT32_MAX)\r\n    return ERROR_INVALID_DATA;\r\n\r\n/* Option B: tighten guard in audin_main.c (covers all backends) */\r\nif (FramesPerPacket == 0 || FramesPerPacket > UINT32_MAX / 4U)\r\n    return ERROR_INVALID_DATA;\r\n```\r\n\r\n## Impact\r\n\r\nHeap out-of-bounds write in the audio input path of FreeRDP clients on macOS and iOS when connecting to a malicious RDP server. An attacker operating a malicious RDP server can trigger this by advertising the audin virtual channel and sending `FramesPerPacket = 2^30` in the Open PDU. No client-side user interaction beyond connecting is required (the server controls the audin negotiation).\r\n\r\nPotential impact: crash or client-side code execution (CWE-190, CWE-787). Same severity class as CVE-2026-91962. The fix shipped in 3.31.0 does not protect macOS or iOS clients.","categories":["Unclassified"],"product":"FreeRDP","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-72qv-gcph-rfwf","source_modified_at":"2026-10-01T11:15:02Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"ecosystem":"c","name":"FreeRDP"},"range":"<= 3.31.0"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-72qv-gcph-rfwf","source_modified_at":"2026-10-01T11:15:02Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"ecosystem":"c","name":"FreeRDP"},"version":"3.32.0"}],"cvss":8.8,"severity":"High","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-01T11:15:02Z","modified_at":"2026-10-01T11:15:02Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-72qv-gcph-rfwf","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-10-01T11:15:02Z"}],"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-10-01T11:15:02Z","content_hash":"e939472ec7577d84ff63a87263431b01460cdcc2ac87ae25a321fe2adb02b449"}}
