*/
#include "postgres.h"
+#include "common/cryptohash.h"
#include "common/hashfn.h"
#include "jit/jit.h"
#include "storage/bufmgr.h"
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
+ ResourceArray cryptohasharr; /* cryptohash contexts */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);
static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintCryptoHashLeakWarning(Datum handle);
/*****************************************************************************
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+ ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL));
return owner;
}
jit_release_context(context);
}
+
+ /* Ditto for cryptohash contexts */
+ while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
+ {
+ pg_cryptohash_ctx *context =
+ (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+
+ if (isCommit)
+ PrintCryptoHashLeakWarning(foundres);
+ pg_cryptohash_free(context);
+ }
}
else if (phase == RESOURCE_RELEASE_LOCKS)
{
Assert(owner->filearr.nitems == 0);
Assert(owner->dsmarr.nitems == 0);
Assert(owner->jitarr.nitems == 0);
+ Assert(owner->cryptohasharr.nitems == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
/*
ResourceArrayFree(&(owner->filearr));
ResourceArrayFree(&(owner->dsmarr));
ResourceArrayFree(&(owner->jitarr));
+ ResourceArrayFree(&(owner->cryptohasharr));
pfree(owner);
}
elog(ERROR, "JIT context %p is not owned by resource owner %s",
DatumGetPointer(handle), owner->name);
}
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * cryptohash context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
+{
+ ResourceArrayEnlarge(&(owner->cryptohasharr));
+}
+
+/*
+ * Remember that a cryptohash context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeCryptoHash()
+ */
+void
+ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
+{
+ ResourceArrayAdd(&(owner->cryptohasharr), handle);
+}
+
+/*
+ * Forget that a cryptohash context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
+{
+ if (!ResourceArrayRemove(&(owner->cryptohasharr), handle))
+ elog(ERROR, "cryptohash context %p is not owned by resource owner %s",
+ DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintCryptoHashLeakWarning(Datum handle)
+{
+ elog(WARNING, "cryptohash context reference leak: context %p still referenced",
+ DatumGetPointer(handle));
+}
#include "postgres_fe.h"
#endif
-#include <openssl/sha.h>
+#include <openssl/evp.h>
#include "common/cryptohash.h"
+#ifndef FRONTEND
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/resowner_private.h"
+#endif
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
#define FREE(ptr) free(ptr)
#endif
+/*
+ * Internal structure for pg_cryptohash_ctx->data.
+ *
+ * This tracks the resource owner associated to each EVP context data
+ * for the backend.
+ */
+typedef struct pg_cryptohash_state
+{
+ EVP_MD_CTX *evpctx;
+
+#ifndef FRONTEND
+ ResourceOwner resowner;
+#endif
+} pg_cryptohash_state;
+
/*
* pg_cryptohash_create
*
pg_cryptohash_create(pg_cryptohash_type type)
{
pg_cryptohash_ctx *ctx;
+ pg_cryptohash_state *state;
ctx = ALLOC(sizeof(pg_cryptohash_ctx));
if (ctx == NULL)
return NULL;
- ctx->type = type;
-
- switch (type)
+ state = ALLOC(sizeof(pg_cryptohash_state));
+ if (state == NULL)
{
- 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;
+ explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+ FREE(ctx);
+ return NULL;
}
- if (ctx->data == NULL)
+ ctx->data = state;
+ ctx->type = type;
+
+#ifndef FRONTEND
+ ResourceOwnerEnlargeCryptoHash(CurrentResourceOwner);
+#endif
+
+ /*
+ * Initialization takes care of assigning the correct type for OpenSSL.
+ */
+ state->evpctx = EVP_MD_CTX_create();
+
+ if (state->evpctx == NULL)
{
+ explicit_bzero(state, sizeof(pg_cryptohash_state));
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+#ifndef FRONTEND
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory")));
+#else
+ FREE(state);
FREE(ctx);
return NULL;
+#endif
}
+#ifndef FRONTEND
+ state->resowner = CurrentResourceOwner;
+ ResourceOwnerRememberCryptoHash(CurrentResourceOwner,
+ PointerGetDatum(ctx));
+#endif
+
return ctx;
}
pg_cryptohash_init(pg_cryptohash_ctx *ctx)
{
int status = 0;
+ pg_cryptohash_state *state;
if (ctx == NULL)
return 0;
+ state = (pg_cryptohash_state *) ctx->data;
+
switch (ctx->type)
{
case PG_SHA224:
- status = SHA224_Init((SHA256_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha224(), NULL);
break;
case PG_SHA256:
- status = SHA256_Init((SHA256_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha256(), NULL);
break;
case PG_SHA384:
- status = SHA384_Init((SHA512_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha384(), NULL);
break;
case PG_SHA512:
- status = SHA512_Init((SHA512_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha512(), NULL);
break;
}
pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
{
int status = 0;
+ pg_cryptohash_state *state;
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;
- }
+ state = (pg_cryptohash_state *) ctx->data;
+ status = EVP_DigestUpdate(state->evpctx, data, len);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
{
int status = 0;
+ pg_cryptohash_state *state;
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;
- }
+ state = (pg_cryptohash_state *) ctx->data;
+ status = EVP_DigestFinal_ex(state->evpctx, dest, 0);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
void
pg_cryptohash_free(pg_cryptohash_ctx *ctx)
{
+ pg_cryptohash_state *state;
+
if (ctx == NULL)
return;
- FREE(ctx->data);
+
+ state = (pg_cryptohash_state *) ctx->data;
+ EVP_MD_CTX_destroy(state->evpctx);
+
+#ifndef FRONTEND
+ ResourceOwnerForgetCryptoHash(state->resowner,
+ PointerGetDatum(ctx));
+#endif
+
+ explicit_bzero(state, sizeof(pg_cryptohash_state));
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+ FREE(state);
FREE(ctx);
}