Skip to content

Commit 1af064b

Browse files
bnoordhuisaddaleax
authored andcommitted
src: don't heap allocate GCM cipher auth tag
Fix a memory leak by removing the heap allocation altogether. Fixes: #13917 PR-URL: #14122 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 174f8c8 commit 1af064b

File tree

2 files changed

+31
-51
lines changed

2 files changed

+31
-51
lines changed

src/node_crypto.cc

+30-46
Original file line numberDiff line numberDiff line change
@@ -3462,42 +3462,22 @@ bool CipherBase::IsAuthenticatedMode() const {
34623462
}
34633463

34643464

3465-
bool CipherBase::GetAuthTag(char** out, unsigned int* out_len) const {
3466-
// only callable after Final and if encrypting.
3467-
if (initialised_ || kind_ != kCipher || !auth_tag_)
3468-
return false;
3469-
*out_len = auth_tag_len_;
3470-
*out = node::Malloc(auth_tag_len_);
3471-
memcpy(*out, auth_tag_, auth_tag_len_);
3472-
return true;
3473-
}
3474-
3475-
34763465
void CipherBase::GetAuthTag(const FunctionCallbackInfo<Value>& args) {
34773466
Environment* env = Environment::GetCurrent(args);
34783467
CipherBase* cipher;
34793468
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
34803469

3481-
char* out = nullptr;
3482-
unsigned int out_len = 0;
3483-
3484-
if (cipher->GetAuthTag(&out, &out_len)) {
3485-
Local<Object> buf = Buffer::New(env, out, out_len).ToLocalChecked();
3486-
args.GetReturnValue().Set(buf);
3487-
} else {
3488-
env->ThrowError("Attempting to get auth tag in unsupported state");
3470+
// Only callable after Final and if encrypting.
3471+
if (cipher->initialised_ ||
3472+
cipher->kind_ != kCipher ||
3473+
cipher->auth_tag_len_ == 0) {
3474+
return env->ThrowError("Attempting to get auth tag in unsupported state");
34893475
}
3490-
}
3491-
34923476

3493-
bool CipherBase::SetAuthTag(const char* data, unsigned int len) {
3494-
if (!initialised_ || !IsAuthenticatedMode() || kind_ != kDecipher)
3495-
return false;
3496-
delete[] auth_tag_;
3497-
auth_tag_len_ = len;
3498-
auth_tag_ = new char[len];
3499-
memcpy(auth_tag_, data, len);
3500-
return true;
3477+
Local<Object> buf =
3478+
Buffer::Copy(env, cipher->auth_tag_, cipher->auth_tag_len_)
3479+
.ToLocalChecked();
3480+
args.GetReturnValue().Set(buf);
35013481
}
35023482

35033483

@@ -3509,8 +3489,20 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
35093489
CipherBase* cipher;
35103490
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
35113491

3512-
if (!cipher->SetAuthTag(Buffer::Data(args[0]), Buffer::Length(args[0])))
3513-
env->ThrowError("Attempting to set auth tag in unsupported state");
3492+
if (!cipher->initialised_ ||
3493+
!cipher->IsAuthenticatedMode() ||
3494+
cipher->kind_ != kDecipher) {
3495+
return env->ThrowError("Attempting to set auth tag in unsupported state");
3496+
}
3497+
3498+
// FIXME(bnoordhuis) Throw when buffer length is not a valid tag size.
3499+
// Note: we don't use std::max() here to work around a header conflict.
3500+
cipher->auth_tag_len_ = Buffer::Length(args[0]);
3501+
if (cipher->auth_tag_len_ > sizeof(cipher->auth_tag_))
3502+
cipher->auth_tag_len_ = sizeof(cipher->auth_tag_);
3503+
3504+
memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_));
3505+
memcpy(cipher->auth_tag_, Buffer::Data(args[0]), cipher->auth_tag_len_);
35143506
}
35153507

35163508

@@ -3550,13 +3542,12 @@ bool CipherBase::Update(const char* data,
35503542
return 0;
35513543

35523544
// on first update:
3553-
if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_ != nullptr) {
3545+
if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_len_ > 0) {
35543546
EVP_CIPHER_CTX_ctrl(&ctx_,
35553547
EVP_CTRL_GCM_SET_TAG,
35563548
auth_tag_len_,
35573549
reinterpret_cast<unsigned char*>(auth_tag_));
3558-
delete[] auth_tag_;
3559-
auth_tag_ = nullptr;
3550+
auth_tag_len_ = 0;
35603551
}
35613552

35623553
*out_len = len + EVP_CIPHER_CTX_block_size(&ctx_);
@@ -3634,18 +3625,11 @@ bool CipherBase::Final(unsigned char** out, int *out_len) {
36343625
static_cast<size_t>(EVP_CIPHER_CTX_block_size(&ctx_)));
36353626
int r = EVP_CipherFinal_ex(&ctx_, *out, out_len);
36363627

3637-
if (r && kind_ == kCipher) {
3638-
delete[] auth_tag_;
3639-
auth_tag_ = nullptr;
3640-
if (IsAuthenticatedMode()) {
3641-
auth_tag_len_ = EVP_GCM_TLS_TAG_LEN; // use default tag length
3642-
auth_tag_ = new char[auth_tag_len_];
3643-
memset(auth_tag_, 0, auth_tag_len_);
3644-
EVP_CIPHER_CTX_ctrl(&ctx_,
3645-
EVP_CTRL_GCM_GET_TAG,
3646-
auth_tag_len_,
3647-
reinterpret_cast<unsigned char*>(auth_tag_));
3648-
}
3628+
if (r == 1 && kind_ == kCipher && IsAuthenticatedMode()) {
3629+
auth_tag_len_ = sizeof(auth_tag_);
3630+
r = EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_GET_TAG, auth_tag_len_,
3631+
reinterpret_cast<unsigned char*>(auth_tag_));
3632+
CHECK_EQ(r, 1);
36493633
}
36503634

36513635
EVP_CIPHER_CTX_cleanup(&ctx_);

src/node_crypto.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,6 @@ class CipherBase : public BaseObject {
428428
~CipherBase() override {
429429
if (!initialised_)
430430
return;
431-
delete[] auth_tag_;
432431
EVP_CIPHER_CTX_cleanup(&ctx_);
433432
}
434433

@@ -451,8 +450,6 @@ class CipherBase : public BaseObject {
451450
bool SetAutoPadding(bool auto_padding);
452451

453452
bool IsAuthenticatedMode() const;
454-
bool GetAuthTag(char** out, unsigned int* out_len) const;
455-
bool SetAuthTag(const char* data, unsigned int len);
456453
bool SetAAD(const char* data, unsigned int len);
457454

458455
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -473,7 +470,6 @@ class CipherBase : public BaseObject {
473470
cipher_(nullptr),
474471
initialised_(false),
475472
kind_(kind),
476-
auth_tag_(nullptr),
477473
auth_tag_len_(0) {
478474
MakeWeak<CipherBase>(this);
479475
}
@@ -483,8 +479,8 @@ class CipherBase : public BaseObject {
483479
const EVP_CIPHER* cipher_; /* coverity[member_decl] */
484480
bool initialised_;
485481
CipherKind kind_;
486-
char* auth_tag_;
487482
unsigned int auth_tag_len_;
483+
char auth_tag_[EVP_GCM_TLS_TAG_LEN];
488484
};
489485

490486
class Hmac : public BaseObject {

0 commit comments

Comments
 (0)