收藏
回答

小程序 WXWebAssembly 出现 iOS 与 Android 不一致的行为

框架类型 问题类型 操作系统 操作系统版本 手机型号 微信版本
小程序 Bug iOS iOS 15.6 iPhone 13 Pro Max 8.0.24

使用 rustwasm 编写简单的 wasm 代码:入参为一个 Uint8Array ,出参为一个字符串。

#[wasm_bindgen]
pub fn echo(input: Vec<u8>) -> String {
    String::from("hello world")
}


引入小程序中, Android 和 macOS 微信客户端可以正常返回 hello world 字符串,iOS 微信客户端返回为空。

跟踪调试,发现问题在于

/**
* @param {Uint8Array} input
* @returns {string}
*/
export function echo(input) {
    try {
        const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
        const ptr0 = passArray8ToWasm0(input, wasm.__wbindgen_malloc);
        const len0 = WASM_VECTOR_LEN;
// 执行到这里时,cachedUint8Memory0 的大小为 wasm.memory.buffer 的大小
        wasm.echo(retptr, ptr0, len0);
// 执行到这里时,cachedUint8Memory0 空间被 wasm 回收,大小应为 0
// Android 和 macOS 微信客户端 cachedUint8Memory0 大小为 0 ,符合预期
// iOS 客户端似乎没能回收 cachedUint8Memory0 的空间,大小仍为执行 wasm.echo 前的值
        var r0 = getInt32Memory0()[retptr / 4 + 0];
        var r1 = getInt32Memory0()[retptr / 4 + 1];
        return getStringFromWasm0(r0, r1);
    } finally {
        wasm.__wbindgen_add_to_stack_pointer(16);
        wasm.__wbindgen_free(r0, r1);
    }
}


排查要点如上,可以看到 iOS 微信客户端的 WXWebAssembly 表现与主流浏览器不同,此问题可能导致内存访问越界进而造成微信客户端 crash (可稳定复现)。希望客户端这边能够排查,感谢~

回答关注问题邀请回答
收藏

3 个回答

  • 举高高
    举高高
    2022-07-20

    同样使用rust wasm存在wasm与js之间字符串传输出现空内容的情况,在rust wasm的胶水js中,有下面这段代码,js和wasm之间的字符串传输应该是使用cachedUint8Memory0(Uint8Array)作为TypedArray进行内存访问,经过调试,在开发者工具中cachedUint8Memory0和wasm.memory.buffer始终保持同步,而在iOS中,通过打印cachedUint8Memory0和wasm.memory.buffer的字节长度发现,后者比前者大,同时打印字符串在Uint8Array中的偏移量,该偏移量在cachedUint8Memory0范围之外,因此读取该字符串时发生了内存越界访问,得到的字符串也就为空了。

    let cachedUint8Memory0;
    function getUint8Memory0() {
        if (cachedUint8Memory0.byteLength === 0) {
            cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
        }
        return cachedUint8Memory0;
    }
    

    改变上述代码块的判断条件,测试在iOS端可以正常在js和wasm间传输字符串了,可以参考一下。

    // cachedUint8Memory0.byteLength === 0 改为下面的判断条件
    cachedUint8Memory0.byteLength !== wasm.memory.buffer.byteLength
    
    // 这个修改并不算正确的做法,直接去掉判断条件,让cachedUint8Memory0始终重新实例化更为有效,这样cachedUint8Memory0肯定就保持了对wasm.memory.buffer的同步
    let cachedUint8Memory0;
    function getUint8Memory0() {
      return (cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer));
    }
    


    2022-07-20
    有用 1
    回复 2
    • momo
      momo
      2022-07-20
      哈哈,我这边现在是直接把 if 去掉了😂
      2022-07-20
      回复
    • 举高高
      举高高
      2022-07-20回复momo
      简单粗暴,确实直接去掉比较管用,改判断条件的话,可能有时还是没法避免特殊情况下造成的TypedArray和ArrayBuffer不对应的情况
      2022-07-20
      回复
  • Taylor
    Taylor
    2023-07-11

    iPhone XR iOS 8.0.39 调试基础库 2.32.3 依然存在view在malloc之后byteLength依然为旧值的情况

    2023-07-11
    有用
    回复 1
    • Taylor
      Taylor
      2023-07-11
      在android和能正常运行的wasm程序在ios上会出现奇怪的内存问题,比如应该返回Vec<f32>的函数出现空值,这在其他任何支持wasm的平台都不会出现
      2023-07-11
      回复
  • Riven.
    Riven.
    2022-06-25

    你好,反馈已收到,我们核实下,有进展会同步。

    2022-06-25
    有用
    回复 1
    • 举高高
      举高高
      2022-07-20
      字符串作为入参,wasm内得到的字符串同样为空,同使用rust wasm
      2022-07-20
      回复
登录 后发表内容