8000 Resource manager: free a data stream's file path when a synchronous init fails by sjoblomj · Pull Request #1176 · mackron/miniaudio · GitHub
[go: up one dir, main page]

Skip to content

Resource manager: free a data stream's file path when a synchronous init fails - #1176

Merged
mackron merged 1 commit into
mackron:dev-0.12from
sjoblomj:fix-stream-init-path-leak
Oct 22, 2026
Merged

mackron merged 1 commit into
mackron:dev-0.12from
sjoblomj:fix-stream-init-path-leak

Conversation

@sjoblomj
Copy link
Copy Markdown
Contributor

Resource manager: free a data stream's file path when a synchronous init fails

Summary

On dev-0.12 (but not on dev), ma_resource_manager_data_stream_init_ex() copies the file path into the data stream (pFilePath / pFilePathW), and only ma_resource_manager_data_stream_uninit() frees that copy. A caller doesn't uninitialize a data stream that failed to initialize, and neither does ma_sound. So when a synchronous (or WAIT_INIT) init fails, for example because the file isn't audio, the copy leaks.

The leak was introduced by b73f018 ("Add support for copying data streams"), which is only on dev-0.12. Before it, and still on dev and in 0.11.25, the path copy goes to the load job, which frees it whether the load succeeds or fails. b73f018 moved the copy into the data stream so that ma_resource_manager_data_stream_init_copy() can reuse it, and left freeing it to uninit.

The fix

In ma_resource_manager_data_stream_init_ex(), on the error path after waiting for the load job: free pFilePath and pFilePathW, and set them to NULL.

The load job only reads the path while initializing the decoder, which it does before it signals the init notification. So by the time the init has waited on that notification, the job is done with the path.

The asynchronous path is unchanged. There, an init that fails later still leaves the caller with a data stream to uninitialize, and uninit frees the path as before.

Reproduction

#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include <stdio.h>

int main(void)
{
    ma_resource_manager_config resourceManagerConfig;
    ma_resource_manager resourceManager;
    ma_resource_manager_data_stream stream;
    ma_result result;
    FILE* pFile;

    /* A file that isn't audio. */
    pFile = fopen("not_audio.bin", "wb");
    fputs("This is not audio.", pFile);
    fclose(pFile);

    resourceManagerConfig = ma_resource_manager_config_init();
    ma_resource_manager_init(&resourceManagerConfig, &resourceManager);

    /* Synchronous init, which fails because the file can't be decoded. */
    result = ma_resource_manager_data_stream_init(&resourceManager, "not_audio.bin", 0, NULL, &stream);
    printf("ma_resource_manager_data_stream_init: %s\n", ma_result_description(
8000
result));

    ma_resource_manager_uninit(&resourceManager);
    return 0;
}
gcc stream_init_leak.c -o stream_init_leak -std=c89 -Wall -Wextra -fsanitize=address -g -lm -lpthread -ldl
./stream_init_leak

Before, on b6df52c:

Direct leak of 14 byte(s) in 1 object(s) allocated from:
    #0 malloc
    #1 ma__malloc_default
    #2 ma_malloc
    #3 ma_copy_string
    #4 ma_resource_manager_data_stream_init_ex
    #5 ma_resource_manager_data_stream_init
    #6 main
SUMMARY: AddressSanitizer: 14 byte(s) leaked in 1 allocation(s).

After: Invalid file, and no leak.

ma_sound_init_from_file() with MA_SOUND_FLAG_STREAM leaks the same way, once per failed init.

Disclosure

The bug was found and fixed with the help of Claude, and it also helped create this PR description.

Licensing

I am ok with the code being put in the public domain.

@mackron
mackron merged commit fc4ebd6 into mackron:dev-0.12 Oct 22, 2026
@mackron
mackron commented Oct 22, 2026
Copy link
Copy Markdown
Owner

Look reasonable. Merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0