Skip to content

Commit b448d7f

Browse files
committed
doc,test: mem protection must be observed in ffi
When using ffi.toBuffer, memory protection on any memory pages exposed must be observed by the caller, otherwise crashes will occur. Now documented, and tested.
1 parent 3f52482 commit b448d7f

File tree

4 files changed

+40
-0
lines changed

4 files changed

+40
-0
lines changed

doc/api/ffi.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ native memory directly. The caller must guarantee that:
533533
* `length` stays within the allocated native region.
534534
* no native code frees or repurposes that memory while JavaScript still uses
535535
the `Buffer`.
536+
* Memory protection is observed.
536537

537538
If these guarantees are not met, reading or writing the `Buffer` can corrupt
538539
memory or crash the process.

test/ffi/ffi-test-common.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const fixtureSymbols = {
7777
array_set_i32: { parameters: ['pointer', 'u64', 'i32'], result: 'void' },
7878
array_get_f64: { parameters: ['pointer', 'u64'], result: 'f64' },
7979
array_set_f64: { parameters: ['pointer', 'u64', 'f64'], result: 'void' },
80+
readonly_memory: { parameters: [], result: 'pointer' },
8081
};
8182

8283
function cString(value) {

test/ffi/fixture_library/ffi_test_library.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <stdint.h>
33
#include <stdlib.h>
44
#include <string.h>
5+
#include <sys/mman.h>
56

67
#ifdef _WIN32
78
#define FFI_EXPORT __declspec(dllexport)
@@ -378,3 +379,10 @@ FFI_EXPORT void array_set_f64(double* arr, size_t index, double value) {
378379

379380
arr[index] = value;
380381
}
382+
383+
FFI_EXPORT void * readonly_memory() {
384+
// TODO(bengl) Add a Windows version of this.
385+
386+
void * p = mmap(0, 4096, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
387+
return p;
388+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Flags: --experimental-ffi
2+
'use strict';
3+
const { skipIfFFIMissing, isWindows, skip } = require('../common');
4+
const assert = require('node:assert');
5+
const { spawnSync } = require('node:child_process');
6+
const { test } = require('node:test');
7+
const { fixtureSymbols, libraryPath } = require('./ffi-test-common');
8+
9+
skipIfFFIMissing();
10+
if (isWindows) {
11+
skip('This test currently relies on POSIX APIs');
12+
}
13+
14+
test('writing to readonly memory via buffer results in SIGBUS', () => {
15+
const symbols = JSON.stringify(fixtureSymbols);
16+
const { stdout, status, signal } = spawnSync(process.execPath, [
17+
'--experimental-ffi',
18+
'-p',
19+
`
20+
const ffi = require('node:ffi');
21+
const { functions } = ffi.dlopen('${libraryPath}', ${symbols})
22+
const p = functions.readonly_memory();
23+
const b = ffi.toBuffer(p, 4096, false);
24+
b[0] = 42;
25+
console.log('success');
26+
`,
27+
]);
28+
assert.notStrictEqual(status, 0);
29+
assert.strictEqual(stdout.length, 0);
30+
});

0 commit comments

Comments
 (0)