-
Notifications
You must be signed in to change notification settings - Fork 30.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
src: more automatic memory management in node_crypto.cc #20238
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Anna, this looks great! I will have a closer look soon.
src/node_crypto.cc
Outdated
@@ -586,24 +595,26 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, | |||
// by SSL_CTX_use_certificate). | |||
|
|||
// Find issuer | |||
if (*issuer != nullptr || X509_check_issued(ca, x) != X509_V_OK) | |||
if (!*issuer || X509_check_issued(ca, x.get()) != X509_V_OK) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something here, but doesn't this negate the check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tniessen Yes, good catch. I don’t think we have tests for this at the moment. :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments, didn't review fully. I like the overall direction.
int iter, | ||
int keylen) | ||
MallocedBuffer<char>&& pass, | ||
MallocedBuffer<char>&& salt, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coincidence: I was working yesterday on simplifying this to something that uses std::vector<char>
.
(Working on adding scrypt support and DRYing the PBKDF2 and RandomBytes code in the process.)
src/node_crypto.h
Outdated
@@ -73,6 +73,32 @@ struct MarkPopErrorOnReturn { | |||
~MarkPopErrorOnReturn() { ERR_pop_to_mark(); } | |||
}; | |||
|
|||
template<typename T, void (*function)(T*)> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: space before <
.
src/node_crypto.h
Outdated
template<typename T, void (*function)(T*)> | ||
struct FunctionDeleter { | ||
void operator()(T* pointer) const { function(pointer); } | ||
typedef std::unique_ptr<T, FunctionDeleter> ptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you name this Ptr
, Pointer
, UniquePointer
or whatever to make it clearer it's a type?
src/node_crypto.cc
Outdated
return ret; | ||
DeleteFnPtr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx( | ||
X509_STORE_CTX_new()); | ||
return store_ctx != nullptr && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose this works because of boolean coercion but it might be clearer to write it as !!store_ctx
or store_ctx == true
or store_ctx.get() != nullptr
.
Fixed tests (not sure why I didn’t see the failures before) and addressed @bnoordhuis’ first round of review nits. |
src/node_crypto.h
Outdated
Error Init(const char* sign_type); | ||
Error Update(const char* data, int len); | ||
|
||
protected: | ||
void CheckThrow(Error error); | ||
|
||
EVP_MD_CTX* mdctx_; | ||
DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free> mdctx_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason not to reuse EVPMDPointer
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tniessen thanks for catching, updated!
CI: https://ci.nodejs.org/job/node-test-commit/18082/ Would anybody in particular like me to wait with landing until they have time to review? I know it’s a large commit, and not urgent, so I really don’t want to push this to land early. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rubber stamp LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong button..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a rebase (I'm guessing it conflicts with the EVP_CTRL_AEAD_SET_IVLEN
change) but LGTM modulo comments. This PR makes the crypto code a lot nicer!
src/node_crypto.cc
Outdated
@@ -1125,6 +1110,9 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) { | |||
|
|||
|
|||
#ifndef OPENSSL_NO_ENGINE | |||
// Helper for the smart pointer. | |||
void ENGINE_free_fn(ENGINE* engine) { ENGINE_free_fn(engine); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/ENGINE_free_fn/ENGINE_free/
in the function body.
src/node_crypto.cc
Outdated
const unsigned char* p = reinterpret_cast<const unsigned char*>(sbuf); | ||
SSL_SESSION* sess = d2i_SSL_SESSION(nullptr, &p, slen); | ||
std::vector<char> sbuf(slen); | ||
memcpy(sbuf.data(), Buffer::Data(args[0]), slen); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a suggestion since this isn't a functional change but it's correcter to use assignment here, like this:
std::vector<char> sbuf;
if (char* p = Buffer::Data(args[0])) sbuf.assign(p, p + slen);
The reason is that Buffer::Data()
can return nullptr when slen == 0
and passing a nullptr to memcpy() is technically UB, even when the pointer isn't dereferenced.
src/node_crypto.cc
Outdated
ctx_ = nullptr; | ||
ctx_.reset(HMAC_CTX_new()); | ||
if (!ctx_ || | ||
!HMAC_Init_ex(ctx_.get(), key, key_len, md, nullptr)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fits on one line now. (Likewise on lines 3289 and 3378, I think.)
src/node_crypto.cc
Outdated
Buffer::Length(args[0]), | ||
0)); | ||
|
||
int dataSize = DH_size(diffieHellman->dh_.get()); | ||
char* data = Malloc(dataSize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use automatic memory management for this as well (and rename dataSize
to data_size
while you're here)? Just a suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea – I’ve switched this to MallocedBuffer
, too (that takes care of dataSize
anyway)
src/node_crypto.cc
Outdated
int r; | ||
|
||
pub = EC_POINT_new(group); | ||
ECPointPointer pub(EC_POINT_new(group)); | ||
if (pub == nullptr) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!pub
, also on line 4366.
src/node_crypto.cc
Outdated
@@ -5320,33 +5136,32 @@ void ConvertKey(const FunctionCallbackInfo<Value>& args) { | |||
if (nid == NID_undef) | |||
return env->ThrowTypeError("Invalid ECDH curve name"); | |||
|
|||
EC_GROUP* group = EC_GROUP_new_by_curve_name(nid); | |||
ECGroupPointer group( | |||
EC_GROUP_new_by_curve_name(nid)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4 space indent.
src/util.h
Outdated
} | ||
MallocedBuffer& operator=(MallocedBuffer&& other) { | ||
this->~MallocedBuffer(); | ||
return *new (this)MallocedBuffer(other); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: new(this)
Don't know if it matters here but use of placement new sometimes inhibits compiler optimizations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know if it matters here but use of placement new sometimes inhibits compiler optimizations.
I don’t think we actually use it at this point, so this is just here for completeness
MallocedBuffer& operator=(const MallocedBuffer&) = delete; | ||
}; | ||
|
||
} // namespace node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this class necessary? A std::unique_ptr
with a custom deleter would be sufficient too, wouldn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnoordhuis Yes, the difference being that this also tracks the length of the buffer by itself. If you feel strongly about it I can switch to splitting up into two variables again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No strong feelings.
Rerunning Windows: https://ci.nodejs.org/job/node-test-commit-windows-fanned/17684/ |
Rebased CI before landing: https://ci.nodejs.org/job/node-test-commit/18218/ |
Another day, another merge conflict, another rebase. :) CI: https://ci.nodejs.org/job/node-test-commit/18232/ |
Prefer custom smart pointers fitted to the OpenSSL data structures over more manual memory management and lots of `goto`s. PR-URL: nodejs#20238 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
Prefer custom smart pointers fitted to the OpenSSL data structures over more manual memory management and lots of `goto`s. Backport-PR-URL: #20609 PR-URL: #20238 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
Prefer custom smart pointers fitted to the OpenSSL data structures over more manual memory management and lots of `goto`s. PR-URL: nodejs#20238 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
Prefer custom smart pointers fitted to the OpenSSL data structures over more manual memory management and lots of `goto`s. Backport-PR-URL: #20706 PR-URL: #20238 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
Prefer custom smart pointers fitted to the OpenSSL data structures
over more manual memory management and lots of
goto
s.Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes