Remove the implementation abstraction from ERR.
Since ERR will soon have thread-local storage, we don't need to worry
about high-performance implementations and thus don't need to be able to
switch two different implementations at run-time.
Change-Id: I0598054ee8a8b499ac686ea635a96f5d03c754e0
diff --git a/crypto/err/err.c b/crypto/err/err.c
index cbeb7c7..a11f891 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.c
@@ -134,29 +134,6 @@
extern const size_t kOpenSSLReasonValuesLen;
extern const char kOpenSSLReasonStringData[];
-/* err_fns contains a pointer to the current error implementation. */
-static const struct ERR_FNS_st *err_fns = NULL;
-extern const struct ERR_FNS_st openssl_err_default_impl;
-
-#define ERRFN(a) err_fns->a
-
-/* err_fns_check is an internal function that checks whether "err_fns" is set
- * and if not, sets it to the default. */
-static void err_fns_check(void) {
- /* In practice, this is not a race problem because loading the error strings
- * at init time will cause this pointer to be set before the process goes
- * multithreaded. */
- if (err_fns) {
- return;
- }
-
- CRYPTO_w_lock(CRYPTO_LOCK_ERR);
- if (!err_fns) {
- err_fns = &openssl_err_default_impl;
- }
- CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
-}
-
/* err_clear_data frees the optional |data| member of the given error. */
static void err_clear_data(struct err_error_st *error) {
if (error->data != NULL && (error->flags & ERR_FLAG_MALLOCED) != 0) {
@@ -172,10 +149,84 @@
memset(error, 0, sizeof(struct err_error_st));
}
+DEFINE_LHASH_OF(ERR_STATE);
+
+/* state_hash is a map from thread ID to ERR_STATE. It works like thread-local
+ * storage. */
+static LHASH_OF(ERR_STATE) *state_hash = NULL;
+
+/* global_next_library contains the next custom library value to return. */
+static int global_next_library = ERR_NUM_LIBS;
+
+/* err_state_hash is an lhash hash function for ERR_STATE. */
+static uint32_t err_state_hash(const ERR_STATE *a) {
+ return CRYPTO_THREADID_hash(&a->tid);
+}
+
+/* err_state_cmp is an lhash compare function for ERR_STATE. */
+static int err_state_cmp(const ERR_STATE *a, const ERR_STATE *b) {
+ return CRYPTO_THREADID_cmp(&a->tid, &b->tid);
+}
+
/* err_get_state gets the ERR_STATE object for the current thread. */
static ERR_STATE *err_get_state(void) {
- err_fns_check();
- return ERRFN(get_state)();
+ CRYPTO_THREADID tid;
+ ERR_STATE pattern, *state, *race_state;
+ int insert_result;
+ static ERR_STATE fallback;
+
+ CRYPTO_THREADID_current(&tid);
+ memset(&pattern, 0, sizeof(pattern));
+ CRYPTO_THREADID_cpy(&pattern.tid, &tid);
+
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ if (state_hash == NULL) {
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ if (state_hash == NULL) {
+ state_hash = lh_ERR_STATE_new(err_state_hash, err_state_cmp);
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ }
+
+ if (state_hash == NULL) {
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ return NULL;
+ }
+
+ state = lh_ERR_STATE_retrieve(state_hash, &pattern);
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ if (state != NULL) {
+ return state;
+ }
+
+ state = OPENSSL_malloc(sizeof(ERR_STATE));
+ if (state == NULL) {
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ /* The other error functions don't cope with a failure to get the error
+ * state, so we return a dummy value. */
+ return &fallback;
+ }
+
+ memset(state, 0, sizeof(ERR_STATE));
+ CRYPTO_THREADID_cpy(&state->tid, &tid);
+
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ insert_result = lh_ERR_STATE_insert(state_hash, &race_state, state);
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+
+ if (!insert_result) {
+ /* Insertion failed because of malloc failure. */
+ OPENSSL_free(state);
+ return &fallback;
+ }
+
+ /* We cannot have raced with another thread to insert an ERR_STATE because no
+ * other thread should be inserting values for this thread. */
+ assert(race_state == NULL);
+
+ return state;
}
static uint32_t get_error_values(int inc, int top, const char **file, int *line,
@@ -321,15 +372,24 @@
void ERR_remove_thread_state(const CRYPTO_THREADID *tid) {
CRYPTO_THREADID current;
- ERR_STATE *state;
if (tid == NULL) {
CRYPTO_THREADID_current(¤t);
tid = ¤t;
}
- err_fns_check();
- state = ERRFN(release_state)(tid);
+ ERR_STATE pattern;
+ CRYPTO_THREADID_cpy(&pattern.tid, tid);
+
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ if (state_hash == NULL) {
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ return;
+ }
+
+ ERR_STATE *state = lh_ERR_STATE_delete(state_hash, &pattern);
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+
if (state == NULL) {
return;
}
@@ -338,8 +398,13 @@
}
int ERR_get_next_error_library(void) {
- err_fns_check();
- return ERRFN(get_next_library)();
+ int ret;
+
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ ret = global_next_library++;
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+
+ return ret;
}
void ERR_clear_system_error(void) {
@@ -784,8 +849,13 @@
void ERR_load_crypto_strings(void) {}
void ERR_free_strings(void) {
- err_fns_check();
- ERRFN(shutdown)(err_state_free);
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ if (state_hash) {
+ lh_ERR_STATE_doall(state_hash, err_state_free);
+ lh_ERR_STATE_free(state_hash);
+ state_hash = NULL;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
}
void ERR_load_BIO_strings(void) {}