From 7335cb747be2b52be0ac170bbe2b2c87330c4e6a Mon Sep 17 00:00:00 2001 From: "taylor.fish" Date: Mon, 24 Nov 2025 17:34:39 -0800 Subject: [PATCH] Fix conversions from `JSValue` to FFI pointer (#25045) Fixes this issue, where two identical JS numbers could become two different FFI pointers: ```c // gcc -fpic -shared -o main.so #include void* getPtr(void) { return (void*)123; } void printPtr(void* ptr) { printf("%zu\n", (size_t)ptr); } ``` ```js import { dlopen, FFIType } from "bun:ffi"; const lib = dlopen("./main.so", { getPtr: { args: [], returns: FFIType.ptr }, printPtr: { args: [FFIType.ptr], returns: FFIType.void }, }); const ptr = lib.symbols.getPtr(); console.log(`${typeof ptr} ${ptr}`); const ptr2 = Number(String(ptr)); console.log(`${typeof ptr2} ${ptr2}`); console.log(`pointers equal? ${ptr === ptr2}`); lib.symbols.printPtr(ptr); lib.symbols.printPtr(ptr2); ``` ```console $ bun main.js number 123 number 123 pointers equal? true 123 18446744073709551615 ``` Fixes #20072 (For internal tracking: fixes ENG-22327) --- src/bun.js/api/FFI.h | 19 +++++++++++-------- src/bun.js/bindings/FFI.zig | 10 ---------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/bun.js/api/FFI.h b/src/bun.js/api/FFI.h index c3796712aa..bdd032dc0a 100644 --- a/src/bun.js/api/FFI.h +++ b/src/bun.js/api/FFI.h @@ -225,23 +225,26 @@ static void* JSVALUE_TO_PTR(EncodedJSValue val) { return 0; if (JSCELL_IS_TYPED_ARRAY(val)) { - return JSVALUE_TO_TYPED_ARRAY_VECTOR(val); + return JSVALUE_TO_TYPED_ARRAY_VECTOR(val); } + if (JSVALUE_IS_INT32(val)) { + return (void*)(uintptr_t)JSVALUE_TO_INT32(val); + } + + // Assume the JSValue is a double val.asInt64 -= DoubleEncodeOffset; - size_t ptr = (size_t)val.asDouble; - return (void*)ptr; + return (void*)(uintptr_t)val.asDouble; } static EncodedJSValue PTR_TO_JSVALUE(void* ptr) { EncodedJSValue val; - if (ptr == 0) - { - val.asInt64 = TagValueNull; - return val; + if (ptr == 0) { + val.asInt64 = TagValueNull; + return val; } - val.asDouble = (double)(size_t)ptr; + val.asDouble = (double)(uintptr_t)ptr; val.asInt64 += DoubleEncodeOffset; return val; } diff --git a/src/bun.js/bindings/FFI.zig b/src/bun.js/bindings/FFI.zig index adbb50862e..35c0425916 100644 --- a/src/bun.js/bindings/FFI.zig +++ b/src/bun.js/bindings/FFI.zig @@ -100,16 +100,6 @@ pub inline fn BOOLEAN_TO_JSVALUE(arg_val: @"bool") EncodedJSValue { res.asInt64 = @as(i64, @bitCast(@as(c_longlong, if (@as(c_int, @intFromBool(val)) != 0) (@as(c_int, 2) | @as(c_int, 4)) | @as(c_int, 1) else (@as(c_int, 2) | @as(c_int, 4)) | @as(c_int, 0)))); return res; } -pub inline fn PTR_TO_JSVALUE(arg_ptr: ?*anyopaque) EncodedJSValue { - const ptr = arg_ptr; - var val: EncodedJSValue = undefined; - val.asInt64 = @as(i64, @intCast(@intFromPtr(ptr))) + (@as(c_longlong, 1) << @as(@import("std").math.Log2Int(c_longlong), @intCast(49))); - return val; -} -pub inline fn JSVALUE_TO_PTR(arg_val: EncodedJSValue) ?*anyopaque { - const val = arg_val; - return @as(?*anyopaque, @ptrFromInt(val.asInt64 - (@as(c_longlong, 1) << @as(@import("std").math.Log2Int(c_longlong), @intCast(49))))); -} pub inline fn JSVALUE_TO_INT32(arg_val: EncodedJSValue) i32 { const val = arg_val; return @as(i32, @bitCast(@as(c_int, @truncate(val.asInt64))));