From f61bae3440e1bfcc83bba6ff0785adfb89b4045e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 14 May 2017 19:24:12 +0900 Subject: [PATCH] Allocate memory of Buffer with V8's allocator (cherry picked from commit 813a45f791668a10f947671eb1e59c2f01003884) (cherry picked from commit cbbe8e8722c72355d2ac25e918e57d413e846c1a) --- src/node_buffer.cc | 25 +++++++++------- src/node_crypto.cc | 73 +++++++++++++++++++++++++++++----------------- src/node_crypto.h | 3 +- src/stream_base.cc | 11 +++++-- src/udp_wrap.cc | 22 +++++++++----- src/util.h | 19 +++++++++--- 6 files changed, 101 insertions(+), 52 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 38b473b8ced..f8126eeddf4 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -61,9 +61,10 @@ bool zero_fill_all_buffers = false; namespace { -inline void* BufferMalloc(size_t length) { - return zero_fill_all_buffers ? node::UncheckedCalloc(length) : - node::UncheckedMalloc(length); +inline void* BufferMalloc(v8::Isolate* isolate, size_t length) { + auto* allocator = isolate->GetArrayBufferAllocator(); + return zero_fill_all_buffers ? allocator->Allocate(length) : + allocator->AllocateUninitialized(length); } } // namespace @@ -241,7 +242,7 @@ MaybeLocal New(Isolate* isolate, char* data = nullptr; if (length > 0) { - data = static_cast(BufferMalloc(length)); + data = static_cast(BufferMalloc(isolate, length)); if (data == nullptr) return Local(); @@ -250,10 +251,11 @@ MaybeLocal New(Isolate* isolate, CHECK(actual <= length); if (actual == 0) { - free(data); + isolate->GetArrayBufferAllocator()->Free(data, length); data = nullptr; } else if (actual < length) { - data = node::Realloc(data, actual); + // We should call realloc here, but v8::ArrayBufferAllocator does not + // provide such ability. } } @@ -262,7 +264,7 @@ MaybeLocal New(Isolate* isolate, return scope.Escape(buf); // Object failed to be created. Clean up resources. - free(data); + isolate->GetArrayBufferAllocator()->Free(data, length); return Local(); } @@ -286,7 +288,7 @@ MaybeLocal New(Environment* env, size_t length) { void* data; if (length > 0) { - data = BufferMalloc(length); + data = BufferMalloc(env->isolate(), length); if (data == nullptr) return Local(); } else { @@ -302,7 +304,7 @@ MaybeLocal New(Environment* env, size_t length) { if (ui.IsEmpty()) { // Object failed to be created. Clean up resources. - free(data); + env->isolate()->GetArrayBufferAllocator()->Free(data, length); } return scope.Escape(ui.FromMaybe(Local())); @@ -327,10 +329,11 @@ MaybeLocal Copy(Environment* env, const char* data, size_t length) { return Local(); } + auto* allocator = env->isolate()->GetArrayBufferAllocator(); void* new_data; if (length > 0) { CHECK_NE(data, nullptr); - new_data = node::UncheckedMalloc(length); + new_data = allocator->AllocateUninitialized(length); if (new_data == nullptr) return Local(); memcpy(new_data, data, length); @@ -347,7 +350,7 @@ MaybeLocal Copy(Environment* env, const char* data, size_t length) { if (ui.IsEmpty()) { // Object failed to be created. Clean up resources. - free(new_data); + allocator->Free(new_data, length); } return scope.Escape(ui.FromMaybe(Local())); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index cff8a5346c6..269fd11af19 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1890,7 +1890,8 @@ void SSLWrap::GetSession(const FunctionCallbackInfo& args) { int slen = i2d_SSL_SESSION(sess, nullptr); CHECK_GT(slen, 0); - char* sbuf = Malloc(slen); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + char* sbuf = static_cast(allocator->AllocateUninitialized(slen)); unsigned char* p = reinterpret_cast(sbuf); i2d_SSL_SESSION(sess, &p); args.GetReturnValue().Set(Buffer::New(env, sbuf, slen).ToLocalChecked()); @@ -3011,7 +3012,8 @@ CipherBase::UpdateResult CipherBase::Update(const char* data, return kErrorState; } - *out = Malloc(buff_len); + auto* allocator = env()->isolate()->GetArrayBufferAllocator(); + *out = static_cast(allocator->AllocateUninitialized(buff_len)); int r = EVP_CipherUpdate(ctx_.get(), *out, out_len, @@ -3053,7 +3055,8 @@ void CipherBase::Update(const FunctionCallbackInfo& args) { } if (r != kSuccess) { - free(out); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + allocator->Free(out, out_len); if (r == kErrorState) { ThrowCryptoError(env, ERR_get_error(), "Trying to add data in unsupported state"); @@ -3091,8 +3094,9 @@ bool CipherBase::Final(unsigned char** out, int* out_len) { const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); - *out = Malloc( - static_cast(EVP_CIPHER_CTX_block_size(ctx_.get()))); + auto* allocator = env()->isolate()->GetArrayBufferAllocator(); + *out = static_cast(allocator->AllocateUninitialized( + EVP_CIPHER_CTX_block_size(ctx_.get()))); // In CCM mode, final() only checks whether authentication failed in update(). // EVP_CipherFinal_ex must not be called and will fail. @@ -3135,7 +3139,8 @@ void CipherBase::Final(const FunctionCallbackInfo& args) { bool r = cipher->Final(&out_value, &out_len); if (out_len <= 0 || !r) { - free(out_value); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + allocator->Free(out_value, out_len); out_value = nullptr; out_len = 0; if (!r) { @@ -3781,7 +3786,8 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { template -bool PublicKeyCipher::Cipher(const char* key_pem, +bool PublicKeyCipher::Cipher(Environment* env, + const char* key_pem, int key_pem_len, const char* passphrase, int padding, @@ -3790,6 +3796,7 @@ bool PublicKeyCipher::Cipher(const char* key_pem, unsigned char** out, size_t* out_len) { EVPKeyPointer pkey; + auto* allocator = env->isolate()->GetArrayBufferAllocator(); BIOPointer bp(BIO_new_mem_buf(const_cast(key_pem), key_pem_len)); if (!bp) @@ -3837,7 +3844,7 @@ bool PublicKeyCipher::Cipher(const char* key_pem, if (EVP_PKEY_cipher(ctx.get(), nullptr, out_len, data, len) <= 0) return false; - *out = Malloc(*out_len); + *out = static_cast(allocator->AllocateUninitialized(*out_len)); if (EVP_PKEY_cipher(ctx.get(), *out, out_len, data, len) <= 0) return false; @@ -3870,6 +3877,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { ClearErrorOnReturn clear_error_on_return; bool r = Cipher( + env, kbuf, klen, args.Length() >= 3 && !args[2]->IsNull() ? *passphrase : nullptr, @@ -3880,7 +3888,8 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { &out_len); if (out_len == 0 || !r) { - free(out_value); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + allocator->Free(out_value, out_len); out_value = nullptr; out_len = 0; if (!r) { @@ -4085,7 +4094,8 @@ void DiffieHellman::GenerateKeys(const FunctionCallbackInfo& args) { const BIGNUM* pub_key; DH_get0_key(diffieHellman->dh_.get(), &pub_key, nullptr); size_t size = BN_num_bytes(pub_key); - char* data = Malloc(size); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + char* data = static_cast(allocator->AllocateUninitialized(size)); BN_bn2bin(pub_key, reinterpret_cast(data)); args.GetReturnValue().Set(Buffer::New(env, data, size).ToLocalChecked()); } @@ -4104,7 +4114,8 @@ void DiffieHellman::GetField(const FunctionCallbackInfo& args, if (num == nullptr) return env->ThrowError(err_if_null); size_t size = BN_num_bytes(num); - char* data = Malloc(size); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + char* data = static_cast(allocator->AllocateUninitialized(size)); BN_bn2bin(num, reinterpret_cast(data)); args.GetReturnValue().Set(Buffer::New(env, data, size).ToLocalChecked()); } @@ -4168,7 +4179,8 @@ void DiffieHellman::ComputeSecret(const FunctionCallbackInfo& args) { Buffer::Length(args[0]), 0)); - MallocedBuffer data(DH_size(diffieHellman->dh_.get())); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + MallocedBuffer data(DH_size(diffieHellman->dh_.get()), allocator); int size = DH_compute_key(reinterpret_cast(data.data), key.get(), @@ -4388,13 +4400,14 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { } // NOTE: field_size is in bits + auto* allocator = env->isolate()->GetArrayBufferAllocator(); int field_size = EC_GROUP_get_degree(ecdh->group_); size_t out_len = (field_size + 7) / 8; - char* out = node::Malloc(out_len); + char* out = static_cast(allocator->AllocateUninitialized(out_len)); int r = ECDH_compute_key(out, out_len, pub.get(), ecdh->key_.get(), nullptr); if (!r) { - free(out); + allocator->Free(out, out_len); return env->ThrowError("Failed to compute ECDH key"); } @@ -4424,11 +4437,13 @@ void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { if (size == 0) return env->ThrowError("Failed to get public key length"); - unsigned char* out = node::Malloc(size); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + unsigned char* out = + static_cast(allocator->AllocateUninitialized(size)); int r = EC_POINT_point2oct(ecdh->group_, pub, form, out, size, nullptr); if (r != size) { - free(out); + allocator->Free(out, size); return env->ThrowError("Failed to get public key"); } @@ -4448,11 +4463,13 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { if (b == nullptr) return env->ThrowError("Failed to get ECDH private key"); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); int size = BN_num_bytes(b); - unsigned char* out = node::Malloc(size); + unsigned char* out = + static_cast(allocator->AllocateUninitialized(size)); if (size != BN_bn2bin(b, out)) { - free(out); + allocator->Free(out, size); return env->ThrowError("Failed to convert ECDH private key to Buffer"); } @@ -4572,7 +4589,7 @@ class PBKDF2Request : public AsyncWrap, public ThreadPoolWork { success_(false), pass_(std::move(pass)), salt_(std::move(salt)), - key_(keylen), + key_(keylen, env->isolate()->GetArrayBufferAllocator()), iteration_count_(iteration_count) { } @@ -4634,6 +4651,7 @@ void PBKDF2Request::AfterThreadPoolWork(int status) { void PBKDF2(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); const EVP_MD* digest = nullptr; int keylen = -1; @@ -4642,12 +4660,12 @@ void PBKDF2(const FunctionCallbackInfo& args) { int passlen = Buffer::Length(args[0]); - MallocedBuffer pass(passlen); + MallocedBuffer pass(passlen, allocator); memcpy(pass.data, Buffer::Data(args[0]), passlen); int saltlen = Buffer::Length(args[1]); - MallocedBuffer salt(saltlen); + MallocedBuffer salt(saltlen, allocator); memcpy(salt.data, Buffer::Data(args[1]), saltlen); iteration_count = args[2]->Int32Value(env->context()).FromJust(); @@ -4724,9 +4742,10 @@ class RandomBytesRequest : public AsyncWrap, public ThreadPoolWork { } inline void release() { + size_t free_size = size_; size_ = 0; if (free_mode_ == FREE_DATA) { - free(data_); + env()->isolate()->GetArrayBufferAllocator()->Free(data_, free_size); data_ = nullptr; } } @@ -4840,7 +4859,8 @@ void RandomBytes(const FunctionCallbackInfo& args) { Local obj = env->randombytes_constructor_template()-> NewInstance(env->context()).ToLocalChecked(); - char* data = node::Malloc(size); + char* data = static_cast( + env->isolate()->GetArrayBufferAllocator()->AllocateUninitialized(size)); std::unique_ptr req( new RandomBytesRequest(env, obj, @@ -5015,8 +5035,9 @@ void VerifySpkac(const FunctionCallbackInfo& args) { } -char* ExportPublicKey(const char* data, int len, size_t* size) { +char* ExportPublicKey(Environment* env, const char* data, int len, size_t* size) { char* buf = nullptr; + auto* allocator = env->isolate()->GetArrayBufferAllocator(); BIOPointer bio(BIO_new(BIO_s_mem())); if (!bio) @@ -5037,7 +5058,7 @@ char* ExportPublicKey(const char* data, int len, size_t* size) { BIO_get_mem_ptr(bio.get(), &ptr); *size = ptr->length; - buf = Malloc(*size); + buf = static_cast(allocator->AllocateUninitialized(*size)); memcpy(buf, ptr->data, *size); return buf; @@ -5055,7 +5076,7 @@ void ExportPublicKey(const FunctionCallbackInfo& args) { CHECK_NE(data, nullptr); size_t pkey_size; - char* pkey = ExportPublicKey(data, length, &pkey_size); + char* pkey = ExportPublicKey(env, data, length, &pkey_size); if (pkey == nullptr) return args.GetReturnValue().SetEmptyString(); diff --git a/src/node_crypto.h b/src/node_crypto.h index ee933ede1f8..ddde9d64199 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -554,7 +554,8 @@ class PublicKeyCipher { template - static bool Cipher(const char* key_pem, + static bool Cipher(Environment* env, + const char* key_pem, int key_pem_len, const char* passphrase, int padding, diff --git a/src/stream_base.cc b/src/stream_base.cc index 3708ffe7b65..2a107a33cac 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -355,7 +355,13 @@ void StreamResource::ClearError() { uv_buf_t StreamListener::OnStreamAlloc(size_t suggested_size) { - return uv_buf_init(Malloc(suggested_size), suggested_size); + CHECK_NE(stream_, nullptr); + StreamBase* stream = static_cast(stream_); + Environment* env = stream->stream_env(); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + return uv_buf_init( + static_cast(allocator->AllocateUninitialized(suggested_size)), + suggested_size); } @@ -363,11 +369,12 @@ void EmitToJSStreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { CHECK_NE(stream_, nullptr); StreamBase* stream = static_cast(stream_); Environment* env = stream->stream_env(); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); if (nread <= 0) { - free(buf.base); + allocator->Free(buf.base, buf.len); if (nread < 0) stream->CallJSOnreadMethod(nread, Local()); return; diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 1d1ded449bd..7500793f78c 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -447,7 +447,10 @@ void UDPWrap::OnSend(uv_udp_send_t* req, int status) { void UDPWrap::OnAlloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - buf->base = node::Malloc(suggested_size); + auto* wrap = static_cast(handle->data); + auto* allocator = wrap->env()->isolate()->GetArrayBufferAllocator(); + buf->base = + static_cast(allocator->AllocateUninitialized(suggested_size)); buf->len = suggested_size; } @@ -457,15 +460,16 @@ void UDPWrap::OnRecv(uv_udp_t* handle, const uv_buf_t* buf, const struct sockaddr* addr, unsigned int flags) { + UDPWrap* wrap = static_cast(handle->data); + Environment* env = wrap->env(); + auto* allocator = env->isolate()->GetArrayBufferAllocator(); + if (nread == 0 && addr == nullptr) { if (buf->base != nullptr) - free(buf->base); + allocator->Free(buf->base, buf->len); return; } - UDPWrap* wrap = static_cast(handle->data); - Environment* env = wrap->env(); - HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); @@ -479,13 +483,15 @@ void UDPWrap::OnRecv(uv_udp_t* handle, if (nread < 0) { if (buf->base != nullptr) - free(buf->base); + allocator->Free(buf->base, buf->len); wrap->MakeCallback(env->onmessage_string(), arraysize(argv), argv); return; } - char* base = node::UncheckedRealloc(buf->base, nread); - argv[2] = Buffer::New(env, base, nread).ToLocalChecked(); + // Note that nread may be smaller thant buf->len, in that case the length + // passed to ArrayBufferAllocator::Free would not be the correct one, but + // it should be fine unless embedder is using some unusual memory allocator. + argv[2] = Buffer::New(env, buf->base, nread).ToLocalChecked(); argv[3] = AddressToJS(env, addr); wrap->MakeCallback(env->onmessage_string(), arraysize(argv), argv); } diff --git a/src/util.h b/src/util.h index 0e6fd5dd067..183a1622dd0 100644 --- a/src/util.h +++ b/src/util.h @@ -424,24 +424,35 @@ template struct MallocedBuffer { T* data; size_t size; + v8::ArrayBuffer::Allocator* allocator; T* release() { + allocator = nullptr; T* ret = data; data = nullptr; return ret; } - MallocedBuffer() : data(nullptr) {} - explicit MallocedBuffer(size_t size) : data(Malloc(size)), size(size) {} - MallocedBuffer(MallocedBuffer&& other) : data(other.data), size(other.size) { + MallocedBuffer() : data(nullptr), allocator(nullptr) {} + MallocedBuffer(size_t size, v8::ArrayBuffer::Allocator* allocator) + : size(size), allocator(allocator) { + data = static_cast(allocator->AllocateUninitialized(size)); + } + MallocedBuffer(MallocedBuffer&& other) + : data(other.data), size(other.size), allocator(other.allocator) { other.data = nullptr; + other.allocator = nullptr; } MallocedBuffer& operator=(MallocedBuffer&& other) { this->~MallocedBuffer(); return *new(this) MallocedBuffer(other); } ~MallocedBuffer() { - free(data); + if (allocator) { + allocator->Free(data, size); + } else { + free(data); + } } MallocedBuffer(const MallocedBuffer&) = delete; MallocedBuffer& operator=(const MallocedBuffer&) = delete;