Skip to content

N-API: Type safety for napi_get_value_external/napi_unwrap #28164

Closed
@argv-minus-one

Description

@argv-minus-one

Is your feature request related to a problem? Please describe.
napi_get_value_external is currently type-unsafe. It yields 32 or 64 arbitrary bits, with no guarantee as to whether they mean what the caller thinks they mean, nor which native module created the external. Even assuming that it's a pointer is unsafe and may result in segfault.

Describe the solution you'd like
Please add a way to attach type information to values created by napi_create_external, and a way to check that type information when calling napi_get_value_external. The “type information” should be some sort of unique identifier generated by Node.js with an opaque C type, so that no two native modules can ever accidentally use the same type identifier for different types of external values.

Suggested API:

// An opaque identifier.
typedef struct napi_external_type__* napi_external_type;

// Creates a new napi_external_type.
// Each call to this function yields a different napi_external_type.
napi_status napi_create_external_type(
    napi_env env,
    napi_external_type* type
);

// Like napi_create_external, but takes a napi_external_type parameter.
// Attaches it to the created napi_value.
napi_status napi_create_typed_external(
    napi_env env,
    void* data,
    napi_finalize finalize_cb,
    void* finalize_hint,
    napi_external_type type,
    napi_value* result
);

// Like napi_get_value_external, but takes a napi_external_type parameter.
// Throws TypeError if the given napi_value has a different (or no) napi_external_type.
napi_status napi_get_value_typed_external(
    napi_env env,
    napi_value value,
    napi_external_type type,
    void** result
);

Describe alternatives you've considered
Currently, I'm just assuming that napi_get_value_external gives me a pointer to something that's at least sizeof(void *) bytes long, and put a magic number at the beginning of the external data structure to identify its type. To make the magic number distinctive, it is a pointer to some data inside my module:

static const void *MAGIC = &MAGIC;

typedef struct {
    void *magic; // check if magic == MAGIC before use
    …
} my_data;

…

my_data *d;
napi_get_value_external(…, …, &d);
if (d->magic != MAGIC) {
    // bail
}

As I've said above, this will result in undefined behavior (probably segfault) if some other native module calls napi_create_external with a value that isn't a valid pointer, and that external value somehow gets fed to my native module. Nor is it actually guaranteed that my magic number won't happen to be at the beginning of some other module's unrelated data structure, though it is highly unlikely.

Metadata

Metadata

Labels

feature requestIssues that request new features to be added to Node.js.node-apiIssues and PRs related to the Node-API.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions