The sequence of operations in the attached proof of concept will trigger a double free. This happens because: 214 if (ctx->digest != type) { 215 if (ctx->digest && ctx->digest->ctx_size) 216 OPENSSL_free(ctx->md_data); 217 ctx->digest = type; 218 if (!(ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) && type->ctx_size) { 219 ctx->update = type->update; 220 ctx->md_data = OPENSSL_malloc(type->ctx_size); 221 if (ctx->md_data == NULL) { 222 EVPerr(EVP_F_EVP_DIGESTINIT_EX, ERR_R_MALLOC_FAILURE); 223 return 0; 224 } 225 } 226 } Under particular circumstances ctx->md_data is freed but not set again, and a subsequent call will free ctx->md_data again. I'm not sure if the PoC represents something that could happen in a real application, or whether other, saner paths towards this double free (or perhaps use after free) exist. Either way, I advice setting ctx->md_data to NULL after freeing it.
PoC:
#include <openssl/evp.h> #include <openssl/bio.h> #include <stdio.h> int main(void) { EVP_MD_CTX *mctx = NULL; EVP_PKEY_CTX *pctx = NULL; BIO *bmd = NULL; EVP_PKEY *sigkey = NULL; int errid = 0; int r; OpenSSL_add_all_digests(); bmd = BIO_new(BIO_f_md()); if ( bmd == 0 ) { errid = 1; goto err; } if (!BIO_get_md_ctx(bmd, &mctx)) { errid = 2; goto err; } if ( !EVP_DigestInit_ex(mctx, EVP_md5(), 0) ) { errid = 3; goto err; } sigkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, 0, (unsigned char*)"aaaa", -1); if ( !sigkey ) { errid = 4; goto err; } r = EVP_DigestSignInit(mctx, &pctx, 0, 0, sigkey); if (!r) { errid = 5; goto err; } if (!EVP_DigestInit_ex(mctx, EVP_md5(), 0)) { errid = 6; goto err; } return 0; err: printf("Error: %d\n", errid); return 1; return 0; }
OpenSSL responded with:
“I do not believe this scenario would ever normally occur, i.e. only as a result of programmer error (you should not be mixing the EVP_DigestInit/EVP_DigestSignInit calls in that way). Therefore I will not be treating this as a security issue.
Nonetheless it seems prudent to set ctx->md_data to NULL after the free,
so I will apply a patch to that effect.
“