Skip to content

Commit 6e1bed1

Browse files
bnoordhuisMylesBorins
authored andcommitted
src: prevent persistent handle resource leaks
Replace v8::Persistent with node::Persistent, a specialization that resets the persistent handle on destruction. Prevents accidental resource leaks when forgetting to call .Reset() manually. I'm fairly confident this commit fixes a number of resource leaks that have gone undiagnosed so far. Backport-PR-URL: #19185 PR-URL: #18656 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent 03784d7 commit 6e1bed1

25 files changed

+97
-82
lines changed

node.gyp

+2-1
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,10 @@
359359
'src/node_internals.h',
360360
'src/node_javascript.h',
361361
'src/node_mutex.h',
362-
'src/node_platform.h',
363362
'src/node_perf.h',
364363
'src/node_perf_common.h',
364+
'src/node_persistent.h',
365+
'src/node_platform.h',
365366
'src/node_root_certs.h',
366367
'src/node_version.h',
367368
'src/node_watchdog.h',

src/async_wrap.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,8 @@ static void DisablePromiseHook(const FunctionCallbackInfo<Value>& args) {
410410
class DestroyParam {
411411
public:
412412
double asyncId;
413-
v8::Persistent<Object> target;
414-
v8::Persistent<Object> propBag;
413+
Persistent<Object> target;
414+
Persistent<Object> propBag;
415415
};
416416

417417

src/base_object-inl.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ inline BaseObject::~BaseObject() {
4747
}
4848

4949

50-
inline v8::Persistent<v8::Object>& BaseObject::persistent() {
50+
inline Persistent<v8::Object>& BaseObject::persistent() {
5151
return persistent_handle_;
5252
}
5353

src/base_object.h

+3-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
2626

27+
#include "node_persistent.h"
2728
#include "v8.h"
2829

2930
namespace node {
@@ -39,12 +40,7 @@ class BaseObject {
3940
// persistent.IsEmpty() is true.
4041
inline v8::Local<v8::Object> object();
4142

42-
// The parent class is responsible for calling .Reset() on destruction
43-
// when the persistent handle is strong because there is no way for
44-
// BaseObject to know when the handle goes out of scope.
45-
// Weak handles have been reset by the time the destructor runs but
46-
// calling .Reset() again is harmless.
47-
inline v8::Persistent<v8::Object>& persistent();
43+
inline Persistent<v8::Object>& persistent();
4844

4945
inline Environment* env() const;
5046

@@ -71,7 +67,7 @@ class BaseObject {
7167
// position of members in memory are predictable. For more information please
7268
// refer to `doc/guides/node-postmortem-support.md`
7369
friend int GenDebugSymbols();
74-
v8::Persistent<v8::Object> persistent_handle_;
70+
Persistent<v8::Object> persistent_handle_;
7571
Environment* env_;
7672
};
7773

src/env-inl.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -541,8 +541,8 @@ void Environment::CreateImmediate(native_immediate_callback cb,
541541
native_immediate_callbacks_.push_back({
542542
cb,
543543
data,
544-
std::unique_ptr<v8::Persistent<v8::Object>>(obj.IsEmpty() ?
545-
nullptr : new v8::Persistent<v8::Object>(isolate_, obj)),
544+
std::unique_ptr<Persistent<v8::Object>>(obj.IsEmpty() ?
545+
nullptr : new Persistent<v8::Object>(isolate_, obj)),
546546
ref
547547
});
548548
immediate_info()->count_inc(1);

src/env.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,7 @@ class Environment {
800800
struct NativeImmediateCallback {
801801
native_immediate_callback cb_;
802802
void* data_;
803-
std::unique_ptr<v8::Persistent<v8::Object>> keep_alive_;
803+
std::unique_ptr<Persistent<v8::Object>> keep_alive_;
804804
bool refed_;
805805
};
806806
std::vector<NativeImmediateCallback> native_immediate_callbacks_;
@@ -811,8 +811,7 @@ class Environment {
811811
v8::Local<v8::Promise> promise,
812812
v8::Local<v8::Value> parent);
813813

814-
#define V(PropertyName, TypeName) \
815-
v8::Persistent<TypeName> PropertyName ## _;
814+
#define V(PropertyName, TypeName) Persistent<TypeName> PropertyName ## _;
816815
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
817816
#undef V
818817

src/inspector_agent.cc

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ using v8::HandleScope;
3030
using v8::Isolate;
3131
using v8::Local;
3232
using v8::Object;
33-
using v8::Persistent;
3433
using v8::String;
3534
using v8::Value;
3635

src/inspector_agent.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class Agent {
9797

9898
private:
9999
void ToggleAsyncHook(v8::Isolate* isolate,
100-
const v8::Persistent<v8::Function>& fn);
100+
const Persistent<v8::Function>& fn);
101101

102102
node::Environment* parent_env_;
103103
std::unique_ptr<NodeInspectorClient> client_;
@@ -109,8 +109,8 @@ class Agent {
109109

110110
bool pending_enable_async_hook_;
111111
bool pending_disable_async_hook_;
112-
v8::Persistent<v8::Function> enable_async_hook_function_;
113-
v8::Persistent<v8::Function> disable_async_hook_function_;
112+
Persistent<v8::Function> enable_async_hook_function_;
113+
Persistent<v8::Function> disable_async_hook_function_;
114114
};
115115

116116
} // namespace inspector

src/inspector_js_api.cc

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ using v8::Local;
1919
using v8::MaybeLocal;
2020
using v8::NewStringType;
2121
using v8::Object;
22-
using v8::Persistent;
2322
using v8::String;
2423
using v8::Value;
2524

src/module_wrap.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ class ModuleWrap : public BaseObject {
4949
v8::Local<v8::String> specifier,
5050
v8::Local<v8::Module> referrer);
5151

52-
v8::Persistent<v8::Module> module_;
53-
v8::Persistent<v8::String> url_;
52+
Persistent<v8::Module> module_;
53+
Persistent<v8::String> url_;
5454
bool linked_ = false;
55-
std::unordered_map<std::string, v8::Persistent<v8::Promise>> resolve_cache_;
56-
v8::Persistent<v8::Context> context_;
55+
std::unordered_map<std::string, Persistent<v8::Promise>> resolve_cache_;
56+
Persistent<v8::Context> context_;
5757
};
5858

5959
} // namespace loader

src/node_api.cc

+13-13
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ struct napi_env__ {
3838
accessor_data_template.Reset();
3939
}
4040
v8::Isolate* isolate;
41-
v8::Persistent<v8::Value> last_exception;
42-
v8::Persistent<v8::ObjectTemplate> wrap_template;
43-
v8::Persistent<v8::ObjectTemplate> function_data_template;
44-
v8::Persistent<v8::ObjectTemplate> accessor_data_template;
41+
node::Persistent<v8::Value> last_exception;
42+
node::Persistent<v8::ObjectTemplate> wrap_template;
43+
node::Persistent<v8::ObjectTemplate> function_data_template;
44+
node::Persistent<v8::ObjectTemplate> accessor_data_template;
4545
napi_extended_error_info last_error;
4646
int open_handle_scopes = 0;
4747
int open_callback_scopes = 0;
@@ -274,13 +274,13 @@ static_assert(sizeof(v8::Local<v8::Value>) == sizeof(napi_value),
274274
"Cannot convert between v8::Local<v8::Value> and napi_value");
275275

276276
static
277-
napi_deferred JsDeferredFromV8Persistent(v8::Persistent<v8::Value>* local) {
277+
napi_deferred JsDeferredFromNodePersistent(node::Persistent<v8::Value>* local) {
278278
return reinterpret_cast<napi_deferred>(local);
279279
}
280280

281281
static
282-
v8::Persistent<v8::Value>* V8PersistentFromJsDeferred(napi_deferred local) {
283-
return reinterpret_cast<v8::Persistent<v8::Value>*>(local);
282+
node::Persistent<v8::Value>* NodePersistentFromJsDeferred(napi_deferred local) {
283+
return reinterpret_cast<node::Persistent<v8::Value>*>(local);
284284
}
285285

286286
static
@@ -360,7 +360,7 @@ class Finalizer {
360360
void* _finalize_hint;
361361
};
362362

363-
// Wrapper around v8::Persistent that implements reference counting.
363+
// Wrapper around node::Persistent that implements reference counting.
364364
class Reference : private Finalizer {
365365
private:
366366
Reference(napi_env env,
@@ -470,7 +470,7 @@ class Reference : private Finalizer {
470470
}
471471
}
472472

473-
v8::Persistent<v8::Value> _persistent;
473+
node::Persistent<v8::Value> _persistent;
474474
uint32_t _refcount;
475475
bool _delete_self;
476476
};
@@ -846,8 +846,8 @@ napi_status ConcludeDeferred(napi_env env,
846846
CHECK_ARG(env, result);
847847

848848
v8::Local<v8::Context> context = env->isolate->GetCurrentContext();
849-
v8::Persistent<v8::Value>* deferred_ref =
850-
V8PersistentFromJsDeferred(deferred);
849+
node::Persistent<v8::Value>* deferred_ref =
850+
NodePersistentFromJsDeferred(deferred);
851851
v8::Local<v8::Value> v8_deferred =
852852
v8::Local<v8::Value>::New(env->isolate, *deferred_ref);
853853

@@ -3493,10 +3493,10 @@ napi_status napi_create_promise(napi_env env,
34933493
CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
34943494

34953495
auto v8_resolver = maybe.ToLocalChecked();
3496-
auto v8_deferred = new v8::Persistent<v8::Value>();
3496+
auto v8_deferred = new node::Persistent<v8::Value>();
34973497
v8_deferred->Reset(env->isolate, v8_resolver);
34983498

3499-
*deferred = v8impl::JsDeferredFromV8Persistent(v8_deferred);
3499+
*deferred = v8impl::JsDeferredFromNodePersistent(v8_deferred);
35003500
*promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise());
35013501
return GET_RETURN_STATUS(env);
35023502
}

src/node_buffer.cc

-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ using v8::Local;
7878
using v8::Maybe;
7979
using v8::MaybeLocal;
8080
using v8::Object;
81-
using v8::Persistent;
8281
using v8::String;
8382
using v8::Uint32Array;
8483
using v8::Uint8Array;

src/node_contextify.cc

-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ using v8::NamedPropertyHandlerConfiguration;
5050
using v8::Nothing;
5151
using v8::Object;
5252
using v8::ObjectTemplate;
53-
using v8::Persistent;
5453
using v8::PropertyAttribute;
5554
using v8::PropertyCallbackInfo;
5655
using v8::PropertyDescriptor;

src/node_contextify.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ContextifyContext {
1515
enum { kSandboxObjectIndex = 1 };
1616

1717
Environment* const env_;
18-
v8::Persistent<v8::Context> context_;
18+
Persistent<v8::Context> context_;
1919

2020
public:
2121
ContextifyContext(Environment* env,

src/node_crypto.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ using v8::MaybeLocal;
109109
using v8::Null;
110110
using v8::Object;
111111
using v8::ObjectTemplate;
112-
using v8::Persistent;
113112
using v8::PropertyAttribute;
114113
using v8::ReadOnly;
115114
using v8::Signature;
@@ -3619,7 +3618,8 @@ void Connection::GetServername(const FunctionCallbackInfo<Value>& args) {
36193618
ASSIGN_OR_RETURN_UNWRAP(&conn, args.Holder());
36203619

36213620
if (conn->is_server() && !conn->servername_.IsEmpty()) {
3622-
args.GetReturnValue().Set(conn->servername_);
3621+
args.GetReturnValue().Set(
3622+
PersistentToLocal(args.GetIsolate(), conn->servername_));
36233623
} else {
36243624
args.GetReturnValue().Set(false);
36253625
}

src/node_crypto.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,11 @@ class SSLWrap {
354354
ClientHelloParser hello_parser_;
355355

356356
#ifdef NODE__HAVE_TLSEXT_STATUS_CB
357-
v8::Persistent<v8::Object> ocsp_response_;
357+
Persistent<v8::Object> ocsp_response_;
358358
#endif // NODE__HAVE_TLSEXT_STATUS_CB
359359

360360
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
361-
v8::Persistent<v8::Value> sni_context_;
361+
Persistent<v8::Value> sni_context_;
362362
#endif
363363

364364
friend class SecureContext;
@@ -380,13 +380,13 @@ class Connection : public AsyncWrap, public SSLWrap<Connection> {
380380
void NewSessionDoneCb();
381381

382382
#ifndef OPENSSL_NO_NEXTPROTONEG
383-
v8::Persistent<v8::Object> npnProtos_;
384-
v8::Persistent<v8::Value> selectedNPNProto_;
383+
Persistent<v8::Object> npnProtos_;
384+
Persistent<v8::Value> selectedNPNProto_;
385385
#endif
386386

387387
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
388-
v8::Persistent<v8::Object> sniObject_;
389-
v8::Persistent<v8::String> servername_;
388+
Persistent<v8::Object> sniObject_;
389+
Persistent<v8::String> servername_;
390390
#endif
391391

392392
size_t self_size() const override { return sizeof(*this); }

src/node_file.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ class fs_req_wrap {
366366
After(uv_req); \
367367
req_wrap = nullptr; \
368368
} else { \
369-
args.GetReturnValue().Set(req_wrap->persistent()); \
369+
args.GetReturnValue().Set( \
370+
PersistentToLocal(env->isolate(), req_wrap->persistent())); \
370371
}
371372

372373
#define ASYNC_CALL(func, req, encoding, ...) \
@@ -1140,7 +1141,8 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
11401141
return;
11411142
}
11421143

1143-
return args.GetReturnValue().Set(req_wrap->persistent());
1144+
return args.GetReturnValue().Set(
1145+
PersistentToLocal(env->isolate(), req_wrap->persistent()));
11441146
}
11451147

11461148

src/node_internals.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
2626

2727
#include "node.h"
28+
#include "node_persistent.h"
2829
#include "util-inl.h"
2930
#include "env-inl.h"
3031
#include "uv.h"
@@ -214,7 +215,7 @@ class Environment;
214215
template <class TypeName>
215216
inline v8::Local<TypeName> PersistentToLocal(
216217
v8::Isolate* isolate,
217-
const v8::Persistent<TypeName>& persistent);
218+
const Persistent<TypeName>& persistent);
218219

219220
// Creates a new context with Node.js-specific tweaks. Currently, it removes
220221
// the `v8BreakIterator` property from the global `Intl` object if present.

src/node_persistent.h

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef SRC_NODE_PERSISTENT_H_
2+
#define SRC_NODE_PERSISTENT_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "v8.h"
7+
8+
namespace node {
9+
10+
template <typename T>
11+
struct ResetInDestructorPersistentTraits {
12+
static const bool kResetInDestructor = true;
13+
template <typename S, typename M>
14+
// Disallow copy semantics by leaving this unimplemented.
15+
inline static void Copy(
16+
const v8::Persistent<S, M>&,
17+
v8::Persistent<T, ResetInDestructorPersistentTraits<T>>*);
18+
};
19+
20+
// v8::Persistent does not reset the object slot in its destructor. That is
21+
// acknowledged as a flaw in the V8 API and expected to change in the future
22+
// but for now node::Persistent is the easier and safer alternative.
23+
template <typename T>
24+
using Persistent = v8::Persistent<T, ResetInDestructorPersistentTraits<T>>;
25+
26+
} // namespace node
27+
28+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
29+
30+
#endif // SRC_NODE_PERSISTENT_H_

src/node_zlib.cc

-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ using v8::HandleScope;
4646
using v8::Local;
4747
using v8::Number;
4848
using v8::Object;
49-
using v8::Persistent;
5049
using v8::String;
5150
using v8::Uint32Array;
5251
using v8::Value;

0 commit comments

Comments
 (0)