From 3ca6db643e3eab73e948d2ef932d1811f612695f Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Wed, 14 Oct 2020 11:38:51 +0900 Subject: [PATCH 2/2] Switch sha2_openssl.c to use EVP Postgres is two decades late for this switch. --- src/common/sha2_openssl.c | 191 +++++++++++++++++++++++-------- src/tools/pgindent/typedefs.list | 1 + 2 files changed, 143 insertions(+), 49 deletions(-) diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c index 57de96df90..3ccfc3a22e 100644 --- a/src/common/sha2_openssl.c +++ b/src/common/sha2_openssl.c @@ -20,9 +20,13 @@ #include "postgres_fe.h" #endif -#include +#include #include "common/sha2.h" +#ifndef FRONTEND +#include "utils/memutils.h" +#include "utils/resowner.h" +#endif /* * In backend, use palloc/pfree to ease the error handling. In frontend, @@ -36,6 +40,68 @@ #define FREE(ptr) free(ptr) #endif +/* + * As the allocations of the EVP context data happens within the allocator + * used by OpenSSL, use resource owner callbacks to free them on abort with + * a linked list of items saved in TopMemoryContext. + */ +#ifndef FRONTEND +typedef struct EVPContext +{ + EVP_MD_CTX *md_ctx; + + ResourceOwner owner; + struct EVPContext *next; + struct EVPContext *prev; +} EVPContext; + +static EVPContext *open_evp_contexts = NULL; +static bool evp_resowner_callback_registered = false; + +/* + * Utility wrapper to clean up an opened EVP context. + */ +static void +free_evp_context(EVPContext *evp_ctx) +{ + EVP_MD_CTX_destroy(evp_ctx->md_ctx); + if (evp_ctx->prev) + evp_ctx->prev->next = evp_ctx->next; + else + open_evp_contexts = evp_ctx->next; + if (evp_ctx->next) + evp_ctx->next->prev = evp_ctx->prev; + pfree(evp_ctx); +} + +static void +evp_context_free_callback(ResourceReleasePhase phase, + bool isCommit, + bool isTopLevel, + void *arg) +{ + EVPContext *curr; + EVPContext *next; + + if (phase != RESOURCE_RELEASE_AFTER_LOCKS) + return; + + next = open_evp_contexts; + while (next) + { + curr = next; + next = curr->next; + + if (curr->owner == CurrentResourceOwner) + { + if (isCommit) + elog(WARNING, "evp context reference leak: context %p still referenced", curr); + free_evp_context(curr); + } + } +} +#endif + /* * pg_sha2_create * @@ -45,6 +111,9 @@ pg_sha2_ctx * pg_sha2_create(pg_sha2_type type) { pg_sha2_ctx *ctx; +#ifndef FRONTEND + EVPContext *evp_ctx; +#endif ctx = ALLOC(sizeof(pg_sha2_ctx)); if (ctx == NULL) @@ -52,25 +121,47 @@ pg_sha2_create(pg_sha2_type type) ctx->type = type; - switch (type) +#ifndef FRONTEND + + /* + * Keep track of any opened EVP context data allocated in OpenSSL to avoid + * any leaks. The EVP data is built before being assigned to the list, so + * the order is important here. + */ + if (!evp_resowner_callback_registered) { - case PG_SHA224: - case PG_SHA256: - ctx->data = ALLOC(sizeof(SHA256_CTX)); - break; - case PG_SHA384: - case PG_SHA512: - ctx->data = ALLOC(sizeof(SHA512_CTX)); - break; + RegisterResourceReleaseCallback(evp_context_free_callback, NULL); + evp_resowner_callback_registered = true; } + evp_ctx = MemoryContextAlloc(TopMemoryContext, sizeof(EVPContext)); +#endif + + /* + * Initialization takes care of assigning the correct type for OpenSSL. + */ + ctx->data = EVP_MD_CTX_create(); + if (ctx->data == NULL) { +#ifndef FRONTEND + FREE(evp_ctx); + elog(ERROR, "out of memory"); +#else explicit_bzero(ctx, sizeof(pg_sha2_ctx)); FREE(ctx); return NULL; +#endif } +#ifndef FRONTEND + evp_ctx->md_ctx = ctx->data; + evp_ctx->owner = CurrentResourceOwner; + evp_ctx->next = open_evp_contexts; + evp_ctx->prev = NULL; + open_evp_contexts = evp_ctx; +#endif + return ctx; } @@ -80,7 +171,7 @@ pg_sha2_create(pg_sha2_type type) * Initialize a SHA2 context. Returns 0 on success, and -1 on failure. */ int -pg_sha2_init(pg_sha2_ctx *ctx) +pg_sha2_init(pg_sha2_ctx * ctx) { int status = 0; @@ -90,16 +181,20 @@ pg_sha2_init(pg_sha2_ctx *ctx) switch (ctx->type) { case PG_SHA224: - status = SHA224_Init((SHA256_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha224(), NULL); break; case PG_SHA256: - status = SHA256_Init((SHA256_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha256(), NULL); break; case PG_SHA384: - status = SHA384_Init((SHA512_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha384(), NULL); break; case PG_SHA512: - status = SHA512_Init((SHA512_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha512(), NULL); break; } @@ -115,28 +210,14 @@ pg_sha2_init(pg_sha2_ctx *ctx) * Update a SHA2 context. Returns 0 on success, and -1 on failure. */ int -pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len) +pg_sha2_update(pg_sha2_ctx * ctx, const uint8 *data, size_t len) { int status; if (ctx == NULL) return 0; - switch (ctx->type) - { - case PG_SHA224: - status = SHA224_Update((SHA256_CTX *) ctx->data, data, len); - break; - case PG_SHA256: - status = SHA256_Update((SHA256_CTX *) ctx->data, data, len); - break; - case PG_SHA384: - status = SHA384_Update((SHA512_CTX *) ctx->data, data, len); - break; - case PG_SHA512: - status = SHA512_Update((SHA512_CTX *) ctx->data, data, len); - break; - } + status = EVP_DigestUpdate((EVP_MD_CTX *) ctx->data, data, len); /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) @@ -150,28 +231,14 @@ pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len) * Finalize a SHA2 context. Returns 0 on success, and -1 on failure. */ int -pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest) +pg_sha2_final(pg_sha2_ctx * ctx, uint8 *dest) { int status; if (ctx == NULL) return 0; - switch (ctx->type) - { - case PG_SHA224: - status = SHA224_Final(dest, (SHA256_CTX *) ctx->data); - break; - case PG_SHA256: - status = SHA256_Final(dest, (SHA256_CTX *) ctx->data); - break; - case PG_SHA384: - status = SHA384_Final(dest, (SHA512_CTX *) ctx->data); - break; - case PG_SHA512: - status = SHA512_Final(dest, (SHA512_CTX *) ctx->data); - break; - } + status = EVP_DigestFinal_ex((EVP_MD_CTX *) ctx->data, dest, 0); /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) @@ -185,11 +252,37 @@ pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest) * Free a SHA2 context. */ void -pg_sha2_free(pg_sha2_ctx *ctx) +pg_sha2_free(pg_sha2_ctx * ctx) { +#ifndef FRONTEND + EVPContext *curr; + EVPContext *next; +#endif + if (ctx == NULL) return; - FREE(ctx->data); + +#ifndef FRONTEND + + /* + * Look at the list of opened EVP contexts for the one to free. + */ + next = open_evp_contexts; + while (next) + { + curr = next; + next = curr->next; + + if (curr->md_ctx == ctx->data) + { + free_evp_context(curr); + break; + } + } +#else + EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx->data); +#endif + explicit_bzero(ctx, sizeof(pg_sha2_ctx)); FREE(ctx); } diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index c52f20d4ba..5773aaa1f9 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -569,6 +569,7 @@ EVP_CIPHER_CTX EVP_MD EVP_MD_CTX EVP_PKEY +EVPContext EachState Edge EditableObjectType -- 2.28.0