Two OOB reads in L3_huffman main_data buffer access — redirected from mackron/dr_libs
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 50/100
Research direction
Both reads live in L3_huffman at minimp3.h:742: the CHECK_BITS macro at :767 (invoked at :810, :818, :846, :873) and the four-byte bs_cache prefill at :773-774. First read how bs->buf, bs->pos and bs->limit are maintained, and note the only existing guard in the function, if (BSPOS > layer3gr_limit) break; at :861, sits in the count1 path after big_values has exited. Gate both reads on the buffer end, then confirm ordinary MP3s decode unchanged using the repo's existing test files, since the PoC inputs are not public.
Written by the indexing model from the issue text.
Description
Hello lieff,
Reporting two heap OOB read vulnerabilities in L3_huffman
(minimp3.h:742) — both involve unchecked reads via bs_next_ptr,
a pointer that walks through the maindata bitstream buffer. They were originally found in
mackron/dr_libs (where this code is forked as drmp3_L3_huffman in
dr_mp3.h). David Reid (mackron) acknowledged the reports on 2026-04-25
and asked us to redirect them upstream:
> "The decoders for these are taken straight from minimp3 so I'm
> not too sure how I want to handle this. ... I think it's probably
> best you report this to the upstream project, lieff/minimp3."
> — David Reid, 2026-04-25
We grouped the two issues into a single report because they share the
same function (L3_huffman), the same buffer (bs_next_ptr walking
through maindata), and the same root cause (no bounds enforcement on
bs_next_ptr against the buffer end). Happy to split into two issues
if you prefer.
These are distinct from existing minimp3 issues we reviewed:
- #122 (shift UB at minimp3.h:379, 649): Layer 1/2 requantize —
DIFFERENT function and bug class.
- #134 (Xing/Info OOB, ByamB4 2026-02-17): mp3dec_check_vbrtag —
DIFFERENT function (overlaps separately with our dr_libs Advisory
5, which mackron fixed in dr_mp3 commit ea24c1f; minimp3-side
fix is pending).
- #140 (Xing/Info int overflow, HOHK0923 2026-03-13): same
Xing/Info extension layer as #134, DIFFERENT from L3_huffman.
- #144 (vague heap overflow, programmervuln 2026-04-23): private
body. We could not determine overlap from the public metadata;
please flag if it does and we will close as duplicate.
=== Issue 1: CHECK_BITS macro advances bs_next_ptr without bounds check (dr_libs Advisory #1) ===
Affected: L3_huffman (minimp3.h:742), big_values decoding loop
Location: macro definition at minimp3.h:767, invoked at minimp3.h:810,
818, 846 (big_values paths) and 873 (count1 path)
Severity: High (CVSS ~8.6 in our analysis; please re-rate as you
see fit)
CWE: CWE-125 (Out-of-bounds Read)
Vulnerable code (minimp3.h verbatim):
// minimp3.h:767
#define CHECK_BITS while (bs_sh >= 0) { bs_cache |= (uint32_t)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
The macro unconditionally advances bs_next_ptr while bs_sh >= 0,
with no check against the end of the maindata buffer. During
big_values Huffman decoding of a crafted MP3 file (where pos drifts
beyond the granule's main_data extent), this reads heap memory past
the bitstream buffer end.
The only BSPOS bound check we found in L3_huffman is in the count1
section (minimp3.h:861 — if (BSPOS > layer3gr_limit) break;), which
runs after the big_values loop has already exited. The big_values
section itself (minimp3.h:778–850) has no equivalent guard before each
CHECK_BITS invocation.
Impact: Heap out-of-bounds read. An attacker supplying a crafted MP3
may read out-of-bounds heap memory beyond the bitstream buffer.
Reachable from any untrusted MP3 input via public
mp3dec_decode_frame / mp3dec_load_* APIs.
Suggested fix: gate CHECK_BITS on bs_next_ptr < bs_end (passing
bs_end = bs->buf + bs->limit/8 + 1 or equivalent), e.g.:
#define CHECK_BITS \
while (bs_sh >= 0 && bs_next_ptr < bs_end) { \
bs_cache |= (uint32_t)*bs_next_ptr++ << bs_sh; \
bs_sh -= 8; \
}
=== Issue 2: 4-byte cache prefill OOB read at L3_huffman entry (dr_libs Advisory #8) ===
Affected: L3_huffman (minimp3.h:742), bs_cache initialization
Location: minimp3.h:773–774 (4-byte read at function entry, before
any CHECK_BITS invocation — distinct from Issue 1)
Severity: High (CVSS ~7.5 in our analysis; please re-rate as you see
fit)
CWE: CWE-125 (Out-of-bounds Read)
Vulnerable code (minimp3.h verbatim):
// minimp3.h:773
const uint8_t *bs_next_ptr = bs->buf + bs->pos/8;
// minimp3.h:774
uint32_t bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7);
The function entry unconditionally reads bs_next_ptr[0..3] (4 bytes)
without checking that 4 bytes remain in the buffer past bs->pos/8.
When the buffer layout leaves fewer than 4 bytes at the entry position,
this reads past the end before the big_values loop is even entered.
This is independent of Issue 1 — it triggers on the very first
function call of a granule, not during the loop.
Impact: Heap out-of-bounds read of 1–3 bytes (depending on remaining
buffer space at entry).
Suggested fix: validate that at least 4 bytes remain past bs->pos/8
at entry, or use a guarded byte-by-byte cache fill that respects the
buffer end.
=== Reproducer ===
We have proof-of-concept inputs for the dr_mp3 fork that trigger both
reads under AddressSanitizer. Since the dr_mp3 Huffman decoder is the
same code as minimp3's L3_huffman, we infer the same inputs would
reproduce on minimp3 with a thin driver (we have not run them against
lieff/minimp3 directly). Happy to share the inputs (and the fuzzing
harness) privately — please reply with a preferred channel (e.g.,
email at [email protected], or any other private channel you prefer).
We will not post the inputs in this issue while a fix is in progress.
=== Discovery method ===
Found via cross-model AI security review (Claude + GPT + Gemini,
independent analyses combined into a Union of findings) targeting
mackron/dr_libs commit 4d74802. Eight advisories were reported to
mackron on 2026-04-07; six are now resolved on the dr_libs side, two
(these two) are redirected here per David's note.
Thanks for maintaining minimp3.
Best,
Song Tae-Eun
[email protected]
- Dominant language
- C
- Stars
- 2k
- Forks
- 271
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from lieff/minimp3
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
[Bug]signed left-shift overflow in mp3dec_skip_id3v1 when parsing the APEv2 tag-size field (CWE-190)Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
-
Integer overflow in Xing/Info VBR header parsing leads to ~10TB allocation (DoS)Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 Half a day Newbie friendliness 30/100
-
Difficulty 3/5 1-2 days Newbie friendliness 50/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
python-pillow/Pillow#10370 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
elf_loader: Xtensa ignores the addend of R_XTENSA_GLOB_DAT / R_XTENSA_JMP_SLOT (C++ vtables)Possibly taken @diplfranzhoepfinger claimed this today. Openelf_loader
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
espressif/esp-iot-solution#813 ·
Maintainers usually reply within 1 day