Native Heap Corruption in BASS_MIDI_StreamSetFonts (Crash) in Android

Started by rrhh_fx,

rrhh_fx

Hi,

I am writing to report a crash in the latest BASSMIDI Android version (2.4.15.3) compared to previous one (specifically the release from May 2022). My application in production usage, frequently needs to release and restart the BASS engine (freeing streams and re-initializing) when loading different projects or switching between complex Activities.

During these transitions, depending on specific race conditions or state timing, it is possible for BASS_MIDI_StreamSetFonts to be called with a 0 handle or a 0 font count.

Old version handled this gracefully (likely returning FALSE or ignoring the call). The app remained stable but new version it causes a Native Heap Corruption (Scudo ERROR: corrupted chunk header), leading to an immediate SIGABRT crash.

Since this happens sporadically in production depending on timing, I created a stress test to consistently reproduce the crash. Please note: The code below is not the normal logic of my app, but it simulates the rapid initialization/destruction cycle that triggers the memory corruption in the new library.

private void testBass() {
    int i = 0;
    // Stress test loop to reproduce the heap corruption found in production
    while (i < 30) {
        if (BASS.BASS_IsStarted() == 0) {
            BASS.BASS_Init(-1, 48100, 0);
        }

        // THE TRIGGER:
        // Passing 0 as handle and null as fonts array (count 0).
        // While this is an invalid call, the library should reject it safely.
        // Instead, the new version corrupts the native memory here.
        BASSMIDI.BASS_MIDI_StreamSetFonts(0, (BASSMIDI.BASS_MIDI_FONT[]) null, 0 | BASSMIDI.BASS_MIDI_FONT_EX);

        i++;

        BASS.BASS_Stop();
        BASS.BASS_Free();
    }
}

The first loop completes with no troubles, but the crash appears on i = 1 or i =2. The log clearly shows memory corruption inside the library:

I scudo   : Scudo ERROR: corrupted chunk header at address 0x2000074b051bfd0
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE)...
F DEBUG   : Abort message: 'Scudo ERROR: corrupted chunk header at address 0x2000074b051bfd0'
F DEBUG   : backtrace:
...
F DEBUG   :       #08 pc 000000000000e018  /.../libbassmidi.so (offset 0x1e44000)
F DEBUG   :       #09 pc 00000000000097a4  /.../libbassmidi.so (offset 0x1e44000) (BASS_MIDI_StreamSetFonts+192)
F DEBUG   :       #10 pc 00000000000210a4  /.../libbassmidi.so (offset 0x1e44000) (Java_com_un4seen_bass_BASSMIDI_BASS_1MIDI_1StreamSetFonts+616)

It seems the new version attempts to access or free memory associated with the font array pointer before validating if the count/handle is valid, causing the corruption.

Could you please check this? I've had to revert to the old library to maintain stability in the meantime, but the old library does not comply with the 16KB page alignment requirement and I am urged to migrate to 16kb.

Thanks in advance

Ian @ un4seen

It's a realloc call in BASS_MIDI_StreamSetFonts that's crashing, apparently because of the new_size=0 parameter in the call when the font count is 0. The latest realloc spec states that new_size=0 behaviour is undefined, so here's an update that doesn't make the realloc call in this case:

    www.un4seen.com/stuff/bassmidi-android.zip

rrhh_fx

Quote from: Ian @ un4seenIt's a realloc call in BASS_MIDI_StreamSetFonts that's crashing, apparently because of the new_size=0 parameter in the call when the font count is 0. The latest realloc spec states that new_size=0 behaviour is undefined, so here's an update that doesn't make the realloc call in this case:

    www.un4seen.com/stuff/bassmidi-android.zip

Thank you Ian. I will test on Monday

rrhh_fx

It works perfectly, thanks for the fix and the explanation! Regarding the future of these updates, will the upcoming official BASS_MIDI releases include this fix as well?

On the other hand, I have a question regarding the BASS_FX build (0x02040812) you posted for 16kb support on the "stuff" folder. When you posted it, you included specific builds for BASS and BASS_MIX as well, and you advised not to mix with other builds, but my project uses many other add-ons (BASS_FLAC, BASS_ENC, BASSMIDI, etc.).

Since nearly a year has passed and all your official libraries on the website are now 16kb-compatible, can this specific 'stuff' version of BASS_FX be safely mixed with the current official releases of BASS and the rest of the add-ons? Thank you

Ian @ un4seen

Good to hear the update fixed the problem. The change will indeed also be in future releases.

Regarding the BASS_FX update, you can indeed use that with the latest versions of the other libraries, or indeed older versions of them :) ... I think the previous "don't mix versions" comment would have been about mixing the 64-bit (eg. arm64-v8a) 16kb update with the 32-bit (eg. armeabi-v7a) release of the same library because they had different version numbers (code differences).

rrhh_fx

Quote from: Ian @ un4seenGood to hear the update fixed the problem. The change will indeed also be in future releases.

Regarding the BASS_FX update, you can indeed use that with the latest versions of the other libraries, or indeed older versions of them :) ... I think the previous "don't mix versions" comment would have been about mixing the 64-bit (eg. arm64-v8a) 16kb update with the 32-bit (eg. armeabi-v7a) release of the same library because they had different version numbers (code differences).

Finally I had to revert back to the 2022 lib, since there are critical crashes with the lib you provided in the previous post. However it is something temporary, since it not 16kb aligned.

The application crashes with a native SIGSEGV or SIGABRT (Scudo ERROR: corrupted chunk header) when playing 2-3 notes after setting a SoundFont. It seems like memory corruption introduced in the newer library versions when handling Font Init/Free operations.

The following sequence works 100% fine on the 2022 lib, but crashes on the latest lib, usually when BASS_MIDI_StreamEvent is called, or shortly after. Even firt events are not sounding:

Quote// 1. Init new font
int newFont = BASSMIDI.BASS_MIDI_FontInit(new Asset(getAssets(), "preset_name"), 0);
BASSMIDI.BASS_MIDI_FONTEX[] sf = new BASSMIDI.BASS_MIDI_FONTEX[1];

if (newFont != 0) {
    // 2. Setup Stream
    // (In the real app, streamHandle is managed globally, but valid here)
    if (streamHandle == 0) {
        streamHandle = BASSMIDI.BASS_MIDI_StreamCreate(1, 0, 0);
        BASS.BASS_ChannelSetAttribute(streamHandle, BASS_ATTRIB_BUFFER, 0);
        BASS.BASS_ChannelPlay(streamHandle, false);
    }

    // 3. Prepare Font Struct
    sf[0] = new BASSMIDI.BASS_MIDI_FONTEX();
    sf[0].font = newFont;
    sf[0].spreset = 0;
    sf[0].sbank = 0;
    sf[0].dbank = 0;
    sf[0].dpreset = 0;
    sf[0].dbanklsb = 0;

    // 4. Apply Font
    BASSMIDI.BASS_MIDI_StreamSetFonts(streamHandle, sf, 1 | BASSMIDI.BASS_MIDI_FONT_EX);
}

// 5. Free old font (Swap logic)
if (oldFont != 0) {
    BASSMIDI.BASS_MIDI_FontFree(oldFont);
}
currentFont = newFont; 

// 6. TRIGGER CRASH
// Sending this line several times causes the SIGSEGV / Scudo Error in the new library version
BASSMIDI.BASS_MIDI_StreamEvent(streamHandle, 0, BASSMIDI.MIDI_EVENT_NOTE, MAKEWORD(midiNote, 127));


The logs show clear signs of heap corruption in the native layer

Quote// LOG 1: Scudo Corruption
I scudo  : Scudo ERROR: corrupted chunk header at address 0x2000074b051bfd0
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 16520 (pool-3-thread-1)
...
A Abort message: 'Scudo ERROR: corrupted chunk header at address 0x2000074b051bfd0'

// LOG 2: SIGSEGV Access Error
F libc    : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x100000a00
...
#00 pc 000000000004c878  /data/app/.../libbass.so
#01 pc 0000000000023d0c  /data/app/.../libbass.so
#02 pc 000000000000e22c  /data/app/.../libbassmidi.so
#03 pc 000000000001fca8  /data/app/.../libbass.so

Thanks.



EDIT1: maybe this is important, but previous to the above code, a BASSMIDI.BASS_MIDI_StreamSetFonts is called in the terms of the crash that was the origin of this conversation, i.e. a BASSMIDI.BASS_MIDI_StreamSetFonts with another different stream that is 0, with number of fonts 0

EDIT2: if avoided and not calling the function in the EDIT1, no sound is calling from the "streamHandle = BASSMIDI.BASS_MIDI_StreamCreate(1, 0, 0)" in the above code, but not crashing

EDIT3: Officail BASSMIDI 2.4.15.3 if properly calling BASSMIDI.BASS_MIDI_StreamSetFonts with valid parameters, it works well and plays streamHandle

Ian @ un4seen

It looks like that crash may have been in using a packed SF2 soundfont (SF2PACK file). Can you confirm whether that is the case, and if so, does the crash still happen if you use an unpacked version of the same soundfont?

rrhh_fx

Quote from: Ian @ un4seenIt looks like that crash may have been in using a packed SF2 soundfont (SF2PACK file). Can you confirm whether that is the case, and if so, does the crash still happen if you use an unpacked version of the same soundfont?

Yes! That is exactly what is happening. No crash when using the unpacked SF2 font, although the build.gradle does include the code below:

QuoteandroidResources {
        noCompress 'sf2', 'sf2pack', 'sfpack', 'ogg', 'sf3'
    }

As my edit was very close in time to your answer, I don't know if you could see my last edit.

Ian @ un4seen

OK. It seems like a sample from the SF2PACK soundfont is probably still being loaded after the soundfont is freed (BASS_MIDI_FontFree), but I'm not sure how that's happening, as any sample loading from the soundfont should get cancelled first. Does the crash still happen if you don't call BASS_MIDI_FontFree? Also, are the newFont and oldFont handles created from the same soundfont file or different files?

Anyway, I think I'll need to send you a debug version to get more info.

rrhh_fx

Quote from: Ian @ un4seenOK. It seems like a sample from the SF2PACK soundfont is probably still being loaded after the soundfont is freed (BASS_MIDI_FontFree), but I'm not sure how that's happening, as any sample loading from the soundfont should get cancelled first. Does the crash still happen if you don't call BASS_MIDI_FontFree? Also, are the newFont and oldFont handles created from the same soundfont file or different files?

Anyway, I think I'll need to send you a debug version to get more info.

Hi Ian,

The scenario is a bit complex to explain because I have multiple MIDI streams mixed with several audio streams. Generally, the MIDI streams correspond to specific instruments, and the one that is failing is a single stream that gets released/recreated every time the user changes the instrument.

In my specific case (and I am aware this might not be the "best practice"), on every Activity change (screen transition), I completely release BASS using BASS_Stop() and BASS_Free(). I do this because, without this full reset, the system eventually reaches a point where BASS stops producing sound for unknown reasons—even though the streams reportedly exist and BASS returns "Active" status. This reset and initialization process happens in a serialized, ordered thread.

Current BASS_MIDI web version (2.4.15.3) crashes at BASS_MIDI_StreamSetFonts under the conditions we previously discussed. However, it does not crash if I strictly avoid that specific condition.

The Test Version (provided in this thread) crashes or produces no sound even when avoiding that condition, in the case that SF2Pack is used.

The method to load the font is tested and it works well in previos BASS/BASS_MIDI versiones

Quotepublic void loadTouchFonts(int pack) {
    // Free the existing stream
    BASS_StreamFree(streamPlay);

    // Get the preset name and init the new font
    String currentPackName = getPresetNameByNumber(pack);
    int newFont = BASSMIDI.BASS_MIDI_FontInit(new Asset(getAssets(), currentPackName), 0);
   
    sf = new BASSMIDI.BASS_MIDI_FONTEX[1];

    if (newFont == 0) {
        return;
    } else {
        streamPlay = BASSMIDI.BASS_MIDI_StreamCreate(1, 0, 0);
        BASS.BASS_ChannelSetAttribute(streamPlay, BASS_ATTRIB_BUFFER, 0);
        boolean c = BASS_ChannelPlay(streamPlay, false);

        sf[0] = new BASSMIDI.BASS_MIDI_FONTEX();
        sf[0].font = newFont;
        sf[0].spreset = 0; // use all presets with -1
        sf[0].sbank = 0;   // use default bank(s)
        sf[0].dbank = 0;
        sf[0].dpreset = 0;
        sf[0].dbanklsb = 0;

        boolean streamnotas = BASSMIDI.BASS_MIDI_StreamSetFonts(streamPlay, sf, 1 | BASS_MIDI_FONT_EX);
    }

    // Free the old soundfont and update the reference
    BASSMIDI.BASS_MIDI_FontFree(previousFont);
    previousFont = newFont;
}

A debug version could be great. Thanks!