CVE-2023-40078 AI-generated analysis
platform/packages/modules/Bluetooth, system/stack/a2dp (A2DP Opus vendor codec decoder)
Adds a check after the opus_decode() retry in a2dp_vendor_opus_decoder_decode_packet. If the retry fails, the packet is dropped. Before, the negative error code was turned into a huge unsigned frame length that was passed to the decode callback.
Root cause
In system/stack/a2dp/a2dp_vendor_opus_decoder.cc, a2dp_vendor_opus_decoder_decode_packet calls opus_decode(). If the first call fails, it retries with a different set of parameters (the retry call shown in the diff passes A2DP_OPUS_DECODE_BUFFER_LENGTH and flags 0). The return value of the retry was not checked. It was used directly in 'size_t frame_len = ret_val * numChannels * sizeof(a2dp_opus_decoder_cb.decode_buf[0])', and the result was handed to a2dp_opus_decoder_cb.decode_callback. If the retry also fails, ret_val is negative. Converted to unsigned, it gives an enormous size that overflows the frame buffer (per the commit message).
How the fix works
- system/stack/a2dp/a2dp_vendor_opus_decoder.cc:a2dp_vendor_opus_decoder_decode_packet: right after the retry opus_decode() call and before frame_len is computed, the fix adds 'if (ret_val < OPUS_OK) { LOG_ERROR("Opus DecodeFrame retry failed with %d, dropping packet", ret_val); return false; }'. A failed retry now returns false before the size computation and before decode_callback is called.
- Single-commit fix: 6 added lines, no other files touched.
Completeness
- Covers the unchecked path shown in the diff, which is the result of the retry opus_decode() call. Per the commit message, a failed first decode leads to the retry, so the retry result is the value that reaches the frame_len computation.
- The fix adds no explicit upper-bound check that frame_len fits in decode_buf. It relies on opus_decode() respecting the frame_size argument it is given. The material does not show the decode_buf declaration, so this cannot be verified.
- Only the Opus vendor decoder is changed. The material shows nothing about other A2DP decoders.
Behavior changes
- A2DP Opus packets for which both the first and the retry opus_decode() fail are now dropped. The function logs 'Opus DecodeFrame retry failed with %d, dropping packet' at error level and returns false instead of calling decode_callback.
- No change for packets that decode successfully on the first or second attempt.
Per major
- Android 14: same fix: commit 6cdf985a6644 in android-security-14.0.0_r3 (cherry-pick of internal c7b6e560eda0, Merged-In Ie8ec891bf5e2537eeee9272f550ae23f8797a878). At android-security-14.0.0_r29, only 2/4 lines of the patched region are present. The material does not show the current text or explain the difference.
- Android 15: not affected per the bulletin (not listed). At android-security-15.0.0_r17, 2/4 lines of the patched region are present. Whether the guard exists in another form is not shown.
- Android 16: not affected per the bulletin (not listed). At android-security-16.0.0_r8, 2/4 lines of the patched region are present. Whether the guard exists in another form is not shown.
- Android 17: not affected per the bulletin (not listed). At android-security-17.0.0_r1, 2/4 lines of the patched region are present. Whether the guard exists in another form is not shown.
Unknowns
- The surrounding code is not in the material: the first opus_decode() call, its failure branch, the decode_buf declaration and size, and how decode_callback consumes frame_len.
- The actual text of the patched region at android-security-14.0.0_r29 and in the 15/16/17 tags is not shown. It is unclear why only 2/4 lines match and whether the guard is present in an equivalent form.
- Which code calls a2dp_vendor_opus_decoder_decode_packet, and whether it can be triggered remotely, is not shown.