SDL_image icon indicating copy to clipboard operation
SDL_image copied to clipboard

SEGMENTATION FAULT with IMG_Load_RW in SDL2

Open oregu1 opened this issue 1 year ago • 6 comments

Get into segmentation fault while running the project in mix with SDL2 & Wasm Workers. Segfault appears at SDL2 method IMG_Load_RW execution but can't understand why.

Version of emscripten/emsdk:

emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.47 (431685f05c67f0424c11473cc16798b9587bb536)
clang version 18.0.0 (https://github.com/llvm/llvm-project 21030b9ab4487d845e29792063f5666d8c4b8e09)
Target: wasm32-unknown-emscripten
Thread model: posix

Uncaught RuntimeError: Aborted(segmentation fault)

Uncaught RuntimeError: Aborted(segmentation fault)
    at abort (mob1x.js:862:40)
    at segfault (mob1x.js:589:2)
    at imports.<computed> (mob1x.js:8326:24)
    at mob1x.wasm.SAFE_HEAP_STORE_i32_4_4 (mob1x.wasm:0xff1e37)
    at SDL_RWtell (SDL_rwops.c:798)
    at IMG_LoadPNG_RW (IMG_png.c:258)
    at IMG_LoadTyped_RW (IMG.c:289)
    at IMG_Load_RW (IMG.c:212)
    at mob1x.wasm._KBTexture2D::InitWithImage(char const*, DIRECTORY_TYPES, bool) (https://------/mob1x.wasm)
    at mob1x.wasm._KBTextureStorage::LoadTextureFromFile(char const*, DIRECTORY_TYPES, bool) (https://------/mob1x.wasm)
    at mob1x.wasm._KBGUIFont::InitFonts() (https://------/mob1x.wasm)
    at mob1x.wasm._KBGUI::Init() (https://------/mob1x.wasm)

user callback triggered after runtime exited or application aborted. Ignoring.

user callback triggered after runtime exited or application aborted.  Ignoring. 2 [mob1x.js:4540:6](https://------/mob1x.js)
    callUserCallback https://------/mob1x.js:4540
    doCallback https://------/mob1x.js:6848
    reportReadyStateChange https://------/mob1x.js:6869
    onreadystatechange https://------/mob1x.js:6703
    (Async: EventHandlerNonNull)
    fetchXHR https://------/mob1x.js:6694
    performUncachedXhr https://------/mob1x.js:6874
    onsuccess https://------/mob1x.js:6786
    (Async: EventHandlerNonNull)
    fetchLoadCachedData https://------/mob1x.js:6768
    _emscripten_start_fetch https://------/mob1x.js:6905
    x https://------/mob1x.js:8326
    emscripten_fetch https://------/mob1x.wasm:1527442
    mob1x.wasm._KBHTTPQuery::Run() https://------/mob1x.wasm:1548884
    mob1x.wasm._KBHTTPManager::RunQuery(_KBHTTPQuery*) https://------/mob1x.wasm:1838212
    mob1x.wasm._KBHTTPManager::AddQuery(_KBHTTPQuery*) https://------/mob1x.wasm:1837294
    mob1x.wasm._KBHTTPManager::Get(char const*, _KBHTTPQueryCallback*, bool, int) https://------/mob1x.wasm:614490
    mob1x.wasm._KBResourceManager::CheckoutResourceVersion(int) https://------/mob1x.wasm:1983027
    mob1x.wasm._KBResourceManager::Init() https://------/mob1x.wasm:883127
    mob1x.wasm._KBCore::Init() https://------/mob1x.wasm:879105
    mob1x.wasm.initCore() https://------/mob1x.wasm:14950189
    mob1x.wasm.allocateSystem() https://------/mob1x.wasm:96246
    dynCall_v https://------/mob1x.wasm:16697430
    x https://------/mob1x.js:8348
    createExportWrapper https://------/mob1x.js:884
    browserIterationFunc https://------/mob1x.js:6291
    callUserCallback https://------/mob1x.js:4544
    runIter https://------/mob1x.js:4618
    Browser_mainLoop_runner https://------/mob1x.js:4519
    (Async: FrameRequestCallback)
    requestAnimationFrame https://------/mob1x.js:4858
    Browser_mainLoop_scheduler_rAF https://------/mob1x.js:1557
    Browser_mainLoop_runner https://------/mob1x.js:4523
    (Async: FrameRequestCallback)
    requestAnimationFrame https://------/mob1x.js:4858
    Browser_mainLoop_scheduler_rAF https://------/mob1x.js:1557
    Browser_mainLoop_runner https://------/mob1x.js:4523
    (Async: FrameRequestCallback)
    requestAnimationFrame https://------/mob1x.js:4858
    Browser_mainLoop_scheduler_rAF https://------/mob1x.js:1557
    setMainLoop https://------/mob1x.js:4531
    _emscripten_set_main_loop https://------/mob1x.js:6292

CMakeLists.txt config

(CMAKE_BUILD_TYPE STREQUAL "Debug")
	set(USE_FLAGS "-sUSE_ZLIB=1 -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png,jpg'] -O0 -flto -g -sWASM_WORKERS=1" CACHE STRING "Compilation flags" FORCE)

	set(LINKING_FLAGS "--preload-file ${ASSETS_PATH}/assets@/assets -sFULL_ES2 -sWASM=1 -sFETCH=1 --use-preload-plugins -sEXIT_RUNTIME=1 -sALLOW_MEMORY_GROWTH=1 -sEXPORTED_FUNCTIONS=['_main','_photoTaken','_malloc','_setNotificationToken','_interceptPushNotification'] -sEXPORTED_RUNTIME_METHODS=[cwrap] -sFORCE_FILESYSTEM=1 -lidbfs.js -O0 -flto -g -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY -sASYNCIFY_STACK_SIZE=65536 -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png,jpg'] -sSAFE_HEAP=1" CACHE STRING "Linking flags" FORCE)

	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}")

	add_definitions(-DTARGET_WEB -DDEBUG -DMA_ENABLE_AUDIO_WORKLETS)

Source code

SDL_Surface *imgRef = 0;
                
    if(filePath)
    {
        char * buffer = 0;
        long bufferLength = 0;
        
        FM->LoadDataAsset(filePath, &buffer, &bufferLength);
        
        if(buffer)
        {
            SDL_RWops* rw = SDL_RWFromConstMem(buffer, bufferLength);
            if(rw)
                imgRef = IMG_Load_RW(rw, 1); //1 to release/free RWops
            free(buffer);
        }
    }

    if (imgRef)
    {
        if (imgRef->format->format == SDL_PIXELFORMAT_ABGR8888)
            pixelFormat = kTexture2DPixelFormat_RGBA8888;
        else if (imgRef->format->format == SDL_PIXELFORMAT_RGBA4444)
            pixelFormat = kTexture2DPixelFormat_RGBA4444;
        else if (imgRef->format->format == SDL_PIXELFORMAT_RGB24)
            pixelFormat = kTexture2DPixelFormat_RGB24;
        else if (imgRef->format->format == SDL_PIXELFORMAT_INDEX8)
            pixelFormat = kTexture2DPixelFormat_A8;

        InitWithData(imgRef->pixels, pixelFormat, imgRef->w, imgRef->h, alias);
        
        SDL_FreeSurface(imgRef); 
        return true;
    }
    
    return false;
LoadDataAsset(const char * assetName, char ** buffer, long* length)
{
    FILE * file = fopen(assetName, "rb");
    if(file) {
        fseek(file, 0, SEEK_END);
        *length = ftell(file);
        fseek(file, 0, SEEK_SET);

        *buffer = (char *) malloc(*length + 1);
        fread(*buffer, *length, 1, file);
        
        fclose(file);
    }
}

oregu1 avatar Feb 09 '24 06:02 oregu1

Do you have the source code for SDL_image and SDL that is being used? Those line numbers don't correspond to the latest release.

slouken avatar Feb 09 '24 09:02 slouken

Our project is made with emscripten version 3.1.47 where SDL2 as port is used and I can't say what version is ported there. Actually I don't know right now how to do it. The only thing that I found is that in emsdk folder generated by emscripten lies sdl2_image.6.0.zip & sdl2.24.2.zip archives. Hope it will help somehow.

oregu1 avatar Feb 09 '24 09:02 oregu1

Do you have the source code for SDL_image and SDL that is being used? Those line numbers don't correspond to the latest release.

@slouken I've found what SDL version is used in my project. It's 2.24.2 for SDL and 6.0 for SDL2_image

oregu1 avatar Feb 13 '24 05:02 oregu1

@slouken Sam any ideas???

oregu1 avatar Feb 13 '24 08:02 oregu1

Yeah, I'm not sure what's happening here. The likely cause would be a NULL SDL_RWops, but we check for that right before calling SDL_RWtell(), and you check for that in your code. Did you ever figure this out?

slouken avatar Jan 31 '25 23:01 slouken

So this is pretty unlikely, but... You don't check the return value of fread. If it does a partial read (or fails), the buffer will contain uninitialized data. It's possible this could trigger an edge case in the image decoder and lead to undefined behaviour.

(You also don't check the return value of ftell, which could lead to getting an inaccessible non-NULL pointer from malloc(0), but this should crash in fread already and if it doesn't SDL_RWFromConstMem should return an error because it takes a signed length and checks that it's positive)

maia-s avatar Feb 01 '25 09:02 maia-s