Skip to content

LoadMusicStreamFromPhysFS leaks the file buffer #58

Description

@RobLoach

Summary

LoadMusicStreamFromPhysFS() (raylib-physfs.h, around line 462) loads the whole file into a buffer via LoadFileDataFromPhysFS() and passes it to LoadMusicStreamFromMemory(). raylib's memory-based music decoders (stb_vorbis, dr_wav, dr_mp3, the module players) reference the caller's buffer rather than copying it, so the buffer must stay alive while the music plays. The wrapper correctly does not free it on success:

Music music = LoadMusicStreamFromMemory(extension, fileData, bytesRead);
// Unload the file data if the music failed to load.
if (music.ctxData == NULL) {
    UnloadFileData(fileData);
}
return music;

…but the fileData pointer is then lost. UnloadMusicStream() only frees raylib's decoder state, not our buffer — so every successful LoadMusicStreamFromPhysFS() permanently leaks the entire file contents. For a game cycling through music tracks this grows unbounded.

Suggested Implementation

Add a matching unloader that frees the tracked buffer:

RAYLIB_PHYSFS_DEF void UnloadMusicStreamFromPhysFS(Music music);  // Unload music loaded from PhysFS, freeing its file buffer
  • Keep a small internal registry (static array of {void* ctxData; unsigned char* fileData;} entries, grown with MemRealloc) mapping each successfully loaded Music.ctxData to its buffer.
  • UnloadMusicStreamFromPhysFS() calls UnloadMusicStream(music), then looks up music.ctxData in the registry, frees the buffer with UnloadFileData(), and removes the entry.
  • Free any leftover registry entries in ClosePhysFS() as a safety net (and free the registry itself).
  • Document in the LoadMusicStreamFromPhysFS() doc comment that it must be paired with UnloadMusicStreamFromPhysFS() (plain UnloadMusicStream() leaks the buffer), and update the README API block.

Alternative considered: freeing fileData right after LoadMusicStreamFromMemory() is wrong — the decoders stream from that buffer and it would be a use-after-free during playback.

QA

  • Test in test/raylib-physfs-test.c: load a music file from the mounted resources (an .ogg/.xm in test/resources/), assert ctxData != NULL, then UnloadMusicStreamFromPhysFS() — should not crash, and repeated load/unload in a loop should not grow memory (spot-check with valgrind locally / ASAN).
  • Missing-file path still returns a zeroed Music and frees nothing.
  • examples/audio/audio_music_stream.c updated to use the new unloader.

Context

  • The existing if (music.ctxData == NULL) UnloadFileData(fileData); failure-path handling was deliberate (buffer must outlive playback), so this is a design gap, not a regression from a specific commit.
  • From a planning pass on 2026-07-20. No dependencies on other filed issues.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions