Skip to content

Handle DirectMusic loader creation failures safely - #985

Closed
Solessfir wants to merge 1 commit into
Try:masterfrom
Solessfir:dmusic-loader-failure
Closed

Solessfir wants to merge 1 commit into
Try:masterfrom
Solessfir:dmusic-loader-failure

Conversation

@Solessfir

Copy link
Copy Markdown
Contributor

When DirectMusic loader initialization fails, DmLoader_create can free its allocation while leaving the output pointer non-null. OpenGothic then passes that dangling pointer to DmLoader_addResolver and later DmLoader_release.

Clear the pointer on failure and register the resolver only after successful initialization. This prevents use-after-free during startup and shutdown.

Found while investigating a Gothic 1 crash on Quit: loader initialization failed, and shutdown crashed while destroying the VFS tree. The change eliminated that native crash in the Android build. It affects shared resource initialization and requires no Android code.

@Try Try left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Solessfir!

I think it would be right to adjust code of dmusic rather. Also wonder, how did you hit this case? Did mtx_lock failed for you?

Comment thread common/resources.cpp
DmResult rv = DmLoader_create(&dmLoader, DmLoader_DOWNLOAD);
if(rv != DmResult_SUCCESS) {
Log::e("Failed to created DmLoader object. Out of memory?");
dmLoader = nullptr;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure, It's not intended for library code (DmLoader_create) to change &dmLoader on error.
So, correct way to fix it is to PR into DirectMusic.

Comment thread common/resources.cpp

DmLoader_addResolver(dmLoader, [](void* ctx, char const* name, size_t* len) -> void* {
else {
DmLoader_addResolver(dmLoader, [](void* ctx, char const* name, size_t* len) -> void* {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also not need - DmLoader_* meant to early out and return DmResult_INVALID_ARGUMENT, for null-argument.

@Solessfir

Copy link
Copy Markdown
Contributor Author

Makes sense. I've prepared and committed the fix in dmusic locally: leave the output pointer untouched on failure and assign it only after initialization succeeds.

- DmLoader* new = *slf = Dm_alloc(sizeof *new);
+ DmLoader* new = Dm_alloc(sizeof *new);

Then add *slf = new; after the cache/list initialization, just before returning DmResult_SUCCESS. The dmusic Release build passes with MSVC.

You're right about the resolver guard too: once the loader pointer stays null, DmLoader_addResolver already handles it.

I found this while investigating an Android crash when quitting Gothic 1. The startup log reported loader creation failure, but didn't record the result code. The dangling-pointer path I found is the mtx_init failure path, not mtx_lock. I can't confirm what caused the original initialization failure from that log alone.

@Solessfir

Copy link
Copy Markdown
Contributor Author

Opened the dmusic fix here: GothicKit/dmusic#14. It assigns the output pointer only after initialization succeeds. This replaces the caller-side workaround proposed here once OpenGothic updates its dmusic submodule.

@Try

Try commented Sep 10, 2026

Copy link
Copy Markdown
Owner

I'll close this one for now - will update dmusic, once PR is merged

@Try Try closed this Sep 10, 2026
@Solessfir
Solessfir deleted the dmusic-loader-failure branch September 12, 2026 16:15
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