AudioModule: validate codec2 header and bound the RX decode reads#11036
Conversation
Two issues reachable from a crafted AUDIO_APP payload: The RX path built a temp codec2 from rx_encode_frame[3] whenever the frame header did not match ours. codec2_create returns NULL for an invalid mode byte, and the next call dereferenced it. Only decode frames that carry our own header (magic + mode) and drop the rest, so the untrusted mode byte never reaches codec2_create. The decode loop advanced by the frame size while testing only i < rx_encode_frame_index, so a payload length that was not a multiple of the frame size read past the received data and could read past rx_encode_frame. Bound each read to i + frameSize <= the received length clamped to the buffer, and clamp the receive memcpy to the buffer. Behavior change: audio frames whose codec2 mode differs from this node's configured mode are dropped instead of decoded with a temporary codec.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAudio RX processing bounds payload copies and validates frame headers and sizes before decoding. TX audio capture now accumulates I2S data using byte-based frame lengths and copies complete frames into the speech buffer. ChangesAudioModule hardening
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/modules/esp32/AudioModule.cpp`:
- Around line 82-83: Update the i2s_write call in the audio output path to pass
the decoded buffer size in bytes by multiplying adc_buffer_size by
sizeof(int16_t), while leaving the buffer pointer and other arguments unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ef7b65b8-7210-477b-b9e6-81ff33e2bd57
📒 Files selected for processing (1)
src/modules/esp32/AudioModule.cpp
|
Addressed in acc4210. Confirmed and correct: Two caveats: this is a pre-existing bug (the original |
|
Follow-up: the sample/byte fix wasn't complete with just the write, so 6d760be fixes the mirror on the capture path. There were three i2s spots in play:
Same caveat as before: ESP32/SX1280-only, no native build, so both directions are verified from the in-module sample/byte convention rather than on hardware. This one reworks the capture state machine, so it's worth a real 2.4 GHz round-trip check before relying on it. |
⚡ Try this PR in the Web FlasherNote Building this pull request… the flash button, badges and supported-board |
Two issues in the codec2 RX path, both reachable from a crafted AUDIO_APP payload (module is
ESP32 + 2.4 GHz / wide-LoRa only, so this affects those builds).
Unvalidated mode byte -> NULL dereference. When a received frame's header did not match
ours, the code built a temporary decoder from the frame's own mode byte:
codec2_createreturns NULL for an out-of-range mode, and the following calls dereference it.Now only frames that carry our own header (magic + mode) are decoded; anything else is dropped,
so an untrusted mode byte never reaches
codec2_create.Out-of-bounds read on a misaligned length. The decode loop stepped by the frame size but only
checked
i < rx_encode_frame_index, so a payload whose length was not a clean multiple of theframe size made the final
codec2_decoderead past the received data and potentially past the233-byte
rx_encode_frame. Each read is now bounded byi + frameSize <= rx_encode_frame_indexclamped to the buffer size, and the receive-side
memcpyis clamped to the buffer as well.Behavior change: a frame whose codec2 mode differs from this node's configured mode is dropped
rather than decoded with a temporary codec. Interoperable audio already requires both ends to run
the same codec2 bitrate, so cross-mode frames were not usefully decodable.
No test: the module is ESP32/SX1280-only and has no native build or existing test coverage, so
these changes are verified by inspection.
Summary by CodeRabbit