CVE-2024-31313 AI-generated analysis
platform/system/libfmq, Fast Message Queue (include/fmq/MessageQueueBase.h)
Adds underflow checks to MessageQueueBase::availableToReadBytes() and availableToWriteBytes() in system/libfmq, so that inconsistent read/write counters yield 0 (with an error log) instead of a wrapped size_t.
Root cause
The FMQ read and write counters (mReadPtr, mWritePtr, declared as std::atomic<uint64_t>*) were used without consistency checks. availableToReadBytes() returned mWritePtr - mReadPtr, which wraps to a huge value if the write counter is behind the read counter. availableToWriteBytes() returned mDesc->getSize() - availableToReadBytes(), which wraps if the available bytes exceed the queue size. Per the in-code comment, availableToReadBytes() is invoked by implementations of both read() and write().
How the fix works
- include/fmq/MessageQueueBase.h:availableToReadBytes(): loads mWritePtr and mReadPtr (memory_order_acquire) into local uint64_t writePtr and readPtr. If writePtr < readPtr, it logs 'The write or read pointer has become corrupted...' via hardware::details::logError and returns 0. Otherwise it returns writePtr - readPtr, computed from the same values it checked (commit title: 'Use the values of the ptrs that we check').
- include/fmq/MessageQueueBase.h:availableToWriteBytes(): reads queueSizeBytes = mDesc->getSize() and availableBytes = availableToReadBytes(). If queueSizeBytes < availableBytes, it logs the same error and returns 0. Otherwise it returns queueSizeBytes - availableBytes.
- Both functions are also marked inline. No other files change. Tested with fmq_fuzzer per the commit message.
Completeness
- Covers the two subtraction sites that could underflow: write-minus-read in availableToReadBytes() and size-minus-available in availableToWriteBytes().
- availableToReadBytes() does not clamp its result to the queue size. Only availableToWriteBytes() compares against mDesc->getSize(). The material does not show whether other users of availableToReadBytes() separately bound the result.
- On detection the functions return 0 and log. The diff does not mark the queue invalid, so later calls recompute from the same counters.
- The surrounding context shows MemTransaction::getSlot() uses 'idx > firstRegionLength + secondRegionLength' as its bounds test. This commit does not touch it.
Behavior changes
- When the write counter is below the read counter, availableToReadBytes() now returns 0 (queue appears empty) and logs an error instead of returning a wrapped value.
- When available bytes exceed the queue size, availableToWriteBytes() now returns 0 (queue appears full) and logs an error.
- availableToReadBytes() and availableToWriteBytes() are now declared inline.
Per major
- Android 14: same fix: commit 7903ae2f7f31 in android-security-14.0.0_r9 (cherry-pick of 38963310ad57 / da080aa565f0). At android-security-14.0.0_r29, 7/8 lines of the patched region are present.
- Android 15: not affected per the bulletin. At android-security-15.0.0_r17, 5/8 lines of the patched region are present. The material does not show whether the fix is present in another form.
- Android 16: not affected per the bulletin. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.
- Android 17: not affected per the bulletin. At android-security-17.0.0_r1, 7/8 lines of the patched region are present.
Unknowns
- Which read/write functions consume the wrapped values and whether they have their own bounds checks (callers of availableToReadBytes/availableToWriteBytes are not shown).
- How the counters become inconsistent in practice (shared-memory mapping and peer write access are not shown).
- Contents of the upstream commits 38963310ad57 and da080aa565f0, and whether an earlier check existed that re-loaded the pointers.
- Whether binaries using this template header must be rebuilt to pick up the fix.
- Why 15.0.0_r17 shows only 5/8 lines of the patched region, and whether 16/17 fully contain the fix.