Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/api/ffi.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,8 @@ native memory directly. The caller must guarantee that:
* `length` stays within the allocated native region.
* no native code frees or repurposes that memory while JavaScript still uses
the `Buffer`.
* Memory protection is observed. For example, read-only memory pages must not
be written to.

If these guarantees are not met, reading or writing the `Buffer` can corrupt
memory or crash the process.
Expand Down
5 changes: 5 additions & 0 deletions test/ffi/ffi-test-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,13 @@ const fixtureSymbols = {
array_set_i32: { parameters: ['pointer', 'u64', 'i32'], result: 'void' },
array_get_f64: { parameters: ['pointer', 'u64'], result: 'f64' },
array_set_f64: { parameters: ['pointer', 'u64', 'f64'], result: 'void' },
readonly_memory: { parameters: [], result: 'pointer' },
};

if (!common.isWindows) {
fixtureSymbols.readonly_memory = { parameters: [], result: 'pointer' };
}

function cString(value) {
return Buffer.from(`${value}\0`);
}
Expand Down
11 changes: 10 additions & 1 deletion test/ffi/fixture_library/ffi_test_library.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#define FFI_EXPORT __declspec(dllexport)
#else
#include <sys/mman.h>
#define FFI_EXPORT
#endif

Expand Down Expand Up @@ -378,3 +378,12 @@ FFI_EXPORT void array_set_f64(double* arr, size_t index, double value) {

arr[index] = value;
}

#ifndef _WIN32
FFI_EXPORT void* readonly_memory() {
// TODO(bengl) Add a Windows version of this.

void* p = mmap(0, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return p;
}
#endif
31 changes: 31 additions & 0 deletions test/ffi/test-ffi-readonly-write.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Flags: --experimental-ffi
'use strict';
const { skipIfFFIMissing, isWindows, skip } = require('../common');
const assert = require('node:assert');
const { spawnSync } = require('node:child_process');
const { test } = require('node:test');
const { fixtureSymbols, libraryPath } = require('./ffi-test-common');

skipIfFFIMissing();
if (isWindows) {
skip('This test currently relies on POSIX APIs');
}

test('writing to readonly memory via buffer fails', () => {
const symbols = JSON.stringify(fixtureSymbols);
const libPath = JSON.stringify(libraryPath);
const { stdout, status } = spawnSync(process.execPath, [
'--experimental-ffi',
'-p',
`
const ffi = require('node:ffi');
const { functions } = ffi.dlopen(${libPath}, ${symbols})
const p = functions.readonly_memory();
const b = ffi.toBuffer(p, 4096, false);
b[0] = 42;
console.log('success');
`,
]);
assert.notStrictEqual(status, 0);
assert.strictEqual(stdout.length, 0);
});
Loading