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,
|
headersPtr: number, headersLen: number, bodyPtr: number, bodyLen: number,
|
||||||
outPtr: number, outLenPtr: number): Promise<number> => {
|
outPtr: number, outLenPtr: number): Promise<number> => {
|
||||||
if (!memory) return 1;
|
if (!memory) return 1;
|
||||||
const buf = memory.buffer;
|
// 在 await 前讀完所有輸入(memory.buffer 在 await 後可能因 grow 而失效)
|
||||||
|
const snapBuf = memory.buffer;
|
||||||
const dec = new TextDecoder();
|
const dec = new TextDecoder();
|
||||||
const url = dec.decode(new Uint8Array(buf, urlPtr, urlLen));
|
const url = dec.decode(new Uint8Array(snapBuf, urlPtr, urlLen));
|
||||||
const method = dec.decode(new Uint8Array(buf, methodPtr, methodLen));
|
const method = dec.decode(new Uint8Array(snapBuf, methodPtr, methodLen));
|
||||||
const headers = dec.decode(new Uint8Array(buf, headersPtr, headersLen));
|
const headers = dec.decode(new Uint8Array(snapBuf, headersPtr, headersLen));
|
||||||
const body = dec.decode(new Uint8Array(buf, bodyPtr, bodyLen));
|
const body = dec.decode(new Uint8Array(snapBuf, bodyPtr, bodyLen));
|
||||||
try {
|
try {
|
||||||
const result = await hostFunctions!.http_request!(url, method, headers, body);
|
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 {
|
} catch {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -240,12 +242,11 @@ export function createWasiShim(stdinData: string, hostFunctions?: WasiHostFuncti
|
|||||||
kv_get: hostFunctions?.kv_get
|
kv_get: hostFunctions?.kv_get
|
||||||
? async (keyPtr: number, keyLen: number, outPtr: number, outLenPtr: number): Promise<number> => {
|
? async (keyPtr: number, keyLen: number, outPtr: number, outLenPtr: number): Promise<number> => {
|
||||||
if (!memory) return 1;
|
if (!memory) return 1;
|
||||||
const buf = memory.buffer;
|
const key = new TextDecoder().decode(new Uint8Array(memory.buffer, keyPtr, keyLen));
|
||||||
const key = new TextDecoder().decode(new Uint8Array(buf, keyPtr, keyLen));
|
|
||||||
try {
|
try {
|
||||||
const result = await hostFunctions!.kv_get!(key);
|
const result = await hostFunctions!.kv_get!(key);
|
||||||
if (result === null) return 2;
|
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 {
|
} catch {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -258,13 +259,12 @@ export function createWasiShim(stdinData: string, hostFunctions?: WasiHostFuncti
|
|||||||
? async (encPtr: number, encLen: number, ivPtr: number, ivLen: number,
|
? async (encPtr: number, encLen: number, ivPtr: number, ivLen: number,
|
||||||
outPtr: number, outLenPtr: number): Promise<number> => {
|
outPtr: number, outLenPtr: number): Promise<number> => {
|
||||||
if (!memory) return 1;
|
if (!memory) return 1;
|
||||||
const buf = memory.buffer;
|
|
||||||
const dec = new TextDecoder();
|
const dec = new TextDecoder();
|
||||||
const encB64 = dec.decode(new Uint8Array(buf, encPtr, encLen));
|
const encB64 = dec.decode(new Uint8Array(memory.buffer, encPtr, encLen));
|
||||||
const ivB64 = dec.decode(new Uint8Array(buf, ivPtr, ivLen));
|
const ivB64 = dec.decode(new Uint8Array(memory.buffer, ivPtr, ivLen));
|
||||||
try {
|
try {
|
||||||
const plaintext = await hostFunctions!.crypto_decrypt!(encB64, ivB64);
|
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 {
|
} catch {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -276,12 +276,12 @@ export function createWasiShim(stdinData: string, hostFunctions?: WasiHostFuncti
|
|||||||
? async (dataPtr: number, dataLen: number, pkcs8Ptr: number, pkcs8Len: number,
|
? async (dataPtr: number, dataLen: number, pkcs8Ptr: number, pkcs8Len: number,
|
||||||
outPtr: number, outLenPtr: number): Promise<number> => {
|
outPtr: number, outLenPtr: number): Promise<number> => {
|
||||||
if (!memory) return 1;
|
if (!memory) return 1;
|
||||||
const buf = memory.buffer;
|
// await 前複製 typed array(避免 memory grow 後 buffer 失效)
|
||||||
const data = new Uint8Array(new Uint8Array(buf, dataPtr, dataLen));
|
const data = new Uint8Array(new Uint8Array(memory.buffer, dataPtr, dataLen));
|
||||||
const pkcs8 = new Uint8Array(new Uint8Array(buf, pkcs8Ptr, pkcs8Len));
|
const pkcs8 = new Uint8Array(new Uint8Array(memory.buffer, pkcs8Ptr, pkcs8Len));
|
||||||
try {
|
try {
|
||||||
const sig = await hostFunctions!.crypto_sign_rs256!(data, pkcs8);
|
const sig = await hostFunctions!.crypto_sign_rs256!(data, pkcs8);
|
||||||
return writeOut(buf, outPtr, outLenPtr, sig);
|
return writeOut(memory.buffer, outPtr, outLenPtr, sig);
|
||||||
} catch {
|
} catch {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user