src: allow not materializing ArrayBuffers from C++

Where appropriate, use a helper that wraps around
`ArrayBufferView::Buffer()` or `ArrayBufferView::CopyContents()`
rather than `Buffer::Data()`, as that may help to avoid materializing
the underlying `ArrayBuffer` when reading small typed arrays from C++.
This allows keeping the performance benefits of the faster creation of
heap-allocated small typed arrays in many cases.

PR-URL: https://github.com/nodejs/node/pull/26301
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Anna Henningsen 2019-02-25 04:12:19 +01:00
parent b42dcb0eeb
commit 31975bbc88
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
8 changed files with 183 additions and 145 deletions

View File

@ -57,6 +57,7 @@ namespace crypto {
using node::THROW_ERR_TLS_INVALID_PROTOCOL_METHOD; using node::THROW_ERR_TLS_INVALID_PROTOCOL_METHOD;
using v8::Array; using v8::Array;
using v8::ArrayBufferView;
using v8::Boolean; using v8::Boolean;
using v8::ConstructorBehavior; using v8::ConstructorBehavior;
using v8::Context; using v8::Context;
@ -539,8 +540,9 @@ static BIOPointer LoadBIO(Environment* env, Local<Value> v) {
return NodeBIO::NewFixed(*s, s.length()); return NodeBIO::NewFixed(*s, s.length());
} }
if (Buffer::HasInstance(v)) { if (v->IsArrayBufferView()) {
return NodeBIO::NewFixed(Buffer::Data(v), Buffer::Length(v)); ArrayBufferViewContents<char> buf(v.As<ArrayBufferView>());
return NodeBIO::NewFixed(buf.data(), buf.length());
} }
return nullptr; return nullptr;
@ -1135,9 +1137,10 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
if (args.Length() >= 2) { if (args.Length() >= 2) {
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[1], "Pass phrase"); THROW_AND_RETURN_IF_NOT_BUFFER(env, args[1], "Pass phrase");
size_t passlen = Buffer::Length(args[1]); Local<ArrayBufferView> abv = args[1].As<ArrayBufferView>();
size_t passlen = abv->ByteLength();
pass.resize(passlen + 1); pass.resize(passlen + 1);
memcpy(pass.data(), Buffer::Data(args[1]), passlen); abv->CopyContents(pass.data(), passlen);
pass[passlen] = '\0'; pass[passlen] = '\0';
} }
@ -1261,15 +1264,16 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {
} }
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Ticket keys"); THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Ticket keys");
ArrayBufferViewContents<char> buf(args[0].As<ArrayBufferView>());
if (Buffer::Length(args[0]) != 48) { if (buf.length() != 48) {
return THROW_ERR_INVALID_ARG_VALUE( return THROW_ERR_INVALID_ARG_VALUE(
env, "Ticket keys length must be 48 bytes"); env, "Ticket keys length must be 48 bytes");
} }
memcpy(wrap->ticket_key_name_, Buffer::Data(args[0]), 16); memcpy(wrap->ticket_key_name_, buf.data(), 16);
memcpy(wrap->ticket_key_hmac_, Buffer::Data(args[0]) + 16, 16); memcpy(wrap->ticket_key_hmac_, buf.data() + 16, 16);
memcpy(wrap->ticket_key_aes_, Buffer::Data(args[0]) + 32, 16); memcpy(wrap->ticket_key_aes_, buf.data() + 32, 16);
args.GetReturnValue().Set(true); args.GetReturnValue().Set(true);
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys) #endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
@ -1349,29 +1353,29 @@ int SecureContext::TicketKeyCallback(SSL* ssl,
return -1; return -1;
} }
memcpy(name, Buffer::Data(name_val), kTicketPartSize); name_val.As<ArrayBufferView>()->CopyContents(name, kTicketPartSize);
memcpy(iv, Buffer::Data(iv_val), kTicketPartSize); iv_val.As<ArrayBufferView>()->CopyContents(iv, kTicketPartSize);
} }
ArrayBufferViewContents<unsigned char> hmac_buf(hmac);
HMAC_Init_ex(hctx, HMAC_Init_ex(hctx,
Buffer::Data(hmac), hmac_buf.data(),
Buffer::Length(hmac), hmac_buf.length(),
EVP_sha256(), EVP_sha256(),
nullptr); nullptr);
const unsigned char* aes_key = ArrayBufferViewContents<unsigned char> aes_key(aes.As<ArrayBufferView>());
reinterpret_cast<unsigned char*>(Buffer::Data(aes));
if (enc) { if (enc) {
EVP_EncryptInit_ex(ectx, EVP_EncryptInit_ex(ectx,
EVP_aes_128_cbc(), EVP_aes_128_cbc(),
nullptr, nullptr,
aes_key, aes_key.data(),
iv); iv);
} else { } else {
EVP_DecryptInit_ex(ectx, EVP_DecryptInit_ex(ectx,
EVP_aes_128_cbc(), EVP_aes_128_cbc(),
nullptr, nullptr,
aes_key, aes_key.data(),
iv); iv);
} }
@ -2094,13 +2098,10 @@ void SSLWrap<Base>::SetSession(const FunctionCallbackInfo<Value>& args) {
} }
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Session"); THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Session");
size_t slen = Buffer::Length(args[0]); ArrayBufferViewContents<unsigned char> sbuf(args[0].As<ArrayBufferView>());
std::vector<char> sbuf(slen);
if (char* p = Buffer::Data(args[0]))
sbuf.assign(p, p + slen);
const unsigned char* p = reinterpret_cast<const unsigned char*>(sbuf.data()); const unsigned char* p = sbuf.data();
SSLSessionPointer sess(d2i_SSL_SESSION(nullptr, &p, slen)); SSLSessionPointer sess(d2i_SSL_SESSION(nullptr, &p, sbuf.length()));
if (sess == nullptr) if (sess == nullptr)
return; return;
@ -2118,11 +2119,10 @@ void SSLWrap<Base>::LoadSession(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
if (args.Length() >= 1 && Buffer::HasInstance(args[0])) { if (args.Length() >= 1 && Buffer::HasInstance(args[0])) {
ssize_t slen = Buffer::Length(args[0]); ArrayBufferViewContents<unsigned char> sbuf(args[0]);
char* sbuf = Buffer::Data(args[0]);
const unsigned char* p = reinterpret_cast<unsigned char*>(sbuf); const unsigned char* p = sbuf.data();
SSL_SESSION* sess = d2i_SSL_SESSION(nullptr, &p, slen); SSL_SESSION* sess = d2i_SSL_SESSION(nullptr, &p, sbuf.length());
// Setup next session and move hello to the BIO buffer // Setup next session and move hello to the BIO buffer
w->next_sess_.reset(sess); w->next_sess_.reset(sess);
@ -2204,7 +2204,7 @@ void SSLWrap<Base>::SetOCSPResponse(const FunctionCallbackInfo<Value>& args) {
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "OCSP response"); THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "OCSP response");
w->ocsp_response_.Reset(args.GetIsolate(), args[0].As<Object>()); w->ocsp_response_.Reset(args.GetIsolate(), args[0].As<ArrayBufferView>());
} }
@ -2403,12 +2403,10 @@ int SSLWrap<Base>::SelectALPNCallback(SSL* s,
w->object()->GetPrivate( w->object()->GetPrivate(
env->context(), env->context(),
env->alpn_buffer_private_symbol()).ToLocalChecked(); env->alpn_buffer_private_symbol()).ToLocalChecked();
CHECK(Buffer::HasInstance(alpn_buffer)); ArrayBufferViewContents<unsigned char> alpn_protos(alpn_buffer);
const unsigned char* alpn_protos =
reinterpret_cast<const unsigned char*>(Buffer::Data(alpn_buffer));
unsigned alpn_protos_len = Buffer::Length(alpn_buffer);
int status = SSL_select_next_proto(const_cast<unsigned char**>(out), outlen, int status = SSL_select_next_proto(const_cast<unsigned char**>(out), outlen,
alpn_protos, alpn_protos_len, in, inlen); alpn_protos.data(), alpn_protos.length(),
in, inlen);
// According to 3.2. Protocol Selection of RFC7301, fatal // According to 3.2. Protocol Selection of RFC7301, fatal
// no_application_protocol alert shall be sent but OpenSSL 1.0.2 does not // no_application_protocol alert shall be sent but OpenSSL 1.0.2 does not
// support it yet. See // support it yet. See
@ -2446,10 +2444,9 @@ void SSLWrap<Base>::SetALPNProtocols(const FunctionCallbackInfo<Value>& args) {
return env->ThrowTypeError("Must give a Buffer as first argument"); return env->ThrowTypeError("Must give a Buffer as first argument");
if (w->is_client()) { if (w->is_client()) {
const unsigned char* alpn_protos = ArrayBufferViewContents<unsigned char> alpn_protos(args[0]);
reinterpret_cast<const unsigned char*>(Buffer::Data(args[0])); int r = SSL_set_alpn_protos(
unsigned alpn_protos_len = Buffer::Length(args[0]); w->ssl_.get(), alpn_protos.data(), alpn_protos.length());
int r = SSL_set_alpn_protos(w->ssl_.get(), alpn_protos, alpn_protos_len);
CHECK_EQ(r, 0); CHECK_EQ(r, 0);
} else { } else {
CHECK( CHECK(
@ -2493,14 +2490,13 @@ int SSLWrap<Base>::TLSExtStatusCallback(SSL* s, void* arg) {
if (w->ocsp_response_.IsEmpty()) if (w->ocsp_response_.IsEmpty())
return SSL_TLSEXT_ERR_NOACK; return SSL_TLSEXT_ERR_NOACK;
Local<Object> obj = PersistentToLocal::Default(env->isolate(), Local<ArrayBufferView> obj = PersistentToLocal::Default(env->isolate(),
w->ocsp_response_); w->ocsp_response_);
char* resp = Buffer::Data(obj); size_t len = obj->ByteLength();
size_t len = Buffer::Length(obj);
// OpenSSL takes control of the pointer after accepting it // OpenSSL takes control of the pointer after accepting it
unsigned char* data = MallocOpenSSL<unsigned char>(len); unsigned char* data = MallocOpenSSL<unsigned char>(len);
memcpy(data, resp, len); obj->CopyContents(data, len);
if (!SSL_set_tlsext_status_ocsp_resp(s, data, len)) if (!SSL_set_tlsext_status_ocsp_resp(s, data, len))
OPENSSL_free(data); OPENSSL_free(data);
@ -2998,10 +2994,12 @@ ByteSource ByteSource::FromString(Environment* env, Local<String> str,
} }
ByteSource ByteSource::FromBuffer(Local<Value> buffer, bool ntc) { ByteSource ByteSource::FromBuffer(Local<Value> buffer, bool ntc) {
size_t size = Buffer::Length(buffer); CHECK(buffer->IsArrayBufferView());
Local<ArrayBufferView> abv = buffer.As<ArrayBufferView>();
size_t size = abv->ByteLength();
if (ntc) { if (ntc) {
char* data = MallocOpenSSL<char>(size + 1); char* data = MallocOpenSSL<char>(size + 1);
memcpy(data, Buffer::Data(buffer), size); abv->CopyContents(data, size);
data[size] = 0; data[size] = 0;
return Allocated(data, size); return Allocated(data, size);
} }
@ -3404,7 +3402,8 @@ void KeyObject::Init(const FunctionCallbackInfo<Value>& args) {
switch (key->key_type_) { switch (key->key_type_) {
case kKeyTypeSecret: case kKeyTypeSecret:
CHECK_EQ(args.Length(), 1); CHECK_EQ(args.Length(), 1);
key->InitSecret(Buffer::Data(args[0]), Buffer::Length(args[0])); CHECK(args[0]->IsArrayBufferView());
key->InitSecret(args[0].As<ArrayBufferView>());
break; break;
case kKeyTypePublic: case kKeyTypePublic:
CHECK_EQ(args.Length(), 3); CHECK_EQ(args.Length(), 3);
@ -3429,11 +3428,12 @@ void KeyObject::Init(const FunctionCallbackInfo<Value>& args) {
} }
} }
void KeyObject::InitSecret(const char* key, size_t key_len) { void KeyObject::InitSecret(v8::Local<v8::ArrayBufferView> abv) {
CHECK_EQ(this->key_type_, kKeyTypeSecret); CHECK_EQ(this->key_type_, kKeyTypeSecret);
size_t key_len = abv->ByteLength();
char* mem = MallocOpenSSL<char>(key_len); char* mem = MallocOpenSSL<char>(key_len);
memcpy(mem, key, key_len); abv->CopyContents(mem, key_len);
this->symmetric_key_ = std::unique_ptr<char, std::function<void(char*)>>(mem, this->symmetric_key_ = std::unique_ptr<char, std::function<void(char*)>>(mem,
[key_len](char* p) { [key_len](char* p) {
OPENSSL_clear_free(p, key_len); OPENSSL_clear_free(p, key_len);
@ -3643,8 +3643,7 @@ void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
CHECK_GE(args.Length(), 3); CHECK_GE(args.Length(), 3);
const node::Utf8Value cipher_type(args.GetIsolate(), args[0]); const node::Utf8Value cipher_type(args.GetIsolate(), args[0]);
const char* key_buf = Buffer::Data(args[1]); ArrayBufferViewContents<char> key_buf(args[1]);
ssize_t key_buf_len = Buffer::Length(args[1]);
// Don't assign to cipher->auth_tag_len_ directly; the value might not // Don't assign to cipher->auth_tag_len_ directly; the value might not
// represent a valid length at this point. // represent a valid length at this point.
@ -3656,7 +3655,7 @@ void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
auth_tag_len = kNoAuthTagLength; auth_tag_len = kNoAuthTagLength;
} }
cipher->Init(*cipher_type, key_buf, key_buf_len, auth_tag_len); cipher->Init(*cipher_type, key_buf.data(), key_buf.length(), auth_tag_len);
} }
void CipherBase::InitIv(const char* cipher_type, void CipherBase::InitIv(const char* cipher_type,
@ -3712,14 +3711,12 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
const node::Utf8Value cipher_type(env->isolate(), args[0]); const node::Utf8Value cipher_type(env->isolate(), args[0]);
const ByteSource key = GetSecretKeyBytes(env, args[1]); const ByteSource key = GetSecretKeyBytes(env, args[1]);
ssize_t iv_len; ArrayBufferViewContents<unsigned char> iv_buf;
const unsigned char* iv_buf; ssize_t iv_len = -1;
if (args[2]->IsNull()) { if (!args[2]->IsNull()) {
iv_buf = nullptr; CHECK(args[2]->IsArrayBufferView());
iv_len = -1; iv_buf.Read(args[2].As<ArrayBufferView>());
} else { iv_len = iv_buf.length();
iv_buf = reinterpret_cast<unsigned char*>(Buffer::Data(args[2]));
iv_len = Buffer::Length(args[2]);
} }
// Don't assign to cipher->auth_tag_len_ directly; the value might not // Don't assign to cipher->auth_tag_len_ directly; the value might not
@ -3735,7 +3732,7 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
cipher->InitIv(*cipher_type, cipher->InitIv(*cipher_type,
reinterpret_cast<const unsigned char*>(key.get()), reinterpret_cast<const unsigned char*>(key.get()),
key.size(), key.size(),
iv_buf, iv_buf.data(),
iv_len, iv_len,
auth_tag_len); auth_tag_len);
} }
@ -3889,7 +3886,8 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
CHECK_LE(cipher->auth_tag_len_, sizeof(cipher->auth_tag_)); CHECK_LE(cipher->auth_tag_len_, sizeof(cipher->auth_tag_));
memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_)); memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_));
memcpy(cipher->auth_tag_, Buffer::Data(args[0]), cipher->auth_tag_len_); args[0].As<ArrayBufferView>()->CopyContents(
cipher->auth_tag_, cipher->auth_tag_len_);
args.GetReturnValue().Set(true); args.GetReturnValue().Set(true);
} }
@ -3953,9 +3951,9 @@ void CipherBase::SetAAD(const FunctionCallbackInfo<Value>& args) {
CHECK_EQ(args.Length(), 2); CHECK_EQ(args.Length(), 2);
CHECK(args[1]->IsInt32()); CHECK(args[1]->IsInt32());
int plaintext_len = args[1].As<Int32>()->Value(); int plaintext_len = args[1].As<Int32>()->Value();
ArrayBufferViewContents<char> buf(args[0]);
bool b = cipher->SetAAD(Buffer::Data(args[0]), Buffer::Length(args[0]), bool b = cipher->SetAAD(buf.data(), buf.length(), plaintext_len);
plaintext_len);
args.GetReturnValue().Set(b); // Possibly report invalid state failure args.GetReturnValue().Set(b); // Possibly report invalid state failure
} }
@ -4028,9 +4026,8 @@ void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
return; return;
r = cipher->Update(decoder.out(), decoder.size(), &out); r = cipher->Update(decoder.out(), decoder.size(), &out);
} else { } else {
char* buf = Buffer::Data(args[0]); ArrayBufferViewContents<char> buf(args[0]);
size_t buflen = Buffer::Length(args[0]); r = cipher->Update(buf.data(), buf.length(), &out);
r = cipher->Update(buf, buflen, &out);
} }
if (r != kSuccess) { if (r != kSuccess) {
@ -4213,10 +4210,8 @@ void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
r = hmac->HmacUpdate(decoder.out(), decoder.size()); r = hmac->HmacUpdate(decoder.out(), decoder.size());
} }
} else { } else {
CHECK(args[0]->IsArrayBufferView()); ArrayBufferViewContents<char> buf(args[0]);
char* buf = Buffer::Data(args[0]); r = hmac->HmacUpdate(buf.data(), buf.length());
size_t buflen = Buffer::Length(args[0]);
r = hmac->HmacUpdate(buf, buflen);
} }
args.GetReturnValue().Set(r); args.GetReturnValue().Set(r);
@ -4324,9 +4319,8 @@ void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
} }
r = hash->HashUpdate(decoder.out(), decoder.size()); r = hash->HashUpdate(decoder.out(), decoder.size());
} else if (args[0]->IsArrayBufferView()) { } else if (args[0]->IsArrayBufferView()) {
char* buf = Buffer::Data(args[0]); ArrayBufferViewContents<char> buf(args[0].As<ArrayBufferView>());
size_t buflen = Buffer::Length(args[0]); r = hash->HashUpdate(buf.data(), buf.length());
r = hash->HashUpdate(buf, buflen);
} }
args.GetReturnValue().Set(r); args.GetReturnValue().Set(r);
@ -4487,9 +4481,8 @@ void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&sign, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&sign, args.Holder());
Error err; Error err;
char* buf = Buffer::Data(args[0]); ArrayBufferViewContents<char> buf(args[0]);
size_t buflen = Buffer::Length(args[0]); err = sign->Update(buf.data(), buf.length());
err = sign->Update(buf, buflen);
sign->CheckThrow(err); sign->CheckThrow(err);
} }
@ -4634,9 +4627,8 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&verify, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&verify, args.Holder());
Error err; Error err;
char* buf = Buffer::Data(args[0]); ArrayBufferViewContents<char> buf(args[0]);
size_t buflen = Buffer::Length(args[0]); err = verify->Update(buf.data(), buf.length());
err = verify->Update(buf, buflen);
verify->CheckThrow(err); verify->CheckThrow(err);
} }
@ -4688,8 +4680,7 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
if (!pkey) if (!pkey)
return; return;
char* hbuf = Buffer::Data(args[offset]); ArrayBufferViewContents<char> hbuf(args[offset]);
ssize_t hlen = Buffer::Length(args[offset]);
CHECK(args[offset + 1]->IsInt32()); CHECK(args[offset + 1]->IsInt32());
int padding = args[offset + 1].As<Int32>()->Value(); int padding = args[offset + 1].As<Int32>()->Value();
@ -4698,8 +4689,8 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
int salt_len = args[offset + 2].As<Int32>()->Value(); int salt_len = args[offset + 2].As<Int32>()->Value();
bool verify_result; bool verify_result;
Error err = verify->VerifyFinal(pkey, hbuf, hlen, padding, salt_len, Error err = verify->VerifyFinal(pkey, hbuf.data(), hbuf.length(), padding,
&verify_result); salt_len, &verify_result);
if (err != kSignOk) if (err != kSignOk)
return verify->CheckThrow(err); return verify->CheckThrow(err);
args.GetReturnValue().Set(verify_result); args.GetReturnValue().Set(verify_result);
@ -4753,8 +4744,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
return; return;
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[offset], "Data"); THROW_AND_RETURN_IF_NOT_BUFFER(env, args[offset], "Data");
char* buf = Buffer::Data(args[offset]); ArrayBufferViewContents<unsigned char> buf(args[offset]);
ssize_t len = Buffer::Length(args[offset]);
uint32_t padding; uint32_t padding;
if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return; if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return;
@ -4767,8 +4757,8 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
env, env,
pkey, pkey,
padding, padding,
reinterpret_cast<const unsigned char*>(buf), buf.data(),
len, buf.length(),
&out); &out);
if (!r) if (!r)
@ -4906,15 +4896,15 @@ void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
args[1].As<Int32>()->Value()); args[1].As<Int32>()->Value());
} }
} else { } else {
ArrayBufferViewContents<char> arg0(args[0]);
if (args[1]->IsInt32()) { if (args[1]->IsInt32()) {
initialized = diffieHellman->Init(Buffer::Data(args[0]), initialized = diffieHellman->Init(arg0.data(),
Buffer::Length(args[0]), arg0.length(),
args[1].As<Int32>()->Value()); args[1].As<Int32>()->Value());
} else { } else {
initialized = diffieHellman->Init(Buffer::Data(args[0]), ArrayBufferViewContents<char> arg1(args[1]);
Buffer::Length(args[0]), initialized = diffieHellman->Init(arg0.data(), arg0.length(),
Buffer::Data(args[1]), arg1.data(), arg1.length());
Buffer::Length(args[1]));
} }
} }
} }
@ -5017,10 +5007,8 @@ void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
} }
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Other party's public key"); THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Other party's public key");
BignumPointer key(BN_bin2bn( ArrayBufferViewContents<unsigned char> key_buf(args[0].As<ArrayBufferView>());
reinterpret_cast<unsigned char*>(Buffer::Data(args[0])), BignumPointer key(BN_bin2bn(key_buf.data(), key_buf.length(), nullptr));
Buffer::Length(args[0]),
nullptr));
AllocatedBuffer ret = env->AllocateManaged(DH_size(diffieHellman->dh_.get())); AllocatedBuffer ret = env->AllocateManaged(DH_size(diffieHellman->dh_.get()));
@ -5087,9 +5075,9 @@ void DiffieHellman::SetKey(const FunctionCallbackInfo<Value>& args,
return THROW_ERR_INVALID_ARG_TYPE(env, errmsg); return THROW_ERR_INVALID_ARG_TYPE(env, errmsg);
} }
ArrayBufferViewContents<unsigned char> buf(args[0].As<ArrayBufferView>());
BIGNUM* num = BIGNUM* num =
BN_bin2bn(reinterpret_cast<unsigned char*>(Buffer::Data(args[0])), BN_bin2bn(buf.data(), buf.length(), nullptr);
Buffer::Length(args[0]), nullptr);
CHECK_NOT_NULL(num); CHECK_NOT_NULL(num);
CHECK_EQ(1, set_field(dh->dh_.get(), num)); CHECK_EQ(1, set_field(dh->dh_.get(), num));
} }
@ -5188,8 +5176,7 @@ void ECDH::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
ECPointPointer ECDH::BufferToPoint(Environment* env, ECPointPointer ECDH::BufferToPoint(Environment* env,
const EC_GROUP* group, const EC_GROUP* group,
char* data, Local<Value> buf) {
size_t len) {
int r; int r;
ECPointPointer pub(EC_POINT_new(group)); ECPointPointer pub(EC_POINT_new(group));
@ -5198,11 +5185,12 @@ ECPointPointer ECDH::BufferToPoint(Environment* env,
return pub; return pub;
} }
ArrayBufferViewContents<unsigned char> input(buf);
r = EC_POINT_oct2point( r = EC_POINT_oct2point(
group, group,
pub.get(), pub.get(),
reinterpret_cast<unsigned char*>(data), input.data(),
len, input.length(),
nullptr); nullptr);
if (!r) if (!r)
return ECPointPointer(); return ECPointPointer();
@ -5227,8 +5215,7 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
ECPointPointer pub( ECPointPointer pub(
ECDH::BufferToPoint(env, ECDH::BufferToPoint(env,
ecdh->group_, ecdh->group_,
Buffer::Data(args[0]), args[0]));
Buffer::Length(args[0])));
if (!pub) { if (!pub) {
args.GetReturnValue().Set( args.GetReturnValue().Set(
FIXED_ONE_BYTE_STRING(env->isolate(), FIXED_ONE_BYTE_STRING(env->isolate(),
@ -5360,8 +5347,7 @@ void ECDH::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
ECPointPointer pub( ECPointPointer pub(
ECDH::BufferToPoint(env, ECDH::BufferToPoint(env,
ecdh->group_, ecdh->group_,
Buffer::Data(args[0].As<Object>()), args[0]));
Buffer::Length(args[0].As<Object>())));
if (!pub) if (!pub)
return env->ThrowError("Failed to convert Buffer to EC_POINT"); return env->ThrowError("Failed to convert Buffer to EC_POINT");
@ -5427,8 +5413,10 @@ void CryptoJob::Run(std::unique_ptr<CryptoJob> job, Local<Value> wrap) {
inline void CopyBuffer(Local<Value> buf, std::vector<char>* vec) { inline void CopyBuffer(Local<Value> buf, std::vector<char>* vec) {
CHECK(buf->IsArrayBufferView());
vec->clear(); vec->clear();
if (auto p = Buffer::Data(buf)) vec->assign(p, p + Buffer::Length(buf)); vec->resize(buf.As<ArrayBufferView>()->ByteLength());
buf.As<ArrayBufferView>()->CopyContents(vec->data(), vec->size());
} }
@ -6033,14 +6021,13 @@ bool VerifySpkac(const char* data, unsigned int len) {
void VerifySpkac(const FunctionCallbackInfo<Value>& args) { void VerifySpkac(const FunctionCallbackInfo<Value>& args) {
bool verify_result = false; bool verify_result = false;
size_t length = Buffer::Length(args[0]); ArrayBufferViewContents<char> input(args[0]);
if (length == 0) if (input.length() == 0)
return args.GetReturnValue().Set(verify_result); return args.GetReturnValue().SetEmptyString();
char* data = Buffer::Data(args[0]); CHECK_NOT_NULL(input.data());
CHECK_NOT_NULL(data);
verify_result = VerifySpkac(data, length); verify_result = VerifySpkac(input.data(), input.length());
args.GetReturnValue().Set(verify_result); args.GetReturnValue().Set(verify_result);
} }
@ -6075,15 +6062,15 @@ AllocatedBuffer ExportPublicKey(Environment* env,
void ExportPublicKey(const FunctionCallbackInfo<Value>& args) { void ExportPublicKey(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
size_t length = Buffer::Length(args[0]); ArrayBufferViewContents<char> input(args[0]);
if (length == 0) if (input.length() == 0)
return args.GetReturnValue().SetEmptyString(); return args.GetReturnValue().SetEmptyString();
char* data = Buffer::Data(args[0]); CHECK_NOT_NULL(input.data());
CHECK_NOT_NULL(data);
size_t pkey_size; size_t pkey_size;
AllocatedBuffer pkey = ExportPublicKey(env, data, length, &pkey_size); AllocatedBuffer pkey =
ExportPublicKey(env, input.data(), input.length(), &pkey_size);
if (pkey.data() == nullptr) if (pkey.data() == nullptr)
return args.GetReturnValue().SetEmptyString(); return args.GetReturnValue().SetEmptyString();
@ -6130,8 +6117,9 @@ void ConvertKey(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
CHECK_EQ(args.Length(), 3); CHECK_EQ(args.Length(), 3);
CHECK(args[0]->IsArrayBufferView());
size_t len = Buffer::Length(args[0]); size_t len = args[0].As<ArrayBufferView>()->ByteLength();
if (len == 0) if (len == 0)
return args.GetReturnValue().SetEmptyString(); return args.GetReturnValue().SetEmptyString();
@ -6149,8 +6137,7 @@ void ConvertKey(const FunctionCallbackInfo<Value>& args) {
ECPointPointer pub( ECPointPointer pub(
ECDH::BufferToPoint(env, ECDH::BufferToPoint(env,
group.get(), group.get(),
Buffer::Data(args[0]), args[0]));
len));
if (pub == nullptr) if (pub == nullptr)
return env->ThrowError("Failed to convert Buffer to EC_POINT"); return env->ThrowError("Failed to convert Buffer to EC_POINT");

View File

@ -333,7 +333,7 @@ class SSLWrap {
ClientHelloParser hello_parser_; ClientHelloParser hello_parser_;
Persistent<v8::Object> ocsp_response_; Persistent<v8::ArrayBufferView> ocsp_response_;
Persistent<v8::Value> sni_context_; Persistent<v8::Value> sni_context_;
friend class SecureContext; friend class SecureContext;
@ -462,7 +462,7 @@ class KeyObject : public BaseObject {
static void New(const v8::FunctionCallbackInfo<v8::Value>& args); static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Init(const v8::FunctionCallbackInfo<v8::Value>& args); static void Init(const v8::FunctionCallbackInfo<v8::Value>& args);
void InitSecret(const char* key, size_t key_len); void InitSecret(v8::Local<v8::ArrayBufferView> abv);
void InitPublic(const ManagedEVPPKey& pkey); void InitPublic(const ManagedEVPPKey& pkey);
void InitPrivate(const ManagedEVPPKey& pkey); void InitPrivate(const ManagedEVPPKey& pkey);
@ -803,8 +803,7 @@ class ECDH : public BaseObject {
static void Initialize(Environment* env, v8::Local<v8::Object> target); static void Initialize(Environment* env, v8::Local<v8::Object> target);
static ECPointPointer BufferToPoint(Environment* env, static ECPointPointer BufferToPoint(Environment* env,
const EC_GROUP* group, const EC_GROUP* group,
char* data, v8::Local<v8::Value> buf);
size_t len);
// TODO(joyeecheung): track the memory used by OpenSSL types // TODO(joyeecheung): track the memory used by OpenSSL types
SET_NO_MEMORY_INFO() SET_NO_MEMORY_INFO()

View File

@ -12,6 +12,7 @@
namespace node { namespace node {
using v8::ArrayBuffer; using v8::ArrayBuffer;
using v8::ArrayBufferView;
using v8::Boolean; using v8::Boolean;
using v8::Context; using v8::Context;
using v8::Float64Array; using v8::Float64Array;
@ -2483,7 +2484,7 @@ void Http2Session::Request(const FunctionCallbackInfo<Value>& args) {
// state of the Http2Session, it's simply a notification. // state of the Http2Session, it's simply a notification.
void Http2Session::Goaway(uint32_t code, void Http2Session::Goaway(uint32_t code,
int32_t lastStreamID, int32_t lastStreamID,
uint8_t* data, const uint8_t* data,
size_t len) { size_t len) {
if (IsDestroyed()) if (IsDestroyed())
return; return;
@ -2508,16 +2509,13 @@ void Http2Session::Goaway(const FunctionCallbackInfo<Value>& args) {
uint32_t code = args[0]->Uint32Value(context).ToChecked(); uint32_t code = args[0]->Uint32Value(context).ToChecked();
int32_t lastStreamID = args[1]->Int32Value(context).ToChecked(); int32_t lastStreamID = args[1]->Int32Value(context).ToChecked();
Local<Value> opaqueData = args[2]; ArrayBufferViewContents<uint8_t> opaque_data;
uint8_t* data = nullptr;
size_t length = 0;
if (Buffer::HasInstance(opaqueData)) { if (args[2]->IsArrayBufferView()) {
data = reinterpret_cast<uint8_t*>(Buffer::Data(opaqueData)); opaque_data.Read(args[2].As<ArrayBufferView>());
length = Buffer::Length(opaqueData);
} }
session->Goaway(code, lastStreamID, data, length); session->Goaway(code, lastStreamID, opaque_data.data(), opaque_data.length());
} }
// Update accounting of data chunks. This is used primarily to manage timeout // Update accounting of data chunks. This is used primarily to manage timeout
@ -2771,10 +2769,10 @@ void Http2Session::Ping(const FunctionCallbackInfo<Value>& args) {
// A PING frame may have exactly 8 bytes of payload data. If not provided, // A PING frame may have exactly 8 bytes of payload data. If not provided,
// then the current hrtime will be used as the payload. // then the current hrtime will be used as the payload.
uint8_t* payload = nullptr; ArrayBufferViewContents<uint8_t, 8> payload;
if (Buffer::HasInstance(args[0])) { if (args[0]->IsArrayBufferView()) {
payload = reinterpret_cast<uint8_t*>(Buffer::Data(args[0])); payload.Read(args[0].As<ArrayBufferView>());
CHECK_EQ(Buffer::Length(args[0]), 8); CHECK_EQ(payload.length(), 8);
} }
Local<Object> obj; Local<Object> obj;
@ -2799,7 +2797,7 @@ void Http2Session::Ping(const FunctionCallbackInfo<Value>& args) {
// the callback will be invoked and a notification sent out to JS land. The // the callback will be invoked and a notification sent out to JS land. The
// notification will include the duration of the ping, allowing the round // notification will include the duration of the ping, allowing the round
// trip to be measured. // trip to be measured.
ping->Send(payload); ping->Send(payload.data());
args.GetReturnValue().Set(true); args.GetReturnValue().Set(true);
} }
@ -2871,7 +2869,7 @@ Http2Session::Http2Ping::Http2Ping(Http2Session* session, Local<Object> obj)
session_(session), session_(session),
startTime_(uv_hrtime()) {} startTime_(uv_hrtime()) {}
void Http2Session::Http2Ping::Send(uint8_t* payload) { void Http2Session::Http2Ping::Send(const uint8_t* payload) {
uint8_t data[8]; uint8_t data[8];
if (payload == nullptr) { if (payload == nullptr) {
memcpy(&data, &startTime_, arraysize(data)); memcpy(&data, &startTime_, arraysize(data));

View File

@ -699,7 +699,8 @@ class Http2Session : public AsyncWrap, public StreamListener {
void Close(uint32_t code = NGHTTP2_NO_ERROR, void Close(uint32_t code = NGHTTP2_NO_ERROR,
bool socket_closed = false); bool socket_closed = false);
void Consume(Local<External> external); void Consume(Local<External> external);
void Goaway(uint32_t code, int32_t lastStreamID, uint8_t* data, size_t len); void Goaway(uint32_t code, int32_t lastStreamID,
const uint8_t* data, size_t len);
void AltSvc(int32_t id, void AltSvc(int32_t id,
uint8_t* origin, uint8_t* origin,
size_t origin_len, size_t origin_len,
@ -1089,7 +1090,7 @@ class Http2Session::Http2Ping : public AsyncWrap {
SET_MEMORY_INFO_NAME(Http2Ping) SET_MEMORY_INFO_NAME(Http2Ping)
SET_SELF_SIZE(Http2Ping) SET_SELF_SIZE(Http2Ping)
void Send(uint8_t* payload); void Send(const uint8_t* payload);
void Done(bool ack, const uint8_t* payload = nullptr); void Done(bool ack, const uint8_t* payload = nullptr);
private: private:

View File

@ -4,6 +4,7 @@
#include "string_decoder-inl.h" #include "string_decoder-inl.h"
using v8::Array; using v8::Array;
using v8::ArrayBufferView;
using v8::Context; using v8::Context;
using v8::FunctionCallbackInfo; using v8::FunctionCallbackInfo;
using v8::Integer; using v8::Integer;
@ -252,9 +253,13 @@ void DecodeData(const FunctionCallbackInfo<Value>& args) {
StringDecoder* decoder = StringDecoder* decoder =
reinterpret_cast<StringDecoder*>(Buffer::Data(args[0])); reinterpret_cast<StringDecoder*>(Buffer::Data(args[0]));
CHECK_NOT_NULL(decoder); CHECK_NOT_NULL(decoder);
size_t nread = Buffer::Length(args[1]);
CHECK(args[1]->IsArrayBufferView());
ArrayBufferViewContents<char> content(args[1].As<ArrayBufferView>());
size_t length = content.length();
MaybeLocal<String> ret = MaybeLocal<String> ret =
decoder->DecodeData(args.GetIsolate(), Buffer::Data(args[1]), &nread); decoder->DecodeData(args.GetIsolate(), content.data(), &length);
if (!ret.IsEmpty()) if (!ret.IsEmpty())
args.GetReturnValue().Set(ret.ToLocalChecked()); args.GetReturnValue().Set(ret.ToLocalChecked());
} }

View File

@ -466,6 +466,32 @@ SlicedArguments::SlicedArguments(
(*this)[i] = args[i + start]; (*this)[i] = args[i + start];
} }
template <typename T, size_t S>
ArrayBufferViewContents<T, S>::ArrayBufferViewContents(
v8::Local<v8::Value> value) {
CHECK(value->IsArrayBufferView());
Read(value.As<v8::ArrayBufferView>());
}
template <typename T, size_t S>
ArrayBufferViewContents<T, S>::ArrayBufferViewContents(
v8::Local<v8::ArrayBufferView> abv) {
Read(abv);
}
template <typename T, size_t S>
void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) {
static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment");
length_ = abv->ByteLength();
if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) {
data_ = static_cast<T*>(abv->Buffer()->GetContents().Data()) +
abv->ByteOffset();
} else {
abv->CopyContents(stack_storage_, sizeof(stack_storage_));
data_ = stack_storage_;
}
}
} // namespace node } // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

View File

@ -29,6 +29,7 @@
namespace node { namespace node {
using v8::ArrayBufferView;
using v8::Isolate; using v8::Isolate;
using v8::Local; using v8::Local;
using v8::String; using v8::String;
@ -89,11 +90,11 @@ BufferValue::BufferValue(Isolate* isolate, Local<Value> value) {
if (value->IsString()) { if (value->IsString()) {
MakeUtf8String(isolate, value, this); MakeUtf8String(isolate, value, this);
} else if (Buffer::HasInstance(value)) { } else if (value->IsArrayBufferView()) {
const size_t len = Buffer::Length(value); const size_t len = value.As<ArrayBufferView>()->ByteLength();
// Leave place for the terminating '\0' byte. // Leave place for the terminating '\0' byte.
AllocateSufficientStorage(len + 1); AllocateSufficientStorage(len + 1);
memcpy(out(), Buffer::Data(value), len); value.As<ArrayBufferView>()->CopyContents(out(), len);
SetLengthAndZeroTerminate(len); SetLengthAndZeroTerminate(len);
} else { } else {
Invalidate(); Invalidate();

View File

@ -417,6 +417,27 @@ class MaybeStackBuffer {
T buf_st_[kStackStorageSize]; T buf_st_[kStackStorageSize];
}; };
// Provides access to an ArrayBufferView's storage, either the original,
// or for small data, a copy of it. This object's lifetime is bound to the
// original ArrayBufferView's lifetime.
template <typename T, size_t kStackStorageSize = 64>
class ArrayBufferViewContents {
public:
ArrayBufferViewContents() {}
explicit inline ArrayBufferViewContents(v8::Local<v8::Value> value);
explicit inline ArrayBufferViewContents(v8::Local<v8::ArrayBufferView> abv);
inline void Read(v8::Local<v8::ArrayBufferView> abv);
inline const T* data() const { return data_; }
inline size_t length() const { return length_; }
private:
T stack_storage_[kStackStorageSize];
T* data_ = nullptr;
size_t length_ = 0;
};
class Utf8Value : public MaybeStackBuffer<char> { class Utf8Value : public MaybeStackBuffer<char> {
public: public:
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value); explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value);