From 2dbeb889f6df24939643043b82ea2ed72050aff1 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 25 Oct 2022 17:39:41 -0400 Subject: [PATCH] node-api: handle no support for external buffers Refs: https://github.com/electron/electron/issues/35801 Refs: https://github.com/nodejs/abi-stable-node/issues/441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson PR-URL: https://github.com/nodejs/node/pull/45181 Backport-PR-URL: https://github.com/nodejs/node/pull/45616 Reviewed-By: Chengzhong Wu Reviewed-By: Minwoo Jung --- doc/api/n-api.md | 27 +++++++++++++++++++ src/js_native_api.h | 2 ++ src/js_native_api_types.h | 3 ++- src/js_native_api_v8.cc | 3 ++- src/node_api.cc | 4 +++ src/node_api.h | 2 ++ .../js-native-api/test_general/test_general.c | 5 ++++ test/node-api/test_general/test_general.c | 5 ++++ 8 files changed, 49 insertions(+), 2 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 2dc473dca16513..455ac3420f0b31 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -573,6 +573,7 @@ typedef enum { napi_arraybuffer_expected, napi_detachable_arraybuffer_expected, napi_would_deadlock, /* unused */ + napi_no_external_buffers_allowed } napi_status; ``` @@ -2252,6 +2253,19 @@ napi_create_external_arraybuffer(napi_env env, Returns `napi_ok` if the API succeeded. +**Some runtimes other than Node.js have dropped support for external buffers**. +On runtimes other than Node.js this method may return +`napi_no_external_buffers_allowed` to indicate that external +buffers are not supported. One such runtime is Electron as +described in this issue +[electron/issues/35801](https://github.com/electron/electron/issues/35801). + +In order to maintain broadest compatibility with all runtimes +you may define `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` in your addon before +includes for the node-api headers. Doing so will hide the 2 functions +that create external buffers. This will ensure a compilation error +occurs if you accidentally use one of these methods. + This API returns a Node-API value corresponding to a JavaScript `ArrayBuffer`. The underlying byte buffer of the `ArrayBuffer` is externally allocated and managed. The caller must ensure that the byte buffer remains valid until the @@ -2295,6 +2309,19 @@ napi_status napi_create_external_buffer(napi_env env, Returns `napi_ok` if the API succeeded. +**Some runtimes other than Node.js have dropped support for external buffers**. +On runtimes other than Node.js this method may return +`napi_no_external_buffers_allowed` to indicate that external +buffers are not supported. One such runtime is Electron as +described in this issue +[electron/issues/35801](https://github.com/electron/electron/issues/35801). + +In order to maintain broadest compatibility with all runtimes +you may define `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` in your addon before +includes for the node-api headers. Doing so will hide the 2 functions +that create external buffers. This will ensure a compilation error +occurs if you accidentally use one of these methods. + This API allocates a `node::Buffer` object and initializes it with data backed by the passed in buffer. While this is still a fully-supported data structure, in most cases using a `TypedArray` will suffice. diff --git a/src/js_native_api.h b/src/js_native_api.h index e804d1d45d2365..d8b1498d73fba2 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -387,6 +387,7 @@ NAPI_EXTERN napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, void** data, napi_value* result); +#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED NAPI_EXTERN napi_status napi_create_external_arraybuffer(napi_env env, void* external_data, @@ -394,6 +395,7 @@ napi_create_external_arraybuffer(napi_env env, napi_finalize finalize_cb, void* finalize_hint, napi_value* result); +#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED NAPI_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer, void** data, diff --git a/src/js_native_api_types.h b/src/js_native_api_types.h index 6aba06629b3154..7529b853655a25 100644 --- a/src/js_native_api_types.h +++ b/src/js_native_api_types.h @@ -92,7 +92,8 @@ typedef enum { napi_date_expected, napi_arraybuffer_expected, napi_detachable_arraybuffer_expected, - napi_would_deadlock // unused + napi_would_deadlock, // unused + napi_no_external_buffers_allowed } napi_status; // Note: when adding a new enum value to `napi_status`, please also update // * `const int last_status` in the definition of `napi_get_last_error_info()' diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 12528799efa43a..d5b3359774d5dc 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -744,6 +744,7 @@ const char* error_messages[] = {nullptr, "An arraybuffer was expected", "A detachable arraybuffer was expected", "Main thread would deadlock", + "External buffers are not allowed", }; napi_status napi_get_last_error_info(napi_env env, @@ -755,7 +756,7 @@ napi_status napi_get_last_error_info(napi_env env, // message in the `napi_status` enum each time a new error message is added. // We don't have a napi_status_last as this would result in an ABI // change each time a message was added. - const int last_status = napi_would_deadlock; + const int last_status = napi_no_external_buffers_allowed; static_assert( NAPI_ARRAYSIZE(error_messages) == last_status + 1, diff --git a/src/node_api.cc b/src/node_api.cc index b1b85073508672..c495c34abbdb7e 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -928,6 +928,10 @@ napi_status napi_create_external_buffer(napi_env env, NAPI_PREAMBLE(env); CHECK_ARG(env, result); +#if defined(V8_ENABLE_SANDBOX) + return napi_set_last_error(env, napi_no_external_buffers_allowed); +#endif + v8::Isolate* isolate = env->isolate; // The finalizer object will delete itself after invoking the callback. diff --git a/src/node_api.h b/src/node_api.h index 1772c67c15afb2..47703198fed09a 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -138,12 +138,14 @@ NAPI_EXTERN napi_status napi_create_buffer(napi_env env, size_t length, void** data, napi_value* result); +#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED NAPI_EXTERN napi_status napi_create_external_buffer(napi_env env, size_t length, void* data, napi_finalize finalize_cb, void* finalize_hint, napi_value* result); +#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED NAPI_EXTERN napi_status napi_create_buffer_copy(napi_env env, size_t length, const void* data, diff --git a/test/js-native-api/test_general/test_general.c b/test/js-native-api/test_general/test_general.c index f6e641167d5bcc..0e1daa17abcda7 100644 --- a/test/js-native-api/test_general/test_general.c +++ b/test/js-native-api/test_general/test_general.c @@ -1,3 +1,8 @@ +// we define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED here to +// validate that it can be used as a form of test itself. It is +// not related to any of the other tests +// defined in the file +#define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED #include #include #include diff --git a/test/node-api/test_general/test_general.c b/test/node-api/test_general/test_general.c index 5c2c2817312de1..03381e7b77c5dd 100644 --- a/test/node-api/test_general/test_general.c +++ b/test/node-api/test_general/test_general.c @@ -1,4 +1,9 @@ #define NAPI_EXPERIMENTAL +// we define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED here to validate that it can +// be used as a form of test itself. It is +// not related to any of the other tests +// defined in the file +#define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED #include #include #include "../../js-native-api/common.h"