From 579dd765d3621ffc89e3158a1e2e9488e3b48811 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Mon, 26 Oct 2026 22:52:26 -0700 Subject: [PATCH] ffi: reject multi-argument buffer fast trampolines The arm64 and x86_64 SysV emitters convert each buffer argument with a helper call after loading earlier arguments into their target registers. The call clobbers those registers, so the target would receive wrong values for every argument before the buffer. This is not reachable today: buffer-typed trampolines are generated only for single-argument signatures. Reject other shapes in the emitters so a future caller falls back instead of miscompiling. Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Assisted-by: claude:opus-5.5 --- src/ffi/platforms/arm64.cc | 5 +++++ src/ffi/platforms/x64.cc | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/ffi/platforms/arm64.cc b/src/ffi/platforms/arm64.cc index df215ecafed3..25221958933c 100644 --- a/src/ffi/platforms/arm64.cc +++ b/src/ffi/platforms/arm64.cc @@ -257,6 +257,11 @@ extern "C" bool node_ffi_create_fast_trampoline( if (has_buffer_args && fp_count != 0) { return false; } + // The buffer helper call clobbers target registers already loaded for + // earlier arguments, so buffer trampolines support a single argument only. + if (has_buffer_args && argc > 1) { + return false; + } const size_t incoming_gp_count = gp_count + (has_buffer_args ? 1 : 0); // When V8 passes FastApiCallbackOptions, it consumes one additional GP diff --git a/src/ffi/platforms/x64.cc b/src/ffi/platforms/x64.cc index d5c15c381574..f9700ac0211b 100644 --- a/src/ffi/platforms/x64.cc +++ b/src/ffi/platforms/x64.cc @@ -378,6 +378,11 @@ extern "C" bool node_ffi_create_fast_trampoline( if (has_buffer_args && fp_count != 0) { return false; } + // The buffer helper call clobbers target registers already loaded for + // earlier arguments, so buffer trampolines support a single argument only. + if (has_buffer_args && argc > 1) { + return false; + } const size_t incoming_gp_count = gp_count + (has_buffer_args ? 1 : 0); // rdi is occupied by the V8 receiver, leaving rsi, rdx, rcx, r8, and r9 for