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/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) {}