"Uncaught RuntimeError: memory access out of bounds" when trying to load file in WASM of C-program

1.9k views Asked by At

I try to load a file from my local filesystem, which is then loaded into MEMFS (provided by Emscripten) and pass the file name as one argument to a C program which is cross compiled as WebAssembly using Emscripten. When calling the function to load the file I get the error: Uncaught RuntimeError: memory access out of bounds. What am I doing wrong? Are the parameters handed-over incorrectly or is the file access not correct?

C program, has standard main and parameters are passed correctly, loading the file looks like this (in an abstract):

int CLASS main(int argc, const char **argv)
{
   ...
   FILE *ifp;
   const char *ifname;
   ...
   // Start - debug code
   for (int i = 0; i < argc; i++)
   {
      // ==> This prints the arguments correctly in the browser console, so the hand-over works.
      printf("argv[%d] = %s\n", i, argv[i]);
   }
   char cwd[1024];
   if (getcwd(cwd, sizeof(cwd)) != NULL)
   {
       // Also prints the MEMFS directory structure correctly.
       printf("Current working directory: %s\n", cwd);
   }
   // End - debug code
   ifname = argv[arg];
   if (!(ifp = fopen(ifname, "rb")))
   {
       perror(ifname);
       continue;
   }
...

In JavaScript it's called like this:

async _runProgram(args) {
    // Load the emscripten wrapper
    this._dcrawModule = await Module();

    // Convert the JavaScript array to a C-style null-terminated string array
    const paramStrings = args.map((str) => this._dcrawModule.allocateUTF8(str));
    
    // Allocate memory for the array of pointers
    const paramArray = this._dcrawModule.stackAlloc((paramStrings.length + 1) * 4); 
    paramStrings.forEach((strPtr, i) => {
        this._dcrawModule.setValue(paramArray + i * 4, strPtr, 'i32');
    });
    
    // Null-terminate the array
    this._dcrawModule.setValue(paramArray + paramStrings.length * 4, 0, 'i32');

    // Call the `_main` method with the parameter array
    return this._dcrawModule['_main'](paramStrings.length + 1, paramArray, 0);
}

The file itself is loaded into MEMFS doing the following and then the '_runProgram' function is called:

...
// Create a workspace directory in the emscripten virtual file system and go to it
FS.mkdir('/workspace');
FS.chdir('/workspace');

if (raw_file) {
    FS.createDataFile('/workspace/', 'raw_buf', raw_file, true, true);
    args.push('/workspace/raw_buf');
    //args.push('raw_buf'); //Tried also this and other variants, no success.
}
...
this._runProgram(args);

Does anyone has experience with Emscripten, MEMFS, WebAssembly and opening a file in a C-program? My preference would be to not change the C-program as this is not mine and I want to avoid having to adapt it every time it might be updated in the future (in addition my C knowledge is quite limited :-P) (full c program can be found here: https://dechifro.org/dcraw/dcraw.c)

Flags used to run the emcc compiler are the following:

FLAGS = -O0 \
          -s ALLOW_MEMORY_GROWTH=1 \
          -s TOTAL_MEMORY=134217728 \
          -s NO_EXIT_RUNTIME=1 \
          -s FORCE_FILESYSTEM=1 \
          --memory-init-file 0 \
          -s MODULARIZE=1 \
          -s WASM=1 \
          -s EXPORT_ES6=1 \
          -s EXPORTED_FUNCTIONS="['_main']" \
          -s EXPORTED_RUNTIME_METHODS=FS,setValue,stringToUTF8,allocateUTF8,stackAlloc  \
          -DNODEPS=1
0

There are 0 answers