Change pgcrypto to use the new ResourceOwner mechanism.
This is a nice example of how extensions can now use ResourceOwners to track extension-specific resource kinds Reviewed-by: Peter Eisentraut, Andres Freund Discussion: https://www.postgresql.org/message-id/d746cead-a1ef-7efe-fb47-933311e876a3%40iki.fi
This commit is contained in:
parent
954e43564d
commit
cd694f60dc
@ -50,9 +50,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest
|
* To make sure we don't leak OpenSSL handles, we use the ResourceOwner
|
||||||
* objects in a linked list, allocated in TopMemoryContext. We use the
|
* mechanism to free them on abort.
|
||||||
* ResourceOwner mechanism to free them on abort.
|
|
||||||
*/
|
*/
|
||||||
typedef struct OSSLDigest
|
typedef struct OSSLDigest
|
||||||
{
|
{
|
||||||
@ -60,56 +59,41 @@ typedef struct OSSLDigest
|
|||||||
EVP_MD_CTX *ctx;
|
EVP_MD_CTX *ctx;
|
||||||
|
|
||||||
ResourceOwner owner;
|
ResourceOwner owner;
|
||||||
struct OSSLDigest *next;
|
|
||||||
struct OSSLDigest *prev;
|
|
||||||
} OSSLDigest;
|
} OSSLDigest;
|
||||||
|
|
||||||
static OSSLDigest *open_digests = NULL;
|
/* ResourceOwner callbacks to hold OpenSSL digest handles */
|
||||||
static bool digest_resowner_callback_registered = false;
|
static void ResOwnerReleaseOSSLDigest(Datum res);
|
||||||
|
|
||||||
|
static const ResourceOwnerDesc ossldigest_resowner_desc =
|
||||||
|
{
|
||||||
|
.name = "pgcrypto OpenSSL digest handle",
|
||||||
|
.release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
|
||||||
|
.release_priority = RELEASE_PRIO_FIRST,
|
||||||
|
.ReleaseResource = ResOwnerReleaseOSSLDigest,
|
||||||
|
.DebugPrint = NULL, /* default message is fine */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Convenience wrappers over ResourceOwnerRemember/Forget */
|
||||||
|
static inline void
|
||||||
|
ResourceOwnerRememberOSSLDigest(ResourceOwner owner, OSSLDigest *digest)
|
||||||
|
{
|
||||||
|
ResourceOwnerRemember(owner, PointerGetDatum(digest), &ossldigest_resowner_desc);
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
ResourceOwnerForgetOSSLDigest(ResourceOwner owner, OSSLDigest *digest)
|
||||||
|
{
|
||||||
|
ResourceOwnerForget(owner, PointerGetDatum(digest), &ossldigest_resowner_desc);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
free_openssl_digest(OSSLDigest *digest)
|
free_openssl_digest(OSSLDigest *digest)
|
||||||
{
|
{
|
||||||
EVP_MD_CTX_destroy(digest->ctx);
|
EVP_MD_CTX_destroy(digest->ctx);
|
||||||
if (digest->prev)
|
if (digest->owner != NULL)
|
||||||
digest->prev->next = digest->next;
|
ResourceOwnerForgetOSSLDigest(digest->owner, digest);
|
||||||
else
|
|
||||||
open_digests = digest->next;
|
|
||||||
if (digest->next)
|
|
||||||
digest->next->prev = digest->prev;
|
|
||||||
pfree(digest);
|
pfree(digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Close any open OpenSSL handles on abort.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
digest_free_callback(ResourceReleasePhase phase,
|
|
||||||
bool isCommit,
|
|
||||||
bool isTopLevel,
|
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
OSSLDigest *curr;
|
|
||||||
OSSLDigest *next;
|
|
||||||
|
|
||||||
if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
|
|
||||||
return;
|
|
||||||
|
|
||||||
next = open_digests;
|
|
||||||
while (next)
|
|
||||||
{
|
|
||||||
curr = next;
|
|
||||||
next = curr->next;
|
|
||||||
|
|
||||||
if (curr->owner == CurrentResourceOwner)
|
|
||||||
{
|
|
||||||
if (isCommit)
|
|
||||||
elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr);
|
|
||||||
free_openssl_digest(curr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned
|
static unsigned
|
||||||
digest_result_size(PX_MD *h)
|
digest_result_size(PX_MD *h)
|
||||||
{
|
{
|
||||||
@ -188,16 +172,12 @@ px_find_digest(const char *name, PX_MD **res)
|
|||||||
OpenSSL_add_all_algorithms();
|
OpenSSL_add_all_algorithms();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!digest_resowner_callback_registered)
|
|
||||||
{
|
|
||||||
RegisterResourceReleaseCallback(digest_free_callback, NULL);
|
|
||||||
digest_resowner_callback_registered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
md = EVP_get_digestbyname(name);
|
md = EVP_get_digestbyname(name);
|
||||||
if (md == NULL)
|
if (md == NULL)
|
||||||
return PXE_NO_HASH;
|
return PXE_NO_HASH;
|
||||||
|
|
||||||
|
ResourceOwnerEnlarge(CurrentResourceOwner);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
|
* Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
|
||||||
* The order is crucial, to make sure we don't leak anything on
|
* The order is crucial, to make sure we don't leak anything on
|
||||||
@ -221,9 +201,7 @@ px_find_digest(const char *name, PX_MD **res)
|
|||||||
digest->algo = md;
|
digest->algo = md;
|
||||||
digest->ctx = ctx;
|
digest->ctx = ctx;
|
||||||
digest->owner = CurrentResourceOwner;
|
digest->owner = CurrentResourceOwner;
|
||||||
digest->next = open_digests;
|
ResourceOwnerRememberOSSLDigest(digest->owner, digest);
|
||||||
digest->prev = NULL;
|
|
||||||
open_digests = digest;
|
|
||||||
|
|
||||||
/* The PX_MD object is allocated in the current memory context. */
|
/* The PX_MD object is allocated in the current memory context. */
|
||||||
h = palloc(sizeof(*h));
|
h = palloc(sizeof(*h));
|
||||||
@ -239,6 +217,17 @@ px_find_digest(const char *name, PX_MD **res)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ResourceOwner callbacks for OSSLDigest */
|
||||||
|
|
||||||
|
static void
|
||||||
|
ResOwnerReleaseOSSLDigest(Datum res)
|
||||||
|
{
|
||||||
|
OSSLDigest *digest = (OSSLDigest *) DatumGetPointer(res);
|
||||||
|
|
||||||
|
digest->owner = NULL;
|
||||||
|
free_openssl_digest(digest);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ciphers
|
* Ciphers
|
||||||
*
|
*
|
||||||
@ -266,9 +255,8 @@ struct ossl_cipher
|
|||||||
* OSSLCipher contains the state for using a cipher. A separate OSSLCipher
|
* OSSLCipher contains the state for using a cipher. A separate OSSLCipher
|
||||||
* object is allocated in each px_find_cipher() call.
|
* object is allocated in each px_find_cipher() call.
|
||||||
*
|
*
|
||||||
* To make sure we don't leak OpenSSL handles on abort, we keep OSSLCipher
|
* To make sure we don't leak OpenSSL handles, we use the ResourceOwner
|
||||||
* objects in a linked list, allocated in TopMemoryContext. We use the
|
* mechanism to free them on abort.
|
||||||
* ResourceOwner mechanism to free them on abort.
|
|
||||||
*/
|
*/
|
||||||
typedef struct OSSLCipher
|
typedef struct OSSLCipher
|
||||||
{
|
{
|
||||||
@ -281,56 +269,41 @@ typedef struct OSSLCipher
|
|||||||
const struct ossl_cipher *ciph;
|
const struct ossl_cipher *ciph;
|
||||||
|
|
||||||
ResourceOwner owner;
|
ResourceOwner owner;
|
||||||
struct OSSLCipher *next;
|
|
||||||
struct OSSLCipher *prev;
|
|
||||||
} OSSLCipher;
|
} OSSLCipher;
|
||||||
|
|
||||||
static OSSLCipher *open_ciphers = NULL;
|
/* ResourceOwner callbacks to hold OpenSSL cipher state */
|
||||||
static bool cipher_resowner_callback_registered = false;
|
static void ResOwnerReleaseOSSLCipher(Datum res);
|
||||||
|
|
||||||
|
static const ResourceOwnerDesc osslcipher_resowner_desc =
|
||||||
|
{
|
||||||
|
.name = "pgcrypto OpenSSL cipher handle",
|
||||||
|
.release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
|
||||||
|
.release_priority = RELEASE_PRIO_FIRST,
|
||||||
|
.ReleaseResource = ResOwnerReleaseOSSLCipher,
|
||||||
|
.DebugPrint = NULL, /* default message is fine */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Convenience wrappers over ResourceOwnerRemember/Forget */
|
||||||
|
static inline void
|
||||||
|
ResourceOwnerRememberOSSLCipher(ResourceOwner owner, OSSLCipher *od)
|
||||||
|
{
|
||||||
|
ResourceOwnerRemember(owner, PointerGetDatum(od), &osslcipher_resowner_desc);
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
ResourceOwnerForgetOSSLCipher(ResourceOwner owner, OSSLCipher *od)
|
||||||
|
{
|
||||||
|
ResourceOwnerForget(owner, PointerGetDatum(od), &osslcipher_resowner_desc);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
free_openssl_cipher(OSSLCipher *od)
|
free_openssl_cipher(OSSLCipher *od)
|
||||||
{
|
{
|
||||||
EVP_CIPHER_CTX_free(od->evp_ctx);
|
EVP_CIPHER_CTX_free(od->evp_ctx);
|
||||||
if (od->prev)
|
if (od->owner != NULL)
|
||||||
od->prev->next = od->next;
|
ResourceOwnerForgetOSSLCipher(od->owner, od);
|
||||||
else
|
|
||||||
open_ciphers = od->next;
|
|
||||||
if (od->next)
|
|
||||||
od->next->prev = od->prev;
|
|
||||||
pfree(od);
|
pfree(od);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Close any open OpenSSL cipher handles on abort.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
cipher_free_callback(ResourceReleasePhase phase,
|
|
||||||
bool isCommit,
|
|
||||||
bool isTopLevel,
|
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
OSSLCipher *curr;
|
|
||||||
OSSLCipher *next;
|
|
||||||
|
|
||||||
if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
|
|
||||||
return;
|
|
||||||
|
|
||||||
next = open_ciphers;
|
|
||||||
while (next)
|
|
||||||
{
|
|
||||||
curr = next;
|
|
||||||
next = curr->next;
|
|
||||||
|
|
||||||
if (curr->owner == CurrentResourceOwner)
|
|
||||||
{
|
|
||||||
if (isCommit)
|
|
||||||
elog(WARNING, "pgcrypto cipher reference leak: cipher %p still referenced", curr);
|
|
||||||
free_openssl_cipher(curr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Common routines for all algorithms */
|
/* Common routines for all algorithms */
|
||||||
|
|
||||||
static unsigned
|
static unsigned
|
||||||
@ -782,11 +755,7 @@ px_find_cipher(const char *name, PX_Cipher **res)
|
|||||||
if (i->name == NULL)
|
if (i->name == NULL)
|
||||||
return PXE_NO_CIPHER;
|
return PXE_NO_CIPHER;
|
||||||
|
|
||||||
if (!cipher_resowner_callback_registered)
|
ResourceOwnerEnlarge(CurrentResourceOwner);
|
||||||
{
|
|
||||||
RegisterResourceReleaseCallback(cipher_free_callback, NULL);
|
|
||||||
cipher_resowner_callback_registered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create an OSSLCipher object, an EVP_CIPHER_CTX object and a PX_Cipher.
|
* Create an OSSLCipher object, an EVP_CIPHER_CTX object and a PX_Cipher.
|
||||||
@ -806,9 +775,7 @@ px_find_cipher(const char *name, PX_Cipher **res)
|
|||||||
|
|
||||||
od->evp_ctx = ctx;
|
od->evp_ctx = ctx;
|
||||||
od->owner = CurrentResourceOwner;
|
od->owner = CurrentResourceOwner;
|
||||||
od->next = open_ciphers;
|
ResourceOwnerRememberOSSLCipher(od->owner, od);
|
||||||
od->prev = NULL;
|
|
||||||
open_ciphers = od;
|
|
||||||
|
|
||||||
if (i->ciph->cipher_func)
|
if (i->ciph->cipher_func)
|
||||||
od->evp_ciph = i->ciph->cipher_func();
|
od->evp_ciph = i->ciph->cipher_func();
|
||||||
@ -827,3 +794,11 @@ px_find_cipher(const char *name, PX_Cipher **res)
|
|||||||
*res = c;
|
*res = c;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ResourceOwner callbacks for OSSLCipher */
|
||||||
|
|
||||||
|
static void
|
||||||
|
ResOwnerReleaseOSSLCipher(Datum res)
|
||||||
|
{
|
||||||
|
free_openssl_cipher((OSSLCipher *) DatumGetPointer(res));
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user