Skip to content

Commit a88fe4c

Browse files
committed
ffi: refresh cached string buffers on every call
Native code can mutate temporary string storage during an FFI call. Rewrite cached buffers on every conversion so a later call with the same JavaScript string receives a fresh copy of its UTF-8 bytes. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol
1 parent f7804f7 commit a88fe4c

3 files changed

Lines changed: 22 additions & 5 deletions

File tree

lib/internal/ffi/fast-api.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,6 @@ function getStringConversionPointer(state, value, index) {
134134
const size = value.length * 3 + 1;
135135
const buffers = state.buffers[state.depth - 1];
136136
let entry = buffers[index];
137-
if (entry !== undefined && entry.string === value) {
138-
return entry.pointer;
139-
}
140137
if (StringPrototypeIncludes(value, '\0')) {
141138
throwFFIArgError(`Argument ${index} must not contain null bytes`);
142139
}
@@ -146,15 +143,13 @@ function getStringConversionPointer(state, value, index) {
146143
__proto__: null,
147144
buffer,
148145
pointer: getRawPointer(buffer),
149-
string: undefined,
150146
};
151147
buffers[index] = entry;
152148
}
153149

154150
const buffer = entry.buffer;
155151
const written = buffer.write(value, 0, size - 1, 'utf8');
156152
buffer[written] = 0;
157-
entry.string = value;
158153
return entry.pointer;
159154
}
160155

test/ffi/fixture_library/ffi_test_library.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ FFI_EXPORT uint8_t string_equals_hello(const char* str) {
108108
return str && strcmp(str, "hello") == 0;
109109
}
110110

111+
FFI_EXPORT char* overwrite_string(char* str, int32_t value, uint64_t length) {
112+
return memset(str, value, (size_t)length);
113+
}
114+
111115
FFI_EXPORT char* string_concat(const char* a, const char* b) {
112116
if (!a || !b) {
113117
// NOLINTNEXTLINE (readability/null_usage)

test/ffi/test-ffi-fast-buffer.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,24 @@ test('fast FFI string buffers survive reentrant callbacks', {
9696
}
9797
});
9898

99+
test('fast FFI refreshes cached temporary string buffers', () => {
100+
const lib = new ffi.DynamicLibrary(libraryPath);
101+
const overwriteString = lib.getFunction('overwrite_string', {
102+
arguments: ['string', 'i32', 'u64'],
103+
return: 'pointer',
104+
});
105+
106+
try {
107+
const mutated = overwriteString('hello', 0x79, 1n);
108+
assert.strictEqual(ffi.toString(mutated), 'yello');
109+
110+
const refreshed = overwriteString('hello', 0x79, 0n);
111+
assert.strictEqual(ffi.toString(refreshed), 'hello');
112+
} finally {
113+
lib.close();
114+
}
115+
});
116+
99117
test('optimized buffer signatures preserve pointer-like conversions', () => {
100118
const lib = new ffi.DynamicLibrary(libraryPath);
101119
const asBuffer = lib.getFunction('pointer_to_usize', {

0 commit comments

Comments
 (0)