-#include "cryptlib.h"
-
-int CRYPTO_get_ex_new_index(int idx, STACK **skp, long argl, char *argp,
- int (*new_func)(), int (*dup_func)(), void (*free_func)())
- {
- int ret= -1;
- CRYPTO_EX_DATA_FUNCS *a;
-
- MemCheck_off();
- if (*skp == NULL)
- *skp=sk_new_null();
- if (*skp == NULL)
- {
- CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX,ERR_R_MALLOC_FAILURE);
- goto err;
- }
- a=(CRYPTO_EX_DATA_FUNCS *)Malloc(sizeof(CRYPTO_EX_DATA_FUNCS));
- if (a == NULL)
- {
- CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX,ERR_R_MALLOC_FAILURE);
- goto err;
- }
- a->argl=argl;
- a->argp=argp;
- a->new_func=new_func;
- a->dup_func=dup_func;
- a->free_func=free_func;
- while (sk_num(*skp) <= idx)
- {
- if (!sk_push(*skp,NULL))
- {
- CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX,ERR_R_MALLOC_FAILURE);
- Free(a);
- goto err;
- }
- }
- sk_set(*skp,idx, (char *)a);
- ret=idx;
+
+/*
+ * Each structure type (sometimes called a class), that supports
+ * exdata has a stack of callbacks for each instance.
+ */
+struct ex_callback_st {
+ long argl; /* Arbitrary long */
+ void *argp; /* Arbitrary void * */
+ CRYPTO_EX_new *new_func;
+ CRYPTO_EX_free *free_func;
+ CRYPTO_EX_dup *dup_func;
+};
+
+/*
+ * The state for each class. This could just be a typedef, but
+ * a structure allows future changes.
+ */
+typedef struct ex_callbacks_st {
+ STACK_OF(EX_CALLBACK) *meth;
+} EX_CALLBACKS;
+
+static EX_CALLBACKS ex_data[CRYPTO_EX_INDEX__COUNT];
+
+static CRYPTO_RWLOCK *ex_data_lock = NULL;
+static CRYPTO_ONCE ex_data_init = CRYPTO_ONCE_STATIC_INIT;
+
+static void do_ex_data_init(void)
+{
+ ex_data_lock = CRYPTO_THREAD_lock_new();
+}
+
+/*
+ * Return the EX_CALLBACKS from the |ex_data| array that corresponds to
+ * a given class. On success, *holds the lock.*
+ */
+static EX_CALLBACKS *get_and_lock(int class_index)
+{
+ EX_CALLBACKS *ip;
+
+ if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) {
+ CRYPTOerr(CRYPTO_F_GET_AND_LOCK, ERR_R_PASSED_INVALID_ARGUMENT);
+ return NULL;
+ }
+
+ CRYPTO_THREAD_run_once(&ex_data_init, do_ex_data_init);
+
+ if (ex_data_lock == NULL) {
+ /*
+ * This can happen in normal operation when using CRYPTO_mem_leaks().
+ * The CRYPTO_mem_leaks() function calls OPENSSL_cleanup() which cleans
+ * up the locks. Subsequently the BIO that CRYPTO_mem_leaks() uses gets
+ * freed, which also attempts to free the ex_data. However
+ * CRYPTO_mem_leaks() ensures that the ex_data is freed early (i.e.
+ * before OPENSSL_cleanup() is called), so if we get here we can safely
+ * ignore this operation. We just treat it as an error.
+ */
+ return NULL;
+ }
+
+ ip = &ex_data[class_index];
+ CRYPTO_THREAD_write_lock(ex_data_lock);
+ return ip;
+}
+
+static void cleanup_cb(EX_CALLBACK *funcs)
+{
+ OPENSSL_free(funcs);
+}
+
+/*
+ * Release all "ex_data" state to prevent memory leaks. This can't be made
+ * thread-safe without overhauling a lot of stuff, and shouldn't really be
+ * called under potential race-conditions anyway (it's for program shutdown
+ * after all).
+ */
+void crypto_cleanup_all_ex_data_int(void)
+{
+ int i;
+
+ for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) {
+ EX_CALLBACKS *ip = &ex_data[i];
+
+ sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb);
+ ip->meth = NULL;
+ }
+
+ CRYPTO_THREAD_lock_free(ex_data_lock);
+ ex_data_lock = NULL;
+}
+
+
+/*
+ * Unregister a new index by replacing the callbacks with no-ops.
+ * Any in-use instances are leaked.
+ */
+static void dummy_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
+ long argl, void *argp)
+{
+}
+
+static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
+ long argl, void *argp)
+{
+}
+
+static int dummy_dup(CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from,
+ void *from_d, int idx,
+ long argl, void *argp)
+{
+ return 0;
+}
+
+int CRYPTO_free_ex_index(int class_index, int idx)
+{
+ EX_CALLBACKS *ip = get_and_lock(class_index);
+ EX_CALLBACK *a;
+ int toret = 0;
+
+ if (ip == NULL)
+ return 0;
+ if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
+ goto err;
+ a = sk_EX_CALLBACK_value(ip->meth, idx);
+ if (a == NULL)
+ goto err;
+ a->new_func = dummy_new;
+ a->dup_func = dummy_dup;
+ a->free_func = dummy_free;
+ toret = 1;