Remove hash table lookups from ex_data.

Instead, each module defines a static CRYPTO_EX_DATA_CLASS to hold the values.
This makes CRYPTO_cleanup_all_ex_data a no-op as spreading the
CRYPTO_EX_DATA_CLASSes across modules (and across crypto and ssl) makes cleanup
slightly trickier. We can make it do something if needbe, but it's probably not
worth the trouble.

Change-Id: Ib6f6fd39a51d8ba88649f0fa29c66db540610c76
Reviewed-on: https://boringssl-review.googlesource.com/4375
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/dh/dh.c b/crypto/dh/dh.c
index 86804bf..77ebb1d 100644
--- a/crypto/dh/dh.c
+++ b/crypto/dh/dh.c
@@ -71,6 +71,8 @@
 
 extern const DH_METHOD DH_default_method;
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 DH *DH_new(void) { return DH_new_method(NULL); }
 
 DH *DH_new_method(const ENGINE *engine) {
@@ -94,13 +96,13 @@
   CRYPTO_MUTEX_init(&dh->method_mont_p_lock);
 
   dh->references = 1;
-  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_DH, dh, &dh->ex_data)) {
+  if (!CRYPTO_new_ex_data(&g_ex_data_class, dh, &dh->ex_data)) {
     OPENSSL_free(dh);
     return NULL;
   }
 
   if (dh->meth->init && !dh->meth->init(dh)) {
-    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DH, dh, &dh->ex_data);
+    CRYPTO_free_ex_data(&g_ex_data_class, dh, &dh->ex_data);
     METHOD_unref(dh->meth);
     OPENSSL_free(dh);
     return NULL;
@@ -123,7 +125,7 @@
   }
   METHOD_unref(dh->meth);
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DH, dh, &dh->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class, dh, &dh->ex_data);
 
   if (dh->method_mont_p) BN_MONT_CTX_free(dh->method_mont_p);
   if (dh->p != NULL) BN_clear_free(dh->p);
@@ -234,8 +236,12 @@
 
 int DH_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                         CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_DH, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, new_func,
+                               dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int DH_set_ex_data(DH *d, int idx, void *arg) {
diff --git a/crypto/dsa/dsa.c b/crypto/dsa/dsa.c
index c580956..78e4154 100644
--- a/crypto/dsa/dsa.c
+++ b/crypto/dsa/dsa.c
@@ -75,6 +75,8 @@
 
 extern const DSA_METHOD DSA_default_method;
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 DSA *DSA_new(void) { return DSA_new_method(NULL); }
 
 DSA *DSA_new_method(const ENGINE *engine) {
@@ -100,14 +102,14 @@
 
   CRYPTO_MUTEX_init(&dsa->method_mont_p_lock);
 
-  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_DSA, dsa, &dsa->ex_data)) {
+  if (!CRYPTO_new_ex_data(&g_ex_data_class, dsa, &dsa->ex_data)) {
     METHOD_unref(dsa->meth);
     OPENSSL_free(dsa);
     return NULL;
   }
 
   if (dsa->meth->init && !dsa->meth->init(dsa)) {
-    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DSA, dsa, &dsa->ex_data);
+    CRYPTO_free_ex_data(&g_ex_data_class, dsa, &dsa->ex_data);
     METHOD_unref(dsa->meth);
     OPENSSL_free(dsa);
     return NULL;
@@ -130,7 +132,7 @@
   }
   METHOD_unref(dsa->meth);
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DSA, dsa, &dsa->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class, dsa, &dsa->ex_data);
 
   if (dsa->p != NULL) {
     BN_clear_free(dsa->p);
@@ -321,8 +323,12 @@
 
 int DSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                          CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_DSA, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, new_func,
+                               dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int DSA_set_ex_data(DSA *d, int idx, void *arg) {
diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c
index a11a439..0f4e666 100644
--- a/crypto/ec/ec_key.c
+++ b/crypto/ec/ec_key.c
@@ -80,6 +80,8 @@
 #include "../internal.h"
 
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 EC_KEY *EC_KEY_new(void) { return EC_KEY_new_method(NULL); }
 
 EC_KEY *EC_KEY_new_method(const ENGINE *engine) {
@@ -102,7 +104,7 @@
   ret->conv_form = POINT_CONVERSION_UNCOMPRESSED;
   ret->references = 1;
 
-  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_EC_KEY, ret, &ret->ex_data)) {
+  if (!CRYPTO_new_ex_data(&g_ex_data_class, ret, &ret->ex_data)) {
     goto err1;
   }
 
@@ -113,7 +115,7 @@
   return ret;
 
 err2:
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EC_KEY, ret, &ret->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class, ret, &ret->ex_data);
 err1:
   if (ret->ecdsa_meth) {
     METHOD_unref(ret->ecdsa_meth);
@@ -161,7 +163,7 @@
     BN_clear_free(r->priv_key);
   }
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EC_KEY, r, &r->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class, r, &r->ex_data);
 
   OPENSSL_cleanse((void *)r, sizeof(EC_KEY));
   OPENSSL_free(r);
@@ -208,8 +210,8 @@
     }
   }
   /* copy method/extra data */
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EC_KEY, dest, &dest->ex_data);
-  if (!CRYPTO_dup_ex_data(CRYPTO_EX_INDEX_EC_KEY, &dest->ex_data,
+  CRYPTO_free_ex_data(&g_ex_data_class, dest, &dest->ex_data);
+  if (!CRYPTO_dup_ex_data(&g_ex_data_class, &dest->ex_data,
                           &src->ex_data)) {
     return NULL;
   }
@@ -505,8 +507,12 @@
 int EC_KEY_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                             CRYPTO_EX_dup *dup_func,
                             CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_EC_KEY, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, new_func,
+                               dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int EC_KEY_set_ex_data(EC_KEY *d, int idx, void *arg) {
diff --git a/crypto/ex_data.c b/crypto/ex_data.c
index 8f54a90..8f199fd 100644
--- a/crypto/ex_data.c
+++ b/crypto/ex_data.c
@@ -121,112 +121,25 @@
 #include "internal.h"
 
 
-typedef struct crypto_ex_data_func_st {
+struct crypto_ex_data_func_st {
   long argl;  /* Arbitary long */
   void *argp; /* Arbitary void pointer */
   CRYPTO_EX_new *new_func;
   CRYPTO_EX_free *free_func;
   CRYPTO_EX_dup *dup_func;
-} CRYPTO_EX_DATA_FUNCS;
+};
 
-typedef struct st_ex_class_item {
-  STACK_OF(CRYPTO_EX_DATA_FUNCS) *meth;
-  int class_value;
-} EX_CLASS_ITEM;
-
-static CRYPTO_once_t global_classes_once = CRYPTO_ONCE_INIT;
-static struct CRYPTO_STATIC_MUTEX global_classes_lock =
-    CRYPTO_STATIC_MUTEX_INIT;
-static LHASH_OF(EX_CLASS_ITEM) *global_classes = NULL;
-
-/* class_hash is a hash function used by an LHASH of |EX_CLASS_ITEM|
- * structures. */
-static uint32_t class_hash(const EX_CLASS_ITEM *a) {
-  return a->class_value;
-}
-
-/* class_cmp is a comparison function for an LHASH of |EX_CLASS_ITEM|
- * structures. */
-static int class_cmp(const EX_CLASS_ITEM *a, const EX_CLASS_ITEM *b) {
-  return a->class_value - b->class_value;
-}
-
-/* data_funcs_free is a callback function from |sk_pop_free| that frees a
- * |CRYPTO_EX_DATA_FUNCS|. */
-static void data_funcs_free(CRYPTO_EX_DATA_FUNCS *funcs) {
-  OPENSSL_free(funcs);
-}
-
-/* class_free is a callback function from lh_doall to free the EX_CLASS_ITEM
- * structures. */
-static void class_free(EX_CLASS_ITEM *item) {
-  sk_CRYPTO_EX_DATA_FUNCS_pop_free(item->meth, data_funcs_free);
-  OPENSSL_free(item);
-}
-
-static void global_classes_init(void) {
-  global_classes = lh_EX_CLASS_ITEM_new(class_hash, class_cmp);
-}
-
-static EX_CLASS_ITEM *get_class(int class_value) {
-  EX_CLASS_ITEM template, *class_item;
-  int ok = 0;
-
-  CRYPTO_once(&global_classes_once, global_classes_init);
-
-  if (global_classes == NULL) {
-    return NULL;
-  }
-
-  CRYPTO_STATIC_MUTEX_lock_write(&global_classes_lock);
-  template.class_value = class_value;
-  class_item = lh_EX_CLASS_ITEM_retrieve(global_classes, &template);
-  if (class_item != NULL) {
-    ok = 1;
-  } else {
-    class_item = OPENSSL_malloc(sizeof(EX_CLASS_ITEM));
-    if (class_item) {
-      class_item->class_value = class_value;
-      class_item->meth = sk_CRYPTO_EX_DATA_FUNCS_new_null();
-      if (class_item->meth != NULL) {
-        EX_CLASS_ITEM *old_data;
-        ok = lh_EX_CLASS_ITEM_insert(global_classes, &old_data, class_item);
-        assert(old_data == NULL);
-      }
-    }
-  }
-  CRYPTO_STATIC_MUTEX_unlock(&global_classes_lock);
-
-  if (!ok) {
-    if (class_item) {
-      if (class_item->meth) {
-        sk_CRYPTO_EX_DATA_FUNCS_free(class_item->meth);
-      }
-      OPENSSL_free(class_item);
-      class_item = NULL;
-    }
-
-    OPENSSL_PUT_ERROR(CRYPTO, get_class, ERR_R_MALLOC_FAILURE);
-  }
-
-  return class_item;
-}
-
-int CRYPTO_get_ex_new_index(int class_value, long argl, void *argp,
-                            CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+int CRYPTO_get_ex_new_index(CRYPTO_EX_DATA_CLASS *ex_data_class, int *out_index,
+                            long argl, void *argp, CRYPTO_EX_new *new_func,
+                            CRYPTO_EX_dup *dup_func,
                             CRYPTO_EX_free *free_func) {
-  EX_CLASS_ITEM *const item = get_class(class_value);
   CRYPTO_EX_DATA_FUNCS *funcs;
-  int ret = -1;
-
-  if (!item) {
-    return -1;
-  }
+  int ret = 0;
 
   funcs = OPENSSL_malloc(sizeof(CRYPTO_EX_DATA_FUNCS));
   if (funcs == NULL) {
     OPENSSL_PUT_ERROR(CRYPTO, CRYPTO_get_ex_new_index, ERR_R_MALLOC_FAILURE);
-    return -1;
+    return 0;
   }
 
   funcs->argl = argl;
@@ -235,18 +148,24 @@
   funcs->dup_func = dup_func;
   funcs->free_func = free_func;
 
-  CRYPTO_STATIC_MUTEX_lock_write(&global_classes_lock);
+  CRYPTO_STATIC_MUTEX_lock_write(&ex_data_class->lock);
 
-  if (!sk_CRYPTO_EX_DATA_FUNCS_push(item->meth, funcs)) {
+  if (ex_data_class->meth == NULL) {
+    ex_data_class->meth = sk_CRYPTO_EX_DATA_FUNCS_new_null();
+  }
+
+  if (ex_data_class->meth == NULL ||
+      !sk_CRYPTO_EX_DATA_FUNCS_push(ex_data_class->meth, funcs)) {
     OPENSSL_PUT_ERROR(CRYPTO, CRYPTO_get_ex_new_index, ERR_R_MALLOC_FAILURE);
     OPENSSL_free(funcs);
     goto err;
   }
 
-  ret = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth) - 1;
+  *out_index = sk_CRYPTO_EX_DATA_FUNCS_num(ex_data_class->meth) - 1;
+  ret = 1;
 
 err:
-  CRYPTO_STATIC_MUTEX_unlock(&global_classes_lock);
+  CRYPTO_STATIC_MUTEX_unlock(&ex_data_class->lock);
   return ret;
 }
 
@@ -287,25 +206,20 @@
  * a fresh stack of them. Otherwise it sets |*out| to NULL. It returns one on
  * success or zero on error. */
 static int get_func_pointers(STACK_OF(CRYPTO_EX_DATA_FUNCS) **out,
-                             int class_value) {
-  EX_CLASS_ITEM *const item = get_class(class_value);
+                             CRYPTO_EX_DATA_CLASS *ex_data_class) {
   size_t n;
 
-  if (!item) {
-    return 0;
-  }
-
   *out = NULL;
 
   /* CRYPTO_EX_DATA_FUNCS structures are static once set, so we can take a
    * shallow copy of the list under lock and then use the structures without
    * the lock held. */
-  CRYPTO_STATIC_MUTEX_lock_read(&global_classes_lock);
-  n = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
+  CRYPTO_STATIC_MUTEX_lock_read(&ex_data_class->lock);
+  n = sk_CRYPTO_EX_DATA_FUNCS_num(ex_data_class->meth);
   if (n > 0) {
-    *out = sk_CRYPTO_EX_DATA_FUNCS_dup(item->meth);
+    *out = sk_CRYPTO_EX_DATA_FUNCS_dup(ex_data_class->meth);
   }
-  CRYPTO_STATIC_MUTEX_unlock(&global_classes_lock);
+  CRYPTO_STATIC_MUTEX_unlock(&ex_data_class->lock);
 
   if (n > 0 && *out == NULL) {
     OPENSSL_PUT_ERROR(CRYPTO, get_func_pointers, ERR_R_MALLOC_FAILURE);
@@ -315,13 +229,14 @@
   return 1;
 }
 
-int CRYPTO_new_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
+int CRYPTO_new_ex_data(CRYPTO_EX_DATA_CLASS *ex_data_class, void *obj,
+                       CRYPTO_EX_DATA *ad) {
   STACK_OF(CRYPTO_EX_DATA_FUNCS) *func_pointers;
   size_t i;
 
   ad->sk = NULL;
 
-  if (!get_func_pointers(&func_pointers, class_value)) {
+  if (!get_func_pointers(&func_pointers, ex_data_class)) {
     return 0;
   }
 
@@ -339,7 +254,7 @@
   return 1;
 }
 
-int CRYPTO_dup_ex_data(int class_value, CRYPTO_EX_DATA *to,
+int CRYPTO_dup_ex_data(CRYPTO_EX_DATA_CLASS *ex_data_class, CRYPTO_EX_DATA *to,
                        const CRYPTO_EX_DATA *from) {
   STACK_OF(CRYPTO_EX_DATA_FUNCS) *func_pointers;
   size_t i;
@@ -350,7 +265,7 @@
     return 1;
   }
 
-  if (!get_func_pointers(&func_pointers, class_value)) {
+  if (!get_func_pointers(&func_pointers, ex_data_class)) {
     return 0;
   }
 
@@ -370,11 +285,12 @@
   return 1;
 }
 
-void CRYPTO_free_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
+void CRYPTO_free_ex_data(CRYPTO_EX_DATA_CLASS *ex_data_class, void *obj,
+                         CRYPTO_EX_DATA *ad) {
   STACK_OF(CRYPTO_EX_DATA_FUNCS) *func_pointers;
   size_t i;
 
-  if (!get_func_pointers(&func_pointers, class_value)) {
+  if (!get_func_pointers(&func_pointers, ex_data_class)) {
     return;
   }
 
@@ -396,13 +312,4 @@
   }
 }
 
-void CRYPTO_cleanup_all_ex_data(void) {
-  CRYPTO_once(&global_classes_once, global_classes_init);
-
-  if (global_classes != NULL) {
-    lh_EX_CLASS_ITEM_doall(global_classes, class_free);
-    lh_EX_CLASS_ITEM_free(global_classes);
-  }
-
-  global_classes = NULL;
-}
+void CRYPTO_cleanup_all_ex_data(void) {}
diff --git a/crypto/internal.h b/crypto/internal.h
index faceefc..9cb8573 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -453,64 +453,54 @@
 
 /* ex_data */
 
-/* CRYPTO_get_ex_new_index allocates a new index for ex_data linked with
- * objects of the given |class|. This should not be called directly, rather
- * each class of object should provide a wrapper function that sets
- * |class_value| correctly.
- *
- * The |class_value| argument should be one of |CRYPTO_EX_INDEX_*|.
- *
- * TODO(fork): replace the class_value with a pointer to EX_CLASS_ITEM. Saves
- * having that hash table and some of the lock-bouncing. Maybe have every
- * module have a private global EX_CLASS_ITEM somewhere and any direct callers
- * of CRYPTO_{get,set}_ex_data{,_index} would have to always call the
- * wrappers. */
-OPENSSL_EXPORT int CRYPTO_get_ex_new_index(int class_value, long argl,
+typedef struct crypto_ex_data_func_st CRYPTO_EX_DATA_FUNCS;
+
+/* CRYPTO_EX_DATA_CLASS tracks the ex_indices registered for a type which
+ * supports ex_data. It should defined as a static global within the module
+ * which defines that type. */
+typedef struct {
+  struct CRYPTO_STATIC_MUTEX lock;
+  STACK_OF(CRYPTO_EX_DATA_FUNCS) *meth;
+} CRYPTO_EX_DATA_CLASS;
+
+#define CRYPTO_EX_DATA_CLASS_INIT {CRYPTO_STATIC_MUTEX_INIT, NULL}
+
+/* CRYPTO_get_ex_new_index allocates a new index for |ex_data_class| and writes
+ * it to |*out_index|. Each class of object should provide a wrapper function
+ * that uses the correct |CRYPTO_EX_DATA_CLASS|. It returns one on success and
+ * zero otherwise. */
+OPENSSL_EXPORT int CRYPTO_get_ex_new_index(CRYPTO_EX_DATA_CLASS *ex_data_class,
+                                           int *out_index, long argl,
                                            void *argp, CRYPTO_EX_new *new_func,
                                            CRYPTO_EX_dup *dup_func,
                                            CRYPTO_EX_free *free_func);
 
-/* CRYPTO_set_ex_data sets an extra data pointer on a given object. This should
- * not be called directly, rather each class of object should provide a wrapper
- * function. */
+/* CRYPTO_set_ex_data sets an extra data pointer on a given object. Each class
+ * of object should provide a wrapper function. */
 OPENSSL_EXPORT int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int index, void *val);
 
-/* CRYPTO_set_ex_data return an extra data pointer for a given object, or NULL
- * if no such index exists. This should not be called directly, rather each
- * class of object should provide a wrapper function. */
+/* CRYPTO_get_ex_data returns an extra data pointer for a given object, or NULL
+ * if no such index exists. Each class of object should provide a wrapper
+ * function. */
 OPENSSL_EXPORT void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int index);
 
-/* CRYPTO_EX_INDEX_* are the built-in classes of objects.
- *
- * TODO(fork): WARNING: these are called "INDEX", but they aren't! */
-enum {
-  CRYPTO_EX_INDEX_SSL,
-  CRYPTO_EX_INDEX_SSL_CTX,
-  CRYPTO_EX_INDEX_SSL_SESSION,
-  CRYPTO_EX_INDEX_X509_STORE_CTX,
-  CRYPTO_EX_INDEX_RSA,
-  CRYPTO_EX_INDEX_DSA,
-  CRYPTO_EX_INDEX_DH,
-  CRYPTO_EX_INDEX_X509,
-  CRYPTO_EX_INDEX_EC_KEY,
-};
-
 /* CRYPTO_new_ex_data initialises a newly allocated |CRYPTO_EX_DATA| which is
- * embedded inside of |obj| which is of class |class_value|. Returns one on
+ * embedded inside of |obj| which is of class |ex_data_class|. Returns one on
  * success and zero otherwise. */
-OPENSSL_EXPORT int CRYPTO_new_ex_data(int class_value, void *obj,
-                                      CRYPTO_EX_DATA *ad);
+OPENSSL_EXPORT int CRYPTO_new_ex_data(CRYPTO_EX_DATA_CLASS *ex_data_class,
+                                      void *obj, CRYPTO_EX_DATA *ad);
 
 /* CRYPTO_dup_ex_data duplicates |from| into a freshly allocated
  * |CRYPTO_EX_DATA|, |to|. Both of which are inside objects of the given
  * class. It returns one on success and zero otherwise. */
-OPENSSL_EXPORT int CRYPTO_dup_ex_data(int class_value, CRYPTO_EX_DATA *to,
+OPENSSL_EXPORT int CRYPTO_dup_ex_data(CRYPTO_EX_DATA_CLASS *ex_data_class,
+                                      CRYPTO_EX_DATA *to,
                                       const CRYPTO_EX_DATA *from);
 
 /* CRYPTO_free_ex_data frees |ad|, which is embedded inside |obj|, which is an
  * object of the given class. */
-OPENSSL_EXPORT void CRYPTO_free_ex_data(int class_value, void *obj,
-                                        CRYPTO_EX_DATA *ad);
+OPENSSL_EXPORT void CRYPTO_free_ex_data(CRYPTO_EX_DATA_CLASS *ex_data_class,
+                                        void *obj, CRYPTO_EX_DATA *ad);
 
 
 #if defined(__cplusplus)
diff --git a/crypto/rsa/rsa.c b/crypto/rsa/rsa.c
index 48be595..650249c 100644
--- a/crypto/rsa/rsa.c
+++ b/crypto/rsa/rsa.c
@@ -72,6 +72,8 @@
 
 extern const RSA_METHOD RSA_default_method;
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 RSA *RSA_new(void) { return RSA_new_method(NULL); }
 
 RSA *RSA_new_method(const ENGINE *engine) {
@@ -96,14 +98,14 @@
   rsa->flags = rsa->meth->flags;
   CRYPTO_MUTEX_init(&rsa->lock);
 
-  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_RSA, rsa, &rsa->ex_data)) {
+  if (!CRYPTO_new_ex_data(&g_ex_data_class, rsa, &rsa->ex_data)) {
     METHOD_unref(rsa->meth);
     OPENSSL_free(rsa);
     return NULL;
   }
 
   if (rsa->meth->init && !rsa->meth->init(rsa)) {
-    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_RSA, rsa, &rsa->ex_data);
+    CRYPTO_free_ex_data(&g_ex_data_class, rsa, &rsa->ex_data);
     METHOD_unref(rsa->meth);
     OPENSSL_free(rsa);
     return NULL;
@@ -128,7 +130,7 @@
   }
   METHOD_unref(rsa->meth);
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_RSA, rsa, &rsa->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class, rsa, &rsa->ex_data);
 
   if (rsa->n != NULL) {
     BN_clear_free(rsa->n);
@@ -285,8 +287,12 @@
 
 int RSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                          CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_RSA, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, new_func,
+                               dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int RSA_set_ex_data(RSA *d, int idx, void *arg) {
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index dae3376..a0cd9fc 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -72,6 +72,8 @@
 #include "../internal.h"
 
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 /* CRL score values */
 
 /* No unhandled critical extensions */
@@ -2054,8 +2056,13 @@
 	{
 	/* This function is (usually) called only once, by
 	 * SSL_get_ex_data_X509_STORE_CTX_idx (ssl/ssl_cert.c). */
-	return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE_CTX, argl, argp,
-			new_func, dup_func, free_func);
+	int index;
+	if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp,
+			new_func, dup_func, free_func))
+		{
+		return -1;
+		}
+	return index;
 	}
 
 int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx, int idx, void *data)
@@ -2225,7 +2232,7 @@
 	ctx->cert=x509;
 	ctx->untrusted=chain;
 
-	if(!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx,
+	if(!CRYPTO_new_ex_data(&g_ex_data_class, ctx,
 			       &ctx->ex_data))
 		{
 		goto err;
@@ -2316,7 +2323,7 @@
 err:
 	if (ex_data_allocated)
 		{
-		CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx, &ctx->ex_data);
+		CRYPTO_free_ex_data(&g_ex_data_class, ctx, &ctx->ex_data);
 		}
 	if (ctx->param != NULL)
 		{
@@ -2357,7 +2364,7 @@
 		sk_X509_pop_free(ctx->chain,X509_free);
 		ctx->chain=NULL;
 		}
-	CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx, &(ctx->ex_data));
+	CRYPTO_free_ex_data(&g_ex_data_class, ctx, &(ctx->ex_data));
 	memset(&ctx->ex_data,0,sizeof(CRYPTO_EX_DATA));
 	}
 
diff --git a/crypto/x509/x_x509.c b/crypto/x509/x_x509.c
index 035b2f4..234494d 100644
--- a/crypto/x509/x_x509.c
+++ b/crypto/x509/x_x509.c
@@ -68,6 +68,8 @@
 #include "../internal.h"
 
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 ASN1_SEQUENCE_enc(X509_CINF, enc, 0) = {
 	ASN1_EXP_OPT(X509_CINF, version, ASN1_INTEGER, 0),
 	ASN1_SIMPLE(X509_CINF, serialNumber, ASN1_INTEGER),
@@ -102,7 +104,7 @@
 		ret->akid = NULL;
 		ret->aux = NULL;
 		ret->crldp = NULL;
-		CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data);
+		CRYPTO_new_ex_data(&g_ex_data_class, ret, &ret->ex_data);
 		break;
 
 		case ASN1_OP_D2I_POST:
@@ -111,7 +113,7 @@
 		break;
 
 		case ASN1_OP_FREE_POST:
-		CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data);
+		CRYPTO_free_ex_data(&g_ex_data_class, ret, &ret->ex_data);
 		X509_CERT_AUX_free(ret->aux);
 		ASN1_OCTET_STRING_free(ret->skid);
 		AUTHORITY_KEYID_free(ret->akid);
@@ -147,8 +149,13 @@
 int X509_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
 	     CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
         {
-	return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509, argl, argp,
-				new_func, dup_func, free_func);
+	int index;
+	if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp,
+			new_func, dup_func, free_func))
+		{
+		return -1;
+		}
+	return index;
         }
 
 int X509_set_ex_data(X509 *r, int idx, void *arg)
diff --git a/include/openssl/ex_data.h b/include/openssl/ex_data.h
index fa9de39..2303eb4 100644
--- a/include/openssl/ex_data.h
+++ b/include/openssl/ex_data.h
@@ -197,10 +197,9 @@
                           void **from_d, int index, long argl, void *argp);
 
 
-/* Private functions. */
+/* Deprecated functions. */
 
-/* CRYPTO_cleanup_all_ex_data cleans up all ex_data state. It assumes that no
- * other threads are executing code that might call ex_data functions. */
+/* CRYPTO_cleanup_all_ex_data does nothing. */
 OPENSSL_EXPORT void CRYPTO_cleanup_all_ex_data(void);
 
 struct crypto_ex_data_st {
diff --git a/include/openssl/lhash.h b/include/openssl/lhash.h
index 02e51ca..d2ee982 100644
--- a/include/openssl/lhash.h
+++ b/include/openssl/lhash.h
@@ -96,7 +96,6 @@
  *
  * LHASH_OF:ASN1_OBJECT
  * LHASH_OF:CONF_VALUE
- * LHASH_OF:EX_CLASS_ITEM
  * LHASH_OF:SSL_SESSION */
 
 #define IN_LHASH_H
diff --git a/include/openssl/lhash_macros.h b/include/openssl/lhash_macros.h
index 018856a..1d98107 100644
--- a/include/openssl/lhash_macros.h
+++ b/include/openssl/lhash_macros.h
@@ -92,46 +92,6 @@
                             void (*)(CONF_VALUE *, void *), func), \
                arg);
 
-/* EX_CLASS_ITEM */
-#define lh_EX_CLASS_ITEM_new(hash, comp)                                    \
-  ((LHASH_OF(EX_CLASS_ITEM) *)lh_new(                                       \
-      CHECKED_CAST(lhash_hash_func, uint32_t (*)(const EX_CLASS_ITEM *),    \
-                   hash),                                                   \
-      CHECKED_CAST(lhash_cmp_func,                                          \
-                   int (*)(const EX_CLASS_ITEM *a, const EX_CLASS_ITEM *b), \
-                   comp)))
-
-#define lh_EX_CLASS_ITEM_free(lh) \
-  lh_free(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh));
-
-#define lh_EX_CLASS_ITEM_num_items(lh) \
-  lh_num_items(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh))
-
-#define lh_EX_CLASS_ITEM_retrieve(lh, data)                  \
-  ((EX_CLASS_ITEM *)lh_retrieve(                             \
-      CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
-      CHECKED_CAST(void *, EX_CLASS_ITEM *, data)))
-
-#define lh_EX_CLASS_ITEM_insert(lh, old_data, data)                \
-  lh_insert(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
-            CHECKED_CAST(void **, EX_CLASS_ITEM **, old_data),     \
-            CHECKED_CAST(void *, EX_CLASS_ITEM *, data))
-
-#define lh_EX_CLASS_ITEM_delete(lh, data)                    \
-  ((EX_CLASS_ITEM *)lh_delete(                               \
-      CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
-      CHECKED_CAST(void *, EX_CLASS_ITEM *, data)))
-
-#define lh_EX_CLASS_ITEM_doall(lh, func)                          \
-  lh_doall(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
-           CHECKED_CAST(void (*)(void *), void (*)(EX_CLASS_ITEM *), func));
-
-#define lh_EX_CLASS_ITEM_doall_arg(lh, func, arg)                     \
-  lh_doall_arg(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
-               CHECKED_CAST(void (*)(void *, void *),                 \
-                            void (*)(EX_CLASS_ITEM *, void *), func), \
-               arg);
-
 /* SSL_SESSION */
 #define lh_SSL_SESSION_new(hash, comp)                                        \
   ((LHASH_OF(SSL_SESSION) *)lh_new(                                           \
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 257ffbc..97a94a2 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -161,6 +161,9 @@
                            SSL_AD_NO_RENEGOTIATION + SSL_AD_REASON_OFFSET,
                        ssl_alert_reason_code_mismatch);
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl = CRYPTO_EX_DATA_CLASS_INIT;
+static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl_ctx = CRYPTO_EX_DATA_CLASS_INIT;
+
 int SSL_clear(SSL *s) {
   if (s->method == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_clear, SSL_R_NO_METHOD_SPECIFIED);
@@ -339,7 +342,7 @@
   s->rwstate = SSL_NOTHING;
   s->rstate = SSL_ST_READ_HEADER;
 
-  CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
+  CRYPTO_new_ex_data(&g_ex_data_class_ssl, s, &s->ex_data);
 
   s->psk_identity_hint = NULL;
   if (ctx->psk_identity_hint) {
@@ -542,7 +545,7 @@
     X509_VERIFY_PARAM_free(s->param);
   }
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class_ssl, s, &s->ex_data);
 
   if (s->bbio != NULL) {
     /* If the buffering BIO is in place, pop it off */
@@ -1833,7 +1836,7 @@
     goto err;
   }
 
-  CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_CTX, ret, &ret->ex_data);
+  CRYPTO_new_ex_data(&g_ex_data_class_ssl_ctx, ret, &ret->ex_data);
 
   ret->extra_certs = NULL;
 
@@ -1905,7 +1908,7 @@
     SSL_CTX_flush_sessions(a, 0);
   }
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class_ssl_ctx, a, &a->ex_data);
 
   if (a->sessions != NULL) {
     lh_SSL_SESSION_free(a->sessions);
@@ -2548,8 +2551,12 @@
 
 int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                          CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl, &index, argl, argp,
+                               new_func, dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int SSL_set_ex_data(SSL *s, int idx, void *arg) {
@@ -2563,8 +2570,12 @@
 int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                              CRYPTO_EX_dup *dup_func,
                              CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_CTX, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl_ctx, &index, argl, argp,
+                               new_func, dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, void *arg) {
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index de210f5..4db1f03 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -150,6 +150,8 @@
  * that it needs to asynchronously fetch session information. */
 static const char g_pending_session_magic = 0;
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s);
 static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s);
 static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck);
@@ -172,8 +174,12 @@
 int SSL_SESSION_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                                  CRYPTO_EX_dup *dup_func,
                                  CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_SESSION, argl, argp,
-                                 new_func, dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, new_func,
+                               dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int SSL_SESSION_set_ex_data(SSL_SESSION *s, int idx, void *arg) {
@@ -198,7 +204,7 @@
   ss->references = 1;
   ss->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
   ss->time = (unsigned long)time(NULL);
-  CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data);
+  CRYPTO_new_ex_data(&g_ex_data_class, ss, &ss->ex_data);
   return ss;
 }
 
@@ -622,7 +628,7 @@
     return;
   }
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class, ss, &ss->ex_data);
 
   OPENSSL_cleanse(ss->master_key, sizeof ss->master_key);
   OPENSSL_cleanse(ss->session_id, sizeof ss->session_id);