Commit 1d5f8e6cba0 for nodejs
commit 1d5f8e6cba0cd5a7f939165a0bd0ca6c09e13ad1
Author: Guy Bedford <guybedford@gmail.com>
Date: Mon Oct 19 09:56:07 2026 -0700
wasi: accept wasm i32 pointers with high bit set
Wasm i32 arguments with the high bit set are passed to JS as negative
Int32 numbers, so WASI functions taking uint32_t pointers rejected
them with EINVAL. Accept Int32 in CheckType<uint32_t> and reinterpret
as uint32_t on conversion, fixing all WASI functions at once.
Fixes: https://github.com/nodejs/node/issues/62671
Refs: https://github.com/nodejs/node/pull/62822
Assisted-by: OpenCode
Signed-off-by: Guy Bedford <guybedford@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66636
Reviewed-By: James M Snell <jasnell@gmail.com>
diff --git a/src/node_wasi.cc b/src/node_wasi.cc
index 6c2adc82657..844221b8898 100644
--- a/src/node_wasi.cc
+++ b/src/node_wasi.cc
@@ -36,6 +36,7 @@ using v8::FastApiCallbackOptions;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
+using v8::Int32;
using v8::Integer;
using v8::Isolate;
using v8::Local;
@@ -296,13 +297,17 @@ static bool CheckType(Local<Value> v);
template <typename VT>
static VT ConvertType(Local<Value> V);
+// Wasm i32 values with the high bit set arrive as negative Int32 numbers.
template <>
bool CheckType<uint32_t>(Local<Value> value) {
- return value->IsUint32();
+ return value->IsUint32() || value->IsInt32();
}
template <>
uint32_t ConvertType(Local<Value> value) {
+ if (value->IsInt32()) {
+ return static_cast<uint32_t>(value.As<Int32>()->Value());
+ }
return value.As<Uint32>()->Value();
}
diff --git a/test/fixtures/wasi-high-bit-ptr.wasm b/test/fixtures/wasi-high-bit-ptr.wasm
new file mode 100644
index 00000000000..78f123ffbde
Binary files /dev/null and b/test/fixtures/wasi-high-bit-ptr.wasm differ
diff --git a/test/fixtures/wasi-high-bit-ptr.wat b/test/fixtures/wasi-high-bit-ptr.wat
new file mode 100644
index 00000000000..5336be28936
--- /dev/null
+++ b/test/fixtures/wasi-high-bit-ptr.wat
@@ -0,0 +1,22 @@
+;; Build: wasm-tools parse wasi-high-bit-ptr.wat -o wasi-high-bit-ptr.wasm
+(module
+ (import "wasi_snapshot_preview1" "clock_time_get"
+ (func $clock_time_get (param i32 i64 i32) (result i32)))
+ (import "wasi_snapshot_preview1" "args_sizes_get"
+ (func $args_sizes_get (param i32 i32) (result i32)))
+ (memory (export "memory") 1)
+ (func (export "_start"))
+ (func (export "clock_time_get_high_ptr") (result i32)
+ i32.const 0
+ i64.const 1
+ i32.const 0x80000000
+ call $clock_time_get)
+ (func (export "clock_time_get_low_ptr") (result i32)
+ i32.const 0
+ i64.const 1
+ i32.const 8
+ call $clock_time_get)
+ (func (export "args_sizes_get_high_ptr") (result i32)
+ i32.const 0x80000000
+ i32.const 0xfffffff0
+ call $args_sizes_get))
diff --git a/test/wasi/test-wasi-high-bit-ptr.js b/test/wasi/test-wasi-high-bit-ptr.js
new file mode 100644
index 00000000000..e2ea0bdbc6c
--- /dev/null
+++ b/test/wasi/test-wasi-high-bit-ptr.js
@@ -0,0 +1,24 @@
+'use strict';
+// Wasm i32 arguments with the high bit set arrive in JS as negative Int32
+// numbers. WASI functions must reinterpret them as uint32 pointers rather
+// than rejecting them with EINVAL.
+const common = require('../common');
+const assert = require('assert');
+const fixtures = require('../common/fixtures');
+const { WASI } = require('wasi');
+
+const UVWASI_EOVERFLOW = 61;
+
+(async () => {
+ const wasi = new WASI({ version: 'preview1', returnOnExit: true });
+ const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
+ const { instance } = await WebAssembly.instantiate(
+ fixtures.readSync('wasi-high-bit-ptr.wasm'), importObject);
+ wasi.start(instance);
+
+ assert.strictEqual(instance.exports.clock_time_get_low_ptr(), 0);
+ assert.strictEqual(instance.exports.clock_time_get_high_ptr(),
+ UVWASI_EOVERFLOW);
+ assert.strictEqual(instance.exports.args_sizes_get_high_ptr(),
+ UVWASI_EOVERFLOW);
+})().then(common.mustCall());