Skip to content
forked from nodejs/node

Commit 2ce5dfb

Browse files
bnoordhuisMayaLekova
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. PR-URL: nodejs#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 ea2963b commit 2ce5dfb

25 files changed

+87
-76
lines changed

node.gyp

+2-1
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,10 @@
371371
'src/node_internals.h',
372372
'src/node_javascript.h',
373373
'src/node_mutex.h',
374-
'src/node_platform.h',
375374
'src/node_perf.h',
376375
'src/node_perf_common.h',
376+
'src/node_persistent.h',
377+
'src/node_platform.h',
377378
'src/node_root_certs.h',
378379
'src/node_version.h',
379380
'src/node_watchdog.h',

src/async_wrap.cc

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

416416

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
@@ -550,8 +550,8 @@ void Environment::CreateImmediate(native_immediate_callback cb,
550550
native_immediate_callbacks_.push_back({
551551
cb,
552552
data,
553-
std::unique_ptr<v8::Persistent<v8::Object>>(obj.IsEmpty() ?
554-
nullptr : new v8::Persistent<v8::Object>(isolate_, obj)),
553+
std::unique_ptr<Persistent<v8::Object>>(obj.IsEmpty() ?
554+
nullptr : new Persistent<v8::Object>(isolate_, obj)),
555555
ref
556556
});
557557
immediate_info()->count_inc(1);

src/env.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ class Environment {
847847
struct NativeImmediateCallback {
848848
native_immediate_callback cb_;
849849
void* data_;
850-
std::unique_ptr<v8::Persistent<v8::Object>> keep_alive_;
850+
std::unique_ptr<Persistent<v8::Object>> keep_alive_;
851851
bool refed_;
852852
};
853853
std::vector<NativeImmediateCallback> native_immediate_callbacks_;
@@ -858,8 +858,7 @@ class Environment {
858858
v8::Local<v8::Promise> promise,
859859
v8::Local<v8::Value> parent);
860860

861-
#define V(PropertyName, TypeName) \
862-
v8::Persistent<TypeName> PropertyName ## _;
861+
#define V(PropertyName, TypeName) Persistent<TypeName> PropertyName ## _;
863862
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
864863
#undef V
865864

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
@@ -62,11 +62,11 @@ class ModuleWrap : public BaseObject {
6262
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);
6363

6464

65-
v8::Persistent<v8::Module> module_;
66-
v8::Persistent<v8::String> url_;
65+
Persistent<v8::Module> module_;
66+
Persistent<v8::String> url_;
6767
bool linked_ = false;
68-
std::unordered_map<std::string, v8::Persistent<v8::Promise>> resolve_cache_;
69-
v8::Persistent<v8::Context> context_;
68+
std::unordered_map<std::string, Persistent<v8::Promise>> resolve_cache_;
69+
Persistent<v8::Context> context_;
7070
};
7171

7272
} // 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
@@ -48,7 +48,6 @@ using v8::NamedPropertyHandlerConfiguration;
4848
using v8::Nothing;
4949
using v8::Object;
5050
using v8::ObjectTemplate;
51-
using v8::Persistent;
5251
using v8::PropertyAttribute;
5352
using v8::PropertyCallbackInfo;
5453
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

-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ using v8::MaybeLocal;
100100
using v8::Null;
101101
using v8::Object;
102102
using v8::ObjectTemplate;
103-
using v8::Persistent;
104103
using v8::PropertyAttribute;
105104
using v8::ReadOnly;
106105
using v8::Signature;

src/node_crypto.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -353,11 +353,11 @@ class SSLWrap {
353353
ClientHelloParser hello_parser_;
354354

355355
#ifdef NODE__HAVE_TLSEXT_STATUS_CB
356-
v8::Persistent<v8::Object> ocsp_response_;
356+
Persistent<v8::Object> ocsp_response_;
357357
#endif // NODE__HAVE_TLSEXT_STATUS_CB
358358

359359
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
360-
v8::Persistent<v8::Value> sni_context_;
360+
Persistent<v8::Value> sni_context_;
361361
#endif
362362

363363
friend class SecureContext;

src/node_file.h

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ using v8::HandleScope;
1414
using v8::Local;
1515
using v8::MaybeLocal;
1616
using v8::Object;
17-
using v8::Persistent;
1817
using v8::Promise;
1918
using v8::Undefined;
2019
using v8::Value;

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"
@@ -215,7 +216,7 @@ class Environment;
215216
template <class TypeName>
216217
inline v8::Local<TypeName> PersistentToLocal(
217218
v8::Isolate* isolate,
218-
const v8::Persistent<TypeName>& persistent);
219+
const Persistent<TypeName>& persistent);
219220

220221
// Creates a new context with Node.js-specific tweaks. Currently, it removes
221222
// 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;

src/stream_base-inl.h

+8-18
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ inline StreamWriteResult StreamBase::Write(
225225
return StreamWriteResult { async, err, req_wrap };
226226
}
227227

228-
template<typename OtherBase, bool kResetPersistent>
229-
SimpleShutdownWrap<OtherBase, kResetPersistent>::SimpleShutdownWrap(
228+
template <typename OtherBase>
229+
SimpleShutdownWrap<OtherBase>::SimpleShutdownWrap(
230230
StreamBase* stream,
231231
v8::Local<v8::Object> req_wrap_obj)
232232
: ShutdownWrap(stream, req_wrap_obj),
@@ -236,23 +236,18 @@ SimpleShutdownWrap<OtherBase, kResetPersistent>::SimpleShutdownWrap(
236236
Wrap(req_wrap_obj, static_cast<AsyncWrap*>(this));
237237
}
238238

239-
template<typename OtherBase, bool kResetPersistent>
240-
SimpleShutdownWrap<OtherBase, kResetPersistent>::~SimpleShutdownWrap() {
239+
template <typename OtherBase>
240+
SimpleShutdownWrap<OtherBase>::~SimpleShutdownWrap() {
241241
ClearWrap(static_cast<AsyncWrap*>(this)->object());
242-
if (kResetPersistent) {
243-
auto& persistent = static_cast<AsyncWrap*>(this)->persistent();
244-
CHECK_EQ(persistent.IsEmpty(), false);
245-
persistent.Reset();
246-
}
247242
}
248243

249244
inline ShutdownWrap* StreamBase::CreateShutdownWrap(
250245
v8::Local<v8::Object> object) {
251246
return new SimpleShutdownWrap<AsyncWrap>(this, object);
252247
}
253248

254-
template<typename OtherBase, bool kResetPersistent>
255-
SimpleWriteWrap<OtherBase, kResetPersistent>::SimpleWriteWrap(
249+
template <typename OtherBase>
250+
SimpleWriteWrap<OtherBase>::SimpleWriteWrap(
256251
StreamBase* stream,
257252
v8::Local<v8::Object> req_wrap_obj)
258253
: WriteWrap(stream, req_wrap_obj),
@@ -262,14 +257,9 @@ SimpleWriteWrap<OtherBase, kResetPersistent>::SimpleWriteWrap(
262257
Wrap(req_wrap_obj, static_cast<AsyncWrap*>(this));
263258
}
264259

265-
template<typename OtherBase, bool kResetPersistent>
266-
SimpleWriteWrap<OtherBase, kResetPersistent>::~SimpleWriteWrap() {
260+
template <typename OtherBase>
261+
SimpleWriteWrap<OtherBase>::~SimpleWriteWrap() {
267262
ClearWrap(static_cast<AsyncWrap*>(this)->object());
268-
if (kResetPersistent) {
269-
auto& persistent = static_cast<AsyncWrap*>(this)->persistent();
270-
CHECK_EQ(persistent.IsEmpty(), false);
271-
persistent.Reset();
272-
}
273263
}
274264

275265
inline WriteWrap* StreamBase::CreateWriteWrap(

0 commit comments

Comments
 (0)