dr_flac is a single-header FLAC decoder for C and C++,
part of mackron’s dr_libs. It is also bundled in miniaudio, where it appears as ma_dr_flac.
Like most single-header libraries it gets copied into projects, so whatever copy a project
vendored is the copy it runs.
The bug needs one thing from the application: it has to open files with one of the
drflac_open_*_with_metadata() functions and read pRawData for CUESHEET blocks in its
callback. Plain decoding, and metadata callbacks that don’t touch a cue sheet’s raw bytes,
never see it.
One free in the wrong place
drflac__read_and_decode_metadata() reads each metadata block into a buffer, fills in a
drflac_metadata struct that points at it, and hands that struct to the application’s onMeta
callback. Every block type frees the buffer once the callback has returned. The CUESHEET case
frees it first:
/* The original data is no longer needed. */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
pRawData = NULL;
onMeta(pUserDataMD, &metadata);
Setting the local pRawData to NULL only clears the local. metadata.pRawData still holds
the old address, so the callback gets a struct whose raw-data pointer points into freed memory.
Seeing it
A metadata callback that sums the bytes of the block is enough:
static void on_meta(void *user, drflac_metadata *m)
{
if (m->type != DRFLAC_METADATA_BLOCK_TYPE_CUESHEET) return;
const drflac_uint8 *p = (const drflac_uint8 *)m->pRawData;
unsigned sum = 0;
for (drflac_uint32 i = 0; i < m->rawDataSize; i++) sum += p[i];
printf("checksum=%u\n", sum);
}
/* drflac_open_file_with_metadata("cue.flac", on_meta, NULL, NULL); */
With a small cue sheet the callback reads stale freed memory: the checksum comes out as 1695 instead of 276. AddressSanitizer confirmed it as a use after free.
Crashing it
Make the block bigger and the picture changes. Pad the CUESHEET block to about 300 KB and glibc returns a buffer that large to the OS when it is freed, so the callback’s read lands on unmapped memory. A normal, non-ASan build then dies with SIGSEGV.
The same file still plays fine in ffmpeg, so the problem is what the application’s callback is handed, not the file.
I didn’t attempt anything beyond the crash, and I’m not claiming anything beyond it.
How I found it
I wrote a fuzz harness for dr_flac’s metadata API. The project’s existing fuzzer opens files
without a metadata callback, so onMeta is never called and the CUESHEET path after the free
is never reached. A harness that supplies a callback and actually reads the block is what
turned it up.
How far back
| Form | First affected | Notes |
|---|---|---|
dr_flac.h |
f4cccfc (2022-08-25) |
first released in v0.12.39 (2022-09-17); affected through v0.13.4 |
ma_dr_flac in miniaudio |
not bisected | present on master (9634bed) and dev (78c8a43) as of Oct 2026; not yet patched |
Before reporting I checked it was actually new. I searched dr_libs issues and PRs for
“cuesheet”, “pRawData”, “use after free” / UAF and onMeta, and miniaudio issues for the same
terms. Nothing matched. The closest was dr_libs #318 (July 2026), a SEEKTABLE pRawData size
mismatch, which is a different bug, and that report listed the CUESHEET handler as fine. No CVE
or advisory existed for it.
The fix
Move the free below the onMeta call, so the CUESHEET case frees the buffer after the callback
like the other block types do:
onMeta(pUserDataMD, &metadata);
/* The original data is no longer needed. */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
pRawData = NULL;
The real commit orders it the same way: onMeta first, then the free of pRawData, then the
free of pTrackData. The maintainer’s commit also fixed a misaligned read I mentioned in the report. Thanks to
mackron for the quick fix.
Timeline
| Date | |
|---|---|
| 2026-10-14 | Found and confirmed with AddressSanitizer |
| 2026-10-18 | Reported as issue #345 |
| 2026-10-20 | Fixed upstream in 9fa8c3c; issue closed |
The project’s SECURITY.md asks for security reports to go in as public issues, so this did.
A CVE has been requested but isn’t assigned yet. The honest
status is: found, reported, fixed, CVE pending.
Verification: found and confirmed with clang 18.1.3 and AddressSanitizer on Ubuntu 24.04; the
non-ASan crash was with gcc 13.3 -O2. Confirmed on dr_libs master dfe8377 (v0.13.4) and on
miniaudio master 9634bed and dev 78c8a43. After the fix (9fa8c3c) both sample files return
the correct checksum and run clean under ASan and UBSan.