fix(wasi-shim): re-read memory.buffer after await in all host functions
WebAssembly memory can grow (and return a new ArrayBuffer) during an async host function call. Reading memory.buffer before await and using it after the await causes host functions (kv_get / crypto_decrypt / crypto_sign_rs256 / http_request) to write into a detached buffer, so the WASM side reads zero bytes → empty string → JSON parse failure. Fix: read inputs before await using the current buffer snapshot, then call memory.buffer again after the await to write the result. For crypto_sign_rs256 and http_request, input arrays are copied before await so the snapshot can be released. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -221,15 +221,17 @@ export function createWasiShim(stdinData: string, hostFunctions?: WasiHostFuncti
|
||||
headersPtr: number, headersLen: number, bodyPtr: number, bodyLen: number,
|
||||
outPtr: number, outLenPtr: number): Promise<number> => {
|
||||
if (!memory) return 1;
|
||||
const buf = memory.buffer;
|
||||
// 在 await 前讀完所有輸入(memory.buffer 在 await 後可能因 grow 而失效)
|
||||
const snapBuf = memory.buffer;
|
||||
const dec = new TextDecoder();
|
||||
const url = dec.decode(new Uint8Array(buf, urlPtr, urlLen));
|
||||
const method = dec.decode(new Uint8Array(buf, methodPtr, methodLen));
|
||||
const headers = dec.decode(new Uint8Array(buf, headersPtr, headersLen));
|
||||
const body = dec.decode(new Uint8Array(buf, bodyPtr, bodyLen));
|
||||
const url = dec.decode(new Uint8Array(snapBuf, urlPtr, urlLen));
|
||||
const method = dec.decode(new Uint8Array(snapBuf, methodPtr, methodLen));
|
||||
const headers = dec.decode(new Uint8Array(snapBuf, headersPtr, headersLen));
|
||||
const body = dec.decode(new Uint8Array(snapBuf, bodyPtr, bodyLen));
|
||||
try {
|
||||
const result = await hostFunctions!.http_request!(url, method, headers, body);
|
||||
return writeOut(buf, outPtr, outLenPtr, new TextEncoder().encode(result));
|
||||
// await 後重新拿 memory.buffer(grow 會產生新的 ArrayBuffer)
|
||||
return writeOut(memory.buffer, outPtr, outLenPtr, new TextEncoder().encode(result));
|
||||
} catch {
|
||||
return 1;
|
||||
}
|
||||
@@ -240,12 +242,11 @@ export function createWasiShim(stdinData: string, hostFunctions?: WasiHostFuncti
|
||||
kv_get: hostFunctions?.kv_get
|
||||
? async (keyPtr: number, keyLen: number, outPtr: number, outLenPtr: number): Promise<number> => {
|
||||
if (!memory) return 1;
|
||||
const buf = memory.buffer;
|
||||
const key = new TextDecoder().decode(new Uint8Array(buf, keyPtr, keyLen));
|
||||
const key = new TextDecoder().decode(new Uint8Array(memory.buffer, keyPtr, keyLen));
|
||||
try {
|
||||
const result = await hostFunctions!.kv_get!(key);
|
||||
if (result === null) return 2;
|
||||
return writeOut(buf, outPtr, outLenPtr, new TextEncoder().encode(result));
|
||||
return writeOut(memory.buffer, outPtr, outLenPtr, new TextEncoder().encode(result));
|
||||
} catch {
|
||||
return 1;
|
||||
}
|
||||
@@ -258,13 +259,12 @@ export function createWasiShim(stdinData: string, hostFunctions?: WasiHostFuncti
|
||||
? async (encPtr: number, encLen: number, ivPtr: number, ivLen: number,
|
||||
outPtr: number, outLenPtr: number): Promise<number> => {
|
||||
if (!memory) return 1;
|
||||
const buf = memory.buffer;
|
||||
const dec = new TextDecoder();
|
||||
const encB64 = dec.decode(new Uint8Array(buf, encPtr, encLen));
|
||||
const ivB64 = dec.decode(new Uint8Array(buf, ivPtr, ivLen));
|
||||
const encB64 = dec.decode(new Uint8Array(memory.buffer, encPtr, encLen));
|
||||
const ivB64 = dec.decode(new Uint8Array(memory.buffer, ivPtr, ivLen));
|
||||
try {
|
||||
const plaintext = await hostFunctions!.crypto_decrypt!(encB64, ivB64);
|
||||
return writeOut(buf, outPtr, outLenPtr, new TextEncoder().encode(plaintext));
|
||||
return writeOut(memory.buffer, outPtr, outLenPtr, new TextEncoder().encode(plaintext));
|
||||
} catch {
|
||||
return 1;
|
||||
}
|
||||
@@ -276,12 +276,12 @@ export function createWasiShim(stdinData: string, hostFunctions?: WasiHostFuncti
|
||||
? async (dataPtr: number, dataLen: number, pkcs8Ptr: number, pkcs8Len: number,
|
||||
outPtr: number, outLenPtr: number): Promise<number> => {
|
||||
if (!memory) return 1;
|
||||
const buf = memory.buffer;
|
||||
const data = new Uint8Array(new Uint8Array(buf, dataPtr, dataLen));
|
||||
const pkcs8 = new Uint8Array(new Uint8Array(buf, pkcs8Ptr, pkcs8Len));
|
||||
// await 前複製 typed array(避免 memory grow 後 buffer 失效)
|
||||
const data = new Uint8Array(new Uint8Array(memory.buffer, dataPtr, dataLen));
|
||||
const pkcs8 = new Uint8Array(new Uint8Array(memory.buffer, pkcs8Ptr, pkcs8Len));
|
||||
try {
|
||||
const sig = await hostFunctions!.crypto_sign_rs256!(data, pkcs8);
|
||||
return writeOut(buf, outPtr, outLenPtr, sig);
|
||||
return writeOut(memory.buffer, outPtr, outLenPtr, sig);
|
||||
} catch {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user