ComponentizeJS icon indicating copy to clipboard operation
ComponentizeJS copied to clipboard

Out of bounds memory access when using fetch

Open mschuwalow opened this issue 8 months ago • 1 comments

Originally opened in the jco repository (https://github.com/bytecodealliance/jco/issues/668). Also posting here for visibility. Feel free to close one of them.


Here is a minimal reproduction:

Create the following files

wit/component.wit:

package pack:name;

world component {
  import wasi:http/[email protected];
  export wasi:cli/[email protected];
}

src/main.js:

const run = {
    async run() {
        try {
            var n = 0;
            let requests = [];
            while (n < 1000) {
                requests.push(fetch("https://jsonplaceholder.typicode.com/todos/1"));
                n += 1;
            }
            for (const request of requests) {
                const response = await request;
                const data = await response.json();
                console.log("HTTP Response:", data);
            }
        }
        catch (error) {
            console.error("HTTP Request Error:", error);
        }
    }
};

export { run };

Run the following commands:

  • jco componentize -w wit -o dist/main.wasm src/main.js
  • wasmtime run -S http=y dist/main.wasm

Output:

HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
HTTP Response: { userId: 1, id: 1, title: "delectus aut autem", completed: false }
Error: failed to run main module `dist/main.wasm`

Caused by:
    0: failed to invoke `run` function
    1: error while executing at wasm backtrace:
           0: 0x7fcce3 - <unknown>!<wasm function 13434>
           1: 0x242dac - <unknown>!<wasm function 5248>
           2: 0x80c526 - <unknown>!post_wasi:cli/[email protected]#run
    2: memory fault at wasm address 0x9ad94527 in linear memory of size 0xb10000
    3: wasm trap: out of bounds memory access

Exception: wasmtime exited with 134

Environment

System: Apple M4 Pro OS: macOS 15.4.1 (24E263)

Tool Versions:

wasmtime 26.0.1 jco 1.10.2 componentize-js 0.18.0

mschuwalow avatar May 15 '25 22:05 mschuwalow

I had ~~I'm having~~ similar error.

Edit: I have rewritten a rust host with wastime 38.0.2 and now I don't see that problem. However I don't know which change exactly did trick.

Environment

  • ComponentizeJS: 0.19.2
  • wasmtime: 26.0.1
  • StarlingMonkey runtime
  • System: Apple M1 Pro
  • OS: macOS 15.6.1

Findings

Initially I had it with that code.

// Test fetch + Response object
export async function greet(name) {
  console.log(`About to call fetch for: ${name}`);

  try {
    const response = await fetch('http://localhost:3001/fast');
    const data = await response.json();
    console.log('Fetch succeeded:', JSON.stringify(data));

    // Return a Response object like our user code does
    const result = new Response(
      JSON.stringify({
        success: true,
        data,
      }),
      {
        headers: { 'Content-Type': 'application/json' },
      },
    );

    // Extract text from response to return as string
    return await result.text();
  } catch (error) {
    console.error('Fetch failed:', error.message);
    return `Error: ${error.message}`;
  }
}

I tried to somehow isolate what is wrong and here is the code that works for me, but when I uncomment related line it starts to fail.

// Exact reproduction of Issue #224
export async function greet(name) {
  console.log(`Testing 1000 fetches for: ${name}`);

  try {
    var n = 0;
    let requests = [];
    while (n < 1000) {
      requests.push(fetch('http://localhost:3001/fast'));
      n += 1;
    }

    console.log('Created 1000 fetch promises');

    for (const request of requests) {
      const response = await request;
      // commented - works
      // uncommented - error while executing at wasm backtrace
      // const data = await response.json();
    }

    console.log('All 1000 requests completed!');
    return `Hello ${name}, fetched 1000 times!`;
  } catch (error) {
    console.error('Fetch failed:', error.message);
    return `Error at request ${n}: ${error.message}`;
  }
}

marcin-kasprowicz avatar Oct 21 '25 14:10 marcin-kasprowicz