From 1930ad12231bcc2c585e6ce4a2833bf0a40d7466 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sun, 23 Feb 2025 14:40:33 +0000 Subject: [PATCH] src: fix crash when lazy getter is invoked in a vm context V8 should invoke native functions in their creation context, preventing dynamic context by the caller. However, the lazy getter has no JavaScript function representation and has no creation context. It is not invoked in the original creation context. Fix the null realm by retrieving the creation context via `this` argument. PR-URL: https://github.com/nodejs/node/pull/57168 Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli Reviewed-By: Anna Henningsen Reviewed-By: Antoine du Hamel --- src/node_errors.h | 2 ++ src/node_util.cc | 23 +++++++++++++--- test/parallel/test-vm-util-lazy-properties.js | 26 +++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-vm-util-lazy-properties.js diff --git a/src/node_errors.h b/src/node_errors.h index c19a496398a12a..9127dd79f1faf4 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -84,6 +84,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); V(ERR_INVALID_ARG_TYPE, TypeError) \ V(ERR_INVALID_FILE_URL_HOST, TypeError) \ V(ERR_INVALID_FILE_URL_PATH, TypeError) \ + V(ERR_INVALID_INVOCATION, TypeError) \ V(ERR_INVALID_PACKAGE_CONFIG, Error) \ V(ERR_INVALID_OBJECT_DEFINE_PROPERTY, TypeError) \ V(ERR_INVALID_MODULE, Error) \ @@ -201,6 +202,7 @@ ERRORS_WITH_CODE(V) "Context not associated with Node.js environment") \ V(ERR_ILLEGAL_CONSTRUCTOR, "Illegal constructor") \ V(ERR_INVALID_ADDRESS, "Invalid socket address") \ + V(ERR_INVALID_INVOCATION, "Invalid invocation") \ V(ERR_INVALID_MODULE, "No such module") \ V(ERR_INVALID_STATE, "Invalid state") \ V(ERR_INVALID_THIS, "Value of \"this\" is the wrong type") \ diff --git a/src/node_util.cc b/src/node_util.cc index 844cdb0f440577..14650cdf1ab0b0 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -350,9 +350,25 @@ static void IsInsideNodeModules(const FunctionCallbackInfo& args) { static void DefineLazyPropertiesGetter( Local name, const v8::PropertyCallbackInfo& info) { - Realm* realm = Realm::GetCurrent(info); - Isolate* isolate = realm->isolate(); - auto context = isolate->GetCurrentContext(); + Isolate* isolate = info.GetIsolate(); + // This getter has no JavaScript function representation and is not + // invoked in the creation context. + // When this getter is invoked in a vm context, the `Realm::GetCurrent(info)` + // returns a nullptr and. Retrieve the creation context via `this` object and + // get the creation Realm. + Local receiver_val = info.This(); + if (!receiver_val->IsObject()) { + THROW_ERR_INVALID_INVOCATION(isolate); + return; + } + Local receiver = receiver_val.As(); + Local context; + if (!receiver->GetCreationContext().ToLocal(&context)) { + THROW_ERR_INVALID_INVOCATION(isolate); + return; + } + + Realm* realm = Realm::GetCurrent(context); Local arg = info.Data(); Local require_result; if (!realm->builtin_module_require() @@ -368,6 +384,7 @@ static void DefineLazyPropertiesGetter( } info.GetReturnValue().Set(ret); } + static void DefineLazyProperties(const FunctionCallbackInfo& args) { // target: object, id: string, keys: string[][, enumerable = true] CHECK_GE(args.Length(), 3); diff --git a/test/parallel/test-vm-util-lazy-properties.js b/test/parallel/test-vm-util-lazy-properties.js new file mode 100644 index 00000000000000..8b0ca1b177217e --- /dev/null +++ b/test/parallel/test-vm-util-lazy-properties.js @@ -0,0 +1,26 @@ +'use strict'; +require('../common'); + +const vm = require('node:vm'); +const util = require('node:util'); +const assert = require('node:assert'); + +// This verifies that invoking property getters defined with +// `require('internal/util').defineLazyProperties` does not crash +// the process. + +const ctx = vm.createContext(); +const getter = vm.runInContext(` + function getter(object, property) { + return object[property]; + } + getter; +`, ctx); + +// `util.parseArgs` is a lazy property. +const parseArgs = getter(util, 'parseArgs'); +assert.strictEqual(parseArgs, util.parseArgs); + +// `globalThis.TextEncoder` is a lazy property. +const TextEncoder = getter(globalThis, 'TextEncoder'); +assert.strictEqual(TextEncoder, globalThis.TextEncoder);