[openssl] master update

Dr. Paul Dale pauli at openssl.org
Sun Sep 26 22:39:00 UTC 2021


The branch master has been updated
       via  63d0f4d2b04ed334e534c9f6d0b18262161b0050 (commit)
       via  0855591e1f3559313641c13e4b7ce900ce42321c (commit)
       via  b66b024cf7124c9639011b27b70a082e3bc3d269 (commit)
       via  06394a6cc7c784b46bc6d1e65a1ff39637093934 (commit)
       via  c568900c9ac02e92c54bd3168773d54d7350a580 (commit)
       via  29c80c6004de8bfd1792e421bbe03ab5f075f21d (commit)
       via  397065c621e733fff80dedb28252120ec143693e (commit)
       via  b0b456f8c8b628c3d7e212339e31cbfd06ac4ec8 (commit)
      from  75cce8ddee8c108039d0329c4f84466aad0f9c3c (commit)


- Log -----------------------------------------------------------------
commit 63d0f4d2b04ed334e534c9f6d0b18262161b0050
Author: Pauli <ppzgs1 at gmail.com>
Date:   Sat Sep 25 10:41:02 2021 +1000

    Add changes entry indicating that the OBJ_* calls are now thread safe
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

commit 0855591e1f3559313641c13e4b7ce900ce42321c
Author: Pauli <pauli at openssl.org>
Date:   Mon Jun 14 11:11:16 2021 +1000

    test: add threading test for object creation
    
    In addition, rework the multi tests to use common code.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

commit b66b024cf7124c9639011b27b70a082e3bc3d269
Author: Pauli <pauli at openssl.org>
Date:   Fri Jun 11 19:10:49 2021 +1000

    doc: add note to indicate that the OBJ_ functions were not thread safe in 3.0
    
    Also remove OBJ_thread from the list of non-threadsafe functions.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

commit 06394a6cc7c784b46bc6d1e65a1ff39637093934
Author: Pauli <pauli at openssl.org>
Date:   Thu Jun 24 23:51:53 2021 +1000

    doc: Document that the OBJ creation functions are now thread safe.
    
    With the OBJ_ thread locking in place, these documentation changes are not
    required.
    
    This reverts commit 0218bcdd3feab456135207c140998305df73ab7b.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

commit c568900c9ac02e92c54bd3168773d54d7350a580
Author: Pauli <pauli at openssl.org>
Date:   Thu Jun 17 11:05:02 2021 +1000

    obj: add locking to the OBJ sigid calls
    
    This is done using a single global lock.  The premise for this is that new
    objects will most frequently be added at start up and never added subsequently.
    Thus, the locking will be for read most of the time.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

commit 29c80c6004de8bfd1792e421bbe03ab5f075f21d
Author: Pauli <pauli at openssl.org>
Date:   Thu Jun 17 12:41:36 2021 +1000

    obj: make new NIDs use tsan if possible
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

commit 397065c621e733fff80dedb28252120ec143693e
Author: Pauli <pauli at openssl.org>
Date:   Fri Jun 11 17:05:20 2021 +1000

    obj: make the OBJ_ calls thread safe
    
    This is done using a single global lock.  The premise for this is that new
    objects will most frequently be added at start up and never added subsequently.
    Thus, the locking will be for read most of the time.
    
    This does, however, introduce the overhead of taking an uncontested read lock
    when accessing the object database.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

commit b0b456f8c8b628c3d7e212339e31cbfd06ac4ec8
Author: Pauli <pauli at openssl.org>
Date:   Thu Jun 17 12:36:33 2021 +1000

    tsan: add an addition macro
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15713)

-----------------------------------------------------------------------

Summary of changes:
 CHANGES.md                     |   4 +
 crypto/objects/obj_dat.c       | 365 +++++++++++++++++++++++-----------------
 crypto/objects/obj_xref.c      | 157 ++++++++++++-----
 doc/man3/OBJ_nid2obj.pod       |   6 +-
 doc/man7/openssl-threads.pod   |   4 +-
 doc/man7/provider-base.pod     |   7 +-
 include/internal/tsan_assist.h |  22 ++-
 test/threadstest.c             | 372 +++++++++++++++++++++++++----------------
 8 files changed, 585 insertions(+), 352 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 84fb4c3f84..dc3008f814 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -24,6 +24,10 @@ OpenSSL 3.1
 
 ### Changes between 3.0 and 3.1 [xx XXX xxxx]
 
+ * The various OBJ_* functions have been made thread safe.
+
+   *Paul Dale*
+
  * CCM8 cipher suites in TLS have been downgraded to security level zero
    because they use a short authentication tag which lowers their strength.
 
diff --git a/crypto/objects/obj_dat.c b/crypto/objects/obj_dat.c
index 653cc9ad94..a146a96aad 100644
--- a/crypto/objects/obj_dat.c
+++ b/crypto/objects/obj_dat.c
@@ -11,6 +11,8 @@
 #include "crypto/ctype.h"
 #include <limits.h>
 #include "internal/cryptlib.h"
+#include "internal/thread_once.h"
+#include "internal/tsan_assist.h"
 #include <openssl/lhash.h>
 #include <openssl/asn1.h>
 #include "crypto/objects.h"
@@ -21,6 +23,14 @@
 /* obj_dat.h is generated from objects.h by obj_dat.pl */
 #include "obj_dat.h"
 
+/*
+ * If we don't have suitable TSAN support, we'll use a lock for generation of
+ * new NIDs.  This will be slower of course.
+ */
+#ifndef tsan_ld_acq
+# define OBJ_USE_LOCK_FOR_NEW_NID
+#endif
+
 DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, sn);
 DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, ln);
 DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, obj);
@@ -35,8 +45,71 @@ struct added_obj_st {
     ASN1_OBJECT *obj;
 };
 
-static int new_nid = NUM_NID;
 static LHASH_OF(ADDED_OBJ) *added = NULL;
+static CRYPTO_RWLOCK *ossl_obj_lock = NULL;
+#ifdef OBJ_USE_LOCK_FOR_NEW_NID
+static CRYPTO_RWLOCK *ossl_obj_nid_lock = NULL;
+#endif
+
+static CRYPTO_ONCE ossl_obj_lock_init = CRYPTO_ONCE_STATIC_INIT;
+
+static ossl_inline void objs_free_locks(void)
+{
+    CRYPTO_THREAD_lock_free(ossl_obj_lock);
+    ossl_obj_lock = NULL;
+#ifdef OBJ_USE_LOCK_FOR_NEW_NID
+    CRYPTO_THREAD_lock_free(ossl_obj_nid_lock);
+    ossl_obj_nid_lock = NULL;
+#endif
+}
+
+DEFINE_RUN_ONCE_STATIC(obj_lock_initialise)
+{
+    /* Make sure we've loaded config before checking for any "added" objects */
+    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+
+    ossl_obj_lock = CRYPTO_THREAD_lock_new();
+    if (ossl_obj_lock == NULL)
+        return 0;
+
+#ifdef OBJ_USE_LOCK_FOR_NEW_NID
+    ossl_obj_nid_lock = CRYPTO_THREAD_lock_new();
+    if (ossl_obj_nid_lock == NULL) {
+        objs_free_locks();
+        return 0;
+    }
+#endif
+    return 1;
+}
+
+static ossl_inline int ossl_init_added_lock(void)
+{
+    return RUN_ONCE(&ossl_obj_lock_init, obj_lock_initialise);
+}
+
+static ossl_inline int ossl_obj_write_lock(int lock)
+{
+    if (!lock)
+        return 1;
+    if (!ossl_init_added_lock())
+        return 0;
+    return CRYPTO_THREAD_write_lock(ossl_obj_lock);
+}
+
+static ossl_inline int ossl_obj_read_lock(int lock)
+{
+    if (!lock)
+        return 1;
+    if (!ossl_init_added_lock())
+        return 0;
+    return CRYPTO_THREAD_read_lock(ossl_obj_lock);
+}
+
+static ossl_inline void ossl_obj_unlock(int lock)
+{
+    if (lock)
+        CRYPTO_THREAD_unlock(ossl_obj_lock);
+}
 
 static int sn_cmp(const ASN1_OBJECT *const *a, const unsigned int *b)
 {
@@ -123,14 +196,6 @@ static int added_obj_cmp(const ADDED_OBJ *ca, const ADDED_OBJ *cb)
     }
 }
 
-static int init_added(void)
-{
-    if (added != NULL)
-        return 1;
-    added = lh_ADDED_OBJ_new(added_obj_hash, added_obj_cmp);
-    return added != NULL;
-}
-
 static void cleanup1_doall(ADDED_OBJ *a)
 {
     a->obj->nid = 0;
@@ -152,47 +217,69 @@ static void cleanup3_doall(ADDED_OBJ *a)
 
 void ossl_obj_cleanup_int(void)
 {
-    if (added == NULL)
-        return;
-    lh_ADDED_OBJ_set_down_load(added, 0);
-    lh_ADDED_OBJ_doall(added, cleanup1_doall); /* zero counters */
-    lh_ADDED_OBJ_doall(added, cleanup2_doall); /* set counters */
-    lh_ADDED_OBJ_doall(added, cleanup3_doall); /* free objects */
-    lh_ADDED_OBJ_free(added);
-    added = NULL;
+    if (added != NULL) {
+        lh_ADDED_OBJ_set_down_load(added, 0);
+        lh_ADDED_OBJ_doall(added, cleanup1_doall); /* zero counters */
+        lh_ADDED_OBJ_doall(added, cleanup2_doall); /* set counters */
+        lh_ADDED_OBJ_doall(added, cleanup3_doall); /* free objects */
+        lh_ADDED_OBJ_free(added);
+        added = NULL;
+    }
+    objs_free_locks();
 }
 
 int OBJ_new_nid(int num)
 {
+#ifdef OBJ_USE_LOCK_FOR_NEW_NID
+    static int new_nid = NUM_NID;
     int i;
 
+    if (!CRYPTO_THREAD_write_lock(ossl_obj_nid_lock)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
+        return NID_undef;
+    }
     i = new_nid;
     new_nid += num;
+    CRYPTO_THREAD_unlock(ossl_obj_nid_lock);
     return i;
+#else
+    static TSAN_QUALIFIER int new_nid = NUM_NID;
+
+    return tsan_add(&new_nid, num);
+#endif
 }
 
-int OBJ_add_object(const ASN1_OBJECT *obj)
+static int ossl_obj_add_object(const ASN1_OBJECT *obj, int lock)
 {
-    ASN1_OBJECT *o;
+    ASN1_OBJECT *o = NULL;
     ADDED_OBJ *ao[4] = { NULL, NULL, NULL, NULL }, *aop;
     int i;
 
-    if (added == NULL)
-        if (!init_added())
-            return 0;
     if ((o = OBJ_dup(obj)) == NULL)
-        goto err;
-    if ((ao[ADDED_NID] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
+        return NID_undef;
+    if ((ao[ADDED_NID] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL
+            || (o->length != 0
+                && obj->data != NULL
+                && (ao[ADDED_DATA] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
+            || (o->sn != NULL
+                && (ao[ADDED_SNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
+            || (o->ln != NULL
+                && (ao[ADDED_LNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE);
         goto err2;
-    if ((o->length != 0) && (obj->data != NULL))
-        if ((ao[ADDED_DATA] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
-            goto err2;
-    if (o->sn != NULL)
-        if ((ao[ADDED_SNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
-            goto err2;
-    if (o->ln != NULL)
-        if ((ao[ADDED_LNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
-            goto err2;
+    }
+
+    if (!ossl_obj_write_lock(lock)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
+        goto err2;
+    }
+    if (added == NULL) {
+        added = lh_ADDED_OBJ_new(added_obj_hash, added_obj_cmp);
+        if (added == NULL) {
+            ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
 
     for (i = ADDED_DATA; i <= ADDED_NID; i++) {
         if (ao[i] != NULL) {
@@ -207,10 +294,12 @@ int OBJ_add_object(const ASN1_OBJECT *obj)
         ~(ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
           ASN1_OBJECT_FLAG_DYNAMIC_DATA);
 
+    ossl_obj_unlock(lock);
     return o->nid;
- err2:
-    ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE);
+
  err:
+    ossl_obj_unlock(lock);
+ err2:
     for (i = ADDED_DATA; i <= ADDED_NID; i++)
         OPENSSL_free(ao[i]);
     ASN1_OBJECT_free(o);
@@ -219,27 +308,24 @@ int OBJ_add_object(const ASN1_OBJECT *obj)
 
 ASN1_OBJECT *OBJ_nid2obj(int n)
 {
-    ADDED_OBJ ad, *adp;
+    ADDED_OBJ ad, *adp = NULL;
     ASN1_OBJECT ob;
 
-    if ((n >= 0) && (n < NUM_NID)) {
-        if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
-            ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID);
-            return NULL;
-        }
-        return (ASN1_OBJECT *)&(nid_objs[n]);
-    }
-
-    /* Make sure we've loaded config before checking for any "added" objects */
-    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
-
-    if (added == NULL)
+    if (n == NID_undef)
         return NULL;
+    if (n >= 0 && n < NUM_NID && nid_objs[n].nid != NID_undef)
+            return (ASN1_OBJECT *)&(nid_objs[n]);
 
     ad.type = ADDED_NID;
     ad.obj = &ob;
     ob.nid = n;
-    adp = lh_ADDED_OBJ_retrieve(added, &ad);
+    if (!ossl_obj_read_lock(1)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK);
+        return NULL;
+    }
+    if (added != NULL)
+        adp = lh_ADDED_OBJ_retrieve(added, &ad);
+    ossl_obj_unlock(1);
     if (adp != NULL)
         return adp->obj;
 
@@ -249,62 +335,16 @@ ASN1_OBJECT *OBJ_nid2obj(int n)
 
 const char *OBJ_nid2sn(int n)
 {
-    ADDED_OBJ ad, *adp;
-    ASN1_OBJECT ob;
-
-    if ((n >= 0) && (n < NUM_NID)) {
-        if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
-            ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID);
-            return NULL;
-        }
-        return nid_objs[n].sn;
-    }
-
-    /* Make sure we've loaded config before checking for any "added" objects */
-    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
-
-    if (added == NULL)
-        return NULL;
+    ASN1_OBJECT *ob = OBJ_nid2obj(n);
 
-    ad.type = ADDED_NID;
-    ad.obj = &ob;
-    ob.nid = n;
-    adp = lh_ADDED_OBJ_retrieve(added, &ad);
-    if (adp != NULL)
-        return adp->obj->sn;
-
-    ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID);
-    return NULL;
+    return ob == NULL ? NULL : ob->sn;
 }
 
 const char *OBJ_nid2ln(int n)
 {
-    ADDED_OBJ ad, *adp;
-    ASN1_OBJECT ob;
-
-    if ((n >= 0) && (n < NUM_NID)) {
-        if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
-            ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID);
-            return NULL;
-        }
-        return nid_objs[n].ln;
-    }
-
-    /* Make sure we've loaded config before checking for any "added" objects */
-    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
-
-    if (added == NULL)
-        return NULL;
+    ASN1_OBJECT *ob = OBJ_nid2obj(n);
 
-    ad.type = ADDED_NID;
-    ad.obj = &ob;
-    ob.nid = n;
-    adp = lh_ADDED_OBJ_retrieve(added, &ad);
-    if (adp != NULL)
-        return adp->obj->ln;
-
-    ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID);
-    return NULL;
+    return ob == NULL ? NULL : ob->ln;
 }
 
 static int obj_cmp(const ASN1_OBJECT *const *ap, const unsigned int *bp)
@@ -323,33 +363,35 @@ static int obj_cmp(const ASN1_OBJECT *const *ap, const unsigned int *bp)
 
 IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, obj);
 
-int OBJ_obj2nid(const ASN1_OBJECT *a)
+static int ossl_obj_obj2nid(const ASN1_OBJECT *a, const int lock)
 {
+    int nid = NID_undef;
     const unsigned int *op;
     ADDED_OBJ ad, *adp;
 
     if (a == NULL)
         return NID_undef;
-    if (a->nid != 0)
+    if (a->nid != NID_undef)
         return a->nid;
-
     if (a->length == 0)
         return NID_undef;
 
-    /* Make sure we've loaded config before checking for any "added" objects */
-    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
-
+    op = OBJ_bsearch_obj(&a, obj_objs, NUM_OBJ);
+    if (op != NULL)
+        return nid_objs[*op].nid;
+    if (!ossl_obj_read_lock(lock)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK);
+        return NID_undef;
+    }
     if (added != NULL) {
         ad.type = ADDED_DATA;
-        ad.obj = (ASN1_OBJECT *)a; /* XXX: ugly but harmless */
+        ad.obj = (ASN1_OBJECT *)a; /* casting away const is harmless here */
         adp = lh_ADDED_OBJ_retrieve(added, &ad);
         if (adp != NULL)
-            return adp->obj->nid;
+            nid = adp->obj->nid;
     }
-    op = OBJ_bsearch_obj(&a, obj_objs, NUM_OBJ);
-    if (op == NULL)
-        return NID_undef;
-    return nid_objs[*op].nid;
+    ossl_obj_unlock(lock);
+    return nid;
 }
 
 /*
@@ -358,20 +400,20 @@ int OBJ_obj2nid(const ASN1_OBJECT *a)
  * into an object: unlike OBJ_txt2nid it can be used with any objects, not
  * just registered ones.
  */
-
 ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name)
 {
     int nid = NID_undef;
-    ASN1_OBJECT *op;
+    ASN1_OBJECT *op = NULL;
     unsigned char *buf;
     unsigned char *p;
     const unsigned char *cp;
     int i, j;
 
     if (!no_name) {
-        if (((nid = OBJ_sn2nid(s)) != NID_undef) ||
-            ((nid = OBJ_ln2nid(s)) != NID_undef))
+        if ((nid = OBJ_sn2nid(s)) != NID_undef ||
+                (nid = OBJ_ln2nid(s)) != NID_undef) {
             return OBJ_nid2obj(nid);
+        }
         if (!ossl_isdigit(*s)) {
             ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_OBJECT_NAME);
             return NULL;
@@ -380,13 +422,9 @@ ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name)
 
     /* Work out size of content octets */
     i = a2d_ASN1_OBJECT(NULL, 0, s, -1);
-    if (i <= 0) {
-        /* Don't clear the error */
-        /*
-         * ERR_clear_error();
-         */
+    if (i <= 0)
         return NULL;
-    }
+
     /* Work out total size */
     j = ASN1_object_size(0, i, V_ASN1_OBJECT);
     if (j < 0)
@@ -416,24 +454,23 @@ int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name)
     unsigned long l;
     const unsigned char *p;
     char tbuf[DECIMAL_SIZE(i) + DECIMAL_SIZE(l) + 2];
+    const char *s;
 
     /* Ensure that, at every state, |buf| is NUL-terminated. */
-    if (buf && buf_len > 0)
+    if (buf != NULL && buf_len > 0)
         buf[0] = '\0';
 
-    if ((a == NULL) || (a->data == NULL))
+    if (a == NULL || a->data == NULL)
         return 0;
 
     if (!no_name && (nid = OBJ_obj2nid(a)) != NID_undef) {
-        const char *s;
         s = OBJ_nid2ln(nid);
         if (s == NULL)
             s = OBJ_nid2sn(nid);
-        if (s) {
-            if (buf)
+        if (s != NULL) {
+            if (buf != NULL)
                 OPENSSL_strlcpy(buf, s, buf_len);
-            n = strlen(s);
-            return n;
+            return (int)strlen(s);
         }
     }
 
@@ -545,11 +582,13 @@ int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name)
 
 int OBJ_txt2nid(const char *s)
 {
-    ASN1_OBJECT *obj;
-    int nid;
-    obj = OBJ_txt2obj(s, 0);
-    nid = OBJ_obj2nid(obj);
-    ASN1_OBJECT_free(obj);
+    ASN1_OBJECT *obj = OBJ_txt2obj(s, 0);
+    int nid = NID_undef;
+
+    if (obj != NULL) {
+        nid = OBJ_obj2nid(obj);
+        ASN1_OBJECT_free(obj);
+    }
     return nid;
 }
 
@@ -559,22 +598,25 @@ int OBJ_ln2nid(const char *s)
     const ASN1_OBJECT *oo = &o;
     ADDED_OBJ ad, *adp;
     const unsigned int *op;
-
-    /* Make sure we've loaded config before checking for any "added" objects */
-    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+    int nid = NID_undef;
 
     o.ln = s;
+    op = OBJ_bsearch_ln(&oo, ln_objs, NUM_LN);
+    if (op != NULL)
+        return nid_objs[*op].nid;
+    if (!ossl_obj_read_lock(1)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK);
+        return NID_undef;
+    }
     if (added != NULL) {
         ad.type = ADDED_LNAME;
         ad.obj = &o;
         adp = lh_ADDED_OBJ_retrieve(added, &ad);
         if (adp != NULL)
-            return adp->obj->nid;
+            nid = adp->obj->nid;
     }
-    op = OBJ_bsearch_ln(&oo, ln_objs, NUM_LN);
-    if (op == NULL)
-        return NID_undef;
-    return nid_objs[*op].nid;
+    ossl_obj_unlock(1);
+    return nid;
 }
 
 int OBJ_sn2nid(const char *s)
@@ -583,22 +625,25 @@ int OBJ_sn2nid(const char *s)
     const ASN1_OBJECT *oo = &o;
     ADDED_OBJ ad, *adp;
     const unsigned int *op;
-
-    /* Make sure we've loaded config before checking for any "added" objects */
-    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+    int nid = NID_undef;
 
     o.sn = s;
+    op = OBJ_bsearch_sn(&oo, sn_objs, NUM_SN);
+    if (op != NULL)
+        return nid_objs[*op].nid;
+    if (!ossl_obj_read_lock(1)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK);
+        return NID_undef;
+    }
     if (added != NULL) {
         ad.type = ADDED_SNAME;
         ad.obj = &o;
         adp = lh_ADDED_OBJ_retrieve(added, &ad);
         if (adp != NULL)
-            return adp->obj->nid;
+            nid = adp->obj->nid;
     }
-    op = OBJ_bsearch_sn(&oo, sn_objs, NUM_SN);
-    if (op == NULL)
-        return NID_undef;
-    return nid_objs[*op].nid;
+    ossl_obj_unlock(1);
+    return nid;
 }
 
 const void *OBJ_bsearch_(const void *key, const void *base, int num, int size,
@@ -698,16 +743,21 @@ int OBJ_create(const char *oid, const char *sn, const char *ln)
     if ((sn != NULL && OBJ_sn2nid(sn) != NID_undef)
             || (ln != NULL && OBJ_ln2nid(ln) != NID_undef)) {
         ERR_raise(ERR_LIB_OBJ, OBJ_R_OID_EXISTS);
-        return 0;
+        goto err;
     }
 
     /* Convert numerical OID string to an ASN1_OBJECT structure */
     tmpoid = OBJ_txt2obj(oid, 1);
     if (tmpoid == NULL)
+        goto err;
+
+    if (!ossl_obj_write_lock(1)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
         return 0;
+    }
 
     /* If NID is not NID_undef then object already exists */
-    if (OBJ_obj2nid(tmpoid) != NID_undef) {
+    if (ossl_obj_obj2nid(tmpoid, 0) != NID_undef) {
         ERR_raise(ERR_LIB_OBJ, OBJ_R_OID_EXISTS);
         goto err;
     }
@@ -716,12 +766,13 @@ int OBJ_create(const char *oid, const char *sn, const char *ln)
     tmpoid->sn = (char *)sn;
     tmpoid->ln = (char *)ln;
 
-    ok = OBJ_add_object(tmpoid);
+    ok = ossl_obj_add_object(tmpoid, 0);
 
     tmpoid->sn = NULL;
     tmpoid->ln = NULL;
 
  err:
+    ossl_obj_unlock(1);
     ASN1_OBJECT_free(tmpoid);
     return ok;
 }
@@ -739,3 +790,13 @@ const unsigned char *OBJ_get0_data(const ASN1_OBJECT *obj)
         return NULL;
     return obj->data;
 }
+
+int OBJ_add_object(const ASN1_OBJECT *obj)
+{
+    return ossl_obj_add_object(obj, 1);
+}
+
+int OBJ_obj2nid(const ASN1_OBJECT *a)
+{
+    return ossl_obj_obj2nid(a, 1);
+}
diff --git a/crypto/objects/obj_xref.c b/crypto/objects/obj_xref.c
index da1035112f..3a6ae02bf0 100644
--- a/crypto/objects/obj_xref.c
+++ b/crypto/objects/obj_xref.c
@@ -10,9 +10,11 @@
 #include <openssl/objects.h>
 #include "obj_xref.h"
 #include "internal/nelem.h"
+#include "internal/thread_once.h"
 #include <openssl/err.h>
 
 static STACK_OF(nid_triple) *sig_app, *sigx_app;
+static CRYPTO_RWLOCK *sig_lock;
 
 static int sig_cmp(const nid_triple *a, const nid_triple *b)
 {
@@ -32,62 +34,104 @@ DECLARE_OBJ_BSEARCH_CMP_FN(const nid_triple *, const nid_triple *, sigx);
 static int sigx_cmp(const nid_triple *const *a, const nid_triple *const *b)
 {
     int ret;
+
     ret = (*a)->hash_id - (*b)->hash_id;
-    if (ret)
+    if (ret != 0)
         return ret;
     return (*a)->pkey_id - (*b)->pkey_id;
 }
 
 IMPLEMENT_OBJ_BSEARCH_CMP_FN(const nid_triple *, const nid_triple *, sigx);
 
-int OBJ_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid)
+static CRYPTO_ONCE sig_init = CRYPTO_ONCE_STATIC_INIT;
+
+DEFINE_RUN_ONCE_STATIC(o_sig_init)
+{
+    sig_lock = CRYPTO_THREAD_lock_new();
+    return sig_lock != NULL;
+}
+
+static ossl_inline int obj_sig_init(void)
+{
+    return RUN_ONCE(&sig_init, o_sig_init);
+}
+
+static int ossl_obj_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid,
+                                    int lock)
 {
     nid_triple tmp;
-    const nid_triple *rv = NULL;
-    tmp.sign_id = signid;
+    const nid_triple *rv;
+    int idx;
 
-    if (sig_app != NULL) {
-        int idx = sk_nid_triple_find(sig_app, &tmp);
-        rv = sk_nid_triple_value(sig_app, idx);
-    }
-#ifndef OBJ_XREF_TEST2
+    if (signid == NID_undef)
+        return 0;
+
+    tmp.sign_id = signid;
+    rv = OBJ_bsearch_sig(&tmp, sigoid_srt, OSSL_NELEM(sigoid_srt));
     if (rv == NULL) {
-        rv = OBJ_bsearch_sig(&tmp, sigoid_srt, OSSL_NELEM(sigoid_srt));
+        if (!obj_sig_init())
+            return 0;
+        if (lock && !CRYPTO_THREAD_read_lock(sig_lock)) {
+            ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK);
+            return 0;
+        }
+        if (sig_app != NULL) {
+            idx = sk_nid_triple_find(sig_app, &tmp);
+            if (idx >= 0)
+                rv = sk_nid_triple_value(sig_app, idx);
+        }
+        if (lock)
+            CRYPTO_THREAD_unlock(sig_lock);
+        if (rv == NULL)
+            return 0;
     }
-#endif
-    if (rv == NULL)
-        return 0;
-    if (pdig_nid)
+
+    if (pdig_nid != NULL)
         *pdig_nid = rv->hash_id;
-    if (ppkey_nid)
+    if (ppkey_nid != NULL)
         *ppkey_nid = rv->pkey_id;
     return 1;
 }
 
+int OBJ_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid)
+{
+    return ossl_obj_find_sigid_algs(signid, pdig_nid, ppkey_nid, 1);
+}
+
 int OBJ_find_sigid_by_algs(int *psignid, int dig_nid, int pkey_nid)
 {
     nid_triple tmp;
     const nid_triple *t = &tmp;
-    const nid_triple **rv = NULL;
+    const nid_triple **rv;
+    int idx;
+
+    if (dig_nid == NID_undef || pkey_nid == NID_undef)
+        return 0;
 
     tmp.hash_id = dig_nid;
     tmp.pkey_id = pkey_nid;
 
-    if (sigx_app) {
-        int idx = sk_nid_triple_find(sigx_app, &tmp);
-        if (idx >= 0) {
-            t = sk_nid_triple_value(sigx_app, idx);
-            rv = &t;
-        }
-    }
-#ifndef OBJ_XREF_TEST2
+    rv = OBJ_bsearch_sigx(&t, sigoid_srt_xref, OSSL_NELEM(sigoid_srt_xref));
     if (rv == NULL) {
-        rv = OBJ_bsearch_sigx(&t, sigoid_srt_xref, OSSL_NELEM(sigoid_srt_xref));
+        if (!obj_sig_init())
+            return 0;
+        if (!CRYPTO_THREAD_read_lock(sig_lock)) {
+            ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK);
+            return 0;
+        }
+        if (sigx_app != NULL) {
+            idx = sk_nid_triple_find(sigx_app, &tmp);
+            if (idx >= 0) {
+                t = sk_nid_triple_value(sigx_app, idx);
+                rv = &t;
+            }
+        }
+        CRYPTO_THREAD_unlock(sig_lock);
+        if (rv == NULL)
+            return 0;
     }
-#endif
-    if (rv == NULL)
-        return 0;
-    if (psignid)
+
+    if (psignid != NULL)
         *psignid = (*rv)->sign_id;
     return 1;
 }
@@ -95,14 +139,14 @@ int OBJ_find_sigid_by_algs(int *psignid, int dig_nid, int pkey_nid)
 int OBJ_add_sigid(int signid, int dig_id, int pkey_id)
 {
     nid_triple *ntr;
-    if (sig_app == NULL)
-        sig_app = sk_nid_triple_new(sig_sk_cmp);
-    if (sig_app == NULL)
+    int dnid = NID_undef, pnid = NID_undef, ret = 0;
+
+    if (signid == NID_undef || dig_id == NID_undef || pkey_id == NID_undef)
         return 0;
-    if (sigx_app == NULL)
-        sigx_app = sk_nid_triple_new(sigx_cmp);
-    if (sigx_app == NULL)
+
+    if (!obj_sig_init())
         return 0;
+
     if ((ntr = OPENSSL_malloc(sizeof(*ntr))) == NULL) {
         ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE);
         return 0;
@@ -111,18 +155,49 @@ int OBJ_add_sigid(int signid, int dig_id, int pkey_id)
     ntr->hash_id = dig_id;
     ntr->pkey_id = pkey_id;
 
-    if (!sk_nid_triple_push(sig_app, ntr)) {
+    if (!CRYPTO_THREAD_write_lock(sig_lock)) {
+        ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
         OPENSSL_free(ntr);
         return 0;
     }
 
-    if (!sk_nid_triple_push(sigx_app, ntr))
-        return 0;
+    /* Check that the entry doesn't exist or exists as desired */
+    if (ossl_obj_find_sigid_algs(signid, &dnid, &pnid, 0)) {
+        ret = dnid == dig_id && pnid == pkey_id;
+        goto err;
+    }
+
+    if (sig_app == NULL) {
+        sig_app = sk_nid_triple_new(sig_sk_cmp);
+        if (sig_app == NULL)
+            goto err;
+    }
+    if (sigx_app == NULL) {
+        sigx_app = sk_nid_triple_new(sigx_cmp);
+        if (sigx_app == NULL)
+            goto err;
+    }
+
+    /*
+     * Better might be to find where to insert the element and insert it there.
+     * This would avoid the sorting steps below.
+     */
+    if (!sk_nid_triple_push(sig_app, ntr))
+        goto err;
+    if (!sk_nid_triple_push(sigx_app, ntr)) {
+        ntr = NULL;             /* This is referenced by sig_app still */
+        goto err;
+    }
 
     sk_nid_triple_sort(sig_app);
     sk_nid_triple_sort(sigx_app);
 
-    return 1;
+    ntr = NULL;
+    ret = 1;
+ err:
+    OPENSSL_free(ntr);
+    CRYPTO_THREAD_unlock(sig_lock);
+    return ret;
 }
 
 static void sid_free(nid_triple *tt)
@@ -133,7 +208,9 @@ static void sid_free(nid_triple *tt)
 void OBJ_sigid_free(void)
 {
     sk_nid_triple_pop_free(sig_app, sid_free);
-    sig_app = NULL;
     sk_nid_triple_free(sigx_app);
+    CRYPTO_THREAD_lock_free(sig_lock);
+    sig_app = NULL;
     sigx_app = NULL;
+    sig_lock = NULL;
 }
diff --git a/doc/man3/OBJ_nid2obj.pod b/doc/man3/OBJ_nid2obj.pod
index 58fc94f6dd..2d16cc83cc 100644
--- a/doc/man3/OBJ_nid2obj.pod
+++ b/doc/man3/OBJ_nid2obj.pod
@@ -139,6 +139,8 @@ These functions cannot return B<const> because an B<ASN1_OBJECT> can
 represent both an internal, constant, OID and a dynamically-created one.
 The latter cannot be constant because it needs to be freed after use.
 
+These functions were not thread safe in OpenSSL 3.0 and before.
+
 =head1 RETURN VALUES
 
 OBJ_nid2obj() returns an B<ASN1_OBJECT> structure or B<NULL> is an
@@ -181,10 +183,6 @@ Instead I<buf> must point to a valid buffer and I<buf_len> should
 be set to a positive value. A buffer length of 80 should be more
 than enough to handle any OID encountered in practice.
 
-Neither OBJ_create() nor OBJ_add_sigid() do any locking and are thus not
-thread safe.  Moreover, none of the other functions should be called while
-concurrent calls to these two functions are possible.
-
 =head1 SEE ALSO
 
 L<ERR_get_error(3)>
diff --git a/doc/man7/openssl-threads.pod b/doc/man7/openssl-threads.pod
index 56cc638e1b..7f29a327ac 100644
--- a/doc/man7/openssl-threads.pod
+++ b/doc/man7/openssl-threads.pod
@@ -73,8 +73,8 @@ For implicit global state or singletons, thread-safety depends on the facility.
 The L<CRYPTO_secure_malloc(3)> and related API's have their own lock,
 while L<CRYPTO_malloc(3)> assumes the underlying platform allocation
 will do any necessary locking.
-Some API's, such as L<NCONF_load(3)> and related, or L<OBJ_create(3)>
-do no locking at all; this can be considered a bug.
+Some API's, such as L<NCONF_load(3)> and related do no locking at all;
+this can be considered a bug.
 
 A separate, although related, issue is modifying "factory" objects
 when other objects have been created from that.
diff --git a/doc/man7/provider-base.pod b/doc/man7/provider-base.pod
index d1e26baa11..5af35bf4dc 100644
--- a/doc/man7/provider-base.pod
+++ b/doc/man7/provider-base.pod
@@ -269,7 +269,6 @@ It will treat as success the case where the OID already exists (even if the
 short name I<sn> or long name I<ln> provided as arguments differ from those
 associated with the existing OID, in which case the new names are not
 associated).
-This function is not thread safe.
 
 The core_obj_add_sigid() function registers a new composite signature algorithm
 (I<sign_name>) consisting of an underlying signature algorithm (I<pkey_name>)
@@ -283,7 +282,6 @@ to identify the object. It will treat as success the case where the composite
 signature algorithm already exists (even if registered against a different
 underlying signature or digest algorithm). It returns 1 on success or 0 on
 failure.
-This function is not thread safe.
 
 CRYPTO_malloc(), CRYPTO_zalloc(), CRYPTO_memdup(), CRYPTO_strdup(),
 CRYPTO_strndup(), CRYPTO_free(), CRYPTO_clear_free(),
@@ -613,6 +611,11 @@ or maximum. A -1 indicates that the group should not be used in that protocol.
 
 =back
 
+=head1 NOTES
+
+The core_obj_create() and core_obj_add_sigid() functions were not thread safe
+in OpenSSL 3.0.
+
 =head1 EXAMPLES
 
 This is an example of a simple provider made available as a
diff --git a/include/internal/tsan_assist.h b/include/internal/tsan_assist.h
index f8285b1d85..c67c591e0e 100644
--- a/include/internal/tsan_assist.h
+++ b/include/internal/tsan_assist.h
@@ -56,8 +56,7 @@
 #  define TSAN_QUALIFIER _Atomic
 #  define tsan_load(ptr) atomic_load_explicit((ptr), memory_order_relaxed)
 #  define tsan_store(ptr, val) atomic_store_explicit((ptr), (val), memory_order_relaxed)
-#  define tsan_counter(ptr) atomic_fetch_add_explicit((ptr), 1, memory_order_relaxed)
-#  define tsan_decr(ptr) atomic_fetch_add_explicit((ptr), -1, memory_order_relaxed)
+#  define tsan_add(ptr, n) atomic_fetch_add_explicit((ptr), (n), memory_order_relaxed)
 #  define tsan_ld_acq(ptr) atomic_load_explicit((ptr), memory_order_acquire)
 #  define tsan_st_rel(ptr, val) atomic_store_explicit((ptr), (val), memory_order_release)
 # endif
@@ -69,8 +68,7 @@
 #  define TSAN_QUALIFIER volatile
 #  define tsan_load(ptr) __atomic_load_n((ptr), __ATOMIC_RELAXED)
 #  define tsan_store(ptr, val) __atomic_store_n((ptr), (val), __ATOMIC_RELAXED)
-#  define tsan_counter(ptr) __atomic_fetch_add((ptr), 1, __ATOMIC_RELAXED)
-#  define tsan_decr(ptr) __atomic_fetch_add((ptr), -1, __ATOMIC_RELAXED)
+#  define tsan_add(ptr, n) __atomic_fetch_add((ptr), (n), __ATOMIC_RELAXED)
 #  define tsan_ld_acq(ptr) __atomic_load_n((ptr), __ATOMIC_ACQUIRE)
 #  define tsan_st_rel(ptr, val) __atomic_store_n((ptr), (val), __ATOMIC_RELEASE)
 # endif
@@ -113,13 +111,10 @@
 # pragma intrinsic(_InterlockedExchangeAdd)
 # ifdef _WIN64
 #  pragma intrinsic(_InterlockedExchangeAdd64)
-#  define tsan_counter(ptr) (sizeof(*(ptr)) == 8 ? _InterlockedExchangeAdd64((ptr), 1) \
-                                                 : _InterlockedExchangeAdd((ptr), 1))
-#  define tsan_decr(ptr) (sizeof(*(ptr)) == 8 ? _InterlockedExchangeAdd64((ptr), -1) \
-                                                 : _InterlockedExchangeAdd((ptr), -1))
+#  define tsan_add(ptr, n) (sizeof(*(ptr)) == 8 ? _InterlockedExchangeAdd64((ptr), (n)) \
+                                                : _InterlockedExchangeAdd((ptr), (n)))
 # else
-#  define tsan_counter(ptr) _InterlockedExchangeAdd((ptr), 1)
-#  define tsan_decr(ptr) _InterlockedExchangeAdd((ptr), -1)
+#  define tsan_add(ptr, n) _InterlockedExchangeAdd((ptr), (n))
 # endif
 # if !defined(_ISO_VOLATILE)
 #  define tsan_ld_acq(ptr) (*(ptr))
@@ -133,8 +128,7 @@
 # define TSAN_QUALIFIER volatile
 # define tsan_load(ptr) (*(ptr))
 # define tsan_store(ptr, val) (*(ptr) = (val))
-# define tsan_counter(ptr) ((*(ptr))++)
-# define tsan_decr(ptr) ((*(ptr))--)
+# define tsan_add(ptr, n) (*(ptr) += (n))
 /*
  * Lack of tsan_ld_acq and tsan_ld_rel means that compiler support is not
  * sophisticated enough to support them. Code that relies on them should be
@@ -142,3 +136,7 @@
  */
 
 #endif
+
+#define tsan_counter(ptr) tsan_add((ptr), 1)
+#define tsan_decr(ptr) tsan_add((ptr), -1)
+
diff --git a/test/threadstest.c b/test/threadstest.c
index 3160d9e334..f689676c54 100644
--- a/test/threadstest.c
+++ b/test/threadstest.c
@@ -7,8 +7,13 @@
  * https://www.openssl.org/source/license.html
  */
 
-/* test_multi below tests the thread safety of a deprecated function */
-#define OPENSSL_SUPPRESS_DEPRECATED
+/*
+ * The test_multi_downgrade_shared_pkey function tests the thread safety of a
+ * deprecated function.
+ */
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+# define OPENSSL_SUPPRESS_DEPRECATED
+#endif
 
 #if defined(_WIN32)
 # include <windows.h>
@@ -18,15 +23,39 @@
 #include <openssl/crypto.h>
 #include <openssl/rsa.h>
 #include <openssl/aes.h>
-#include <openssl/rsa.h>
+#include <openssl/err.h>
+#include "internal/tsan_assist.h"
+#include "internal/nelem.h"
 #include "testutil.h"
 #include "threadstest.h"
 
+/* Limit the maximum number of threads */
+#define MAXIMUM_THREADS     3
+
+/* Limit the maximum number of providers loaded into a library context */
+#define MAXIMUM_PROVIDERS   4
+
 static int do_fips = 0;
 static char *privkey;
 static char *config_file = NULL;
 static int multidefault_run = 0;
 
+static const char *default_provider[] = { "default", NULL };
+static const char *fips_provider[] = { "fips", NULL };
+static const char *fips_and_default_providers[] = { "default", "fips", NULL };
+
+/* Grab a globally unique integer value */
+static int get_new_uid(void)
+{
+    /*
+     * Start with a nice large number to avoid potential conflicts when
+     * we generate a new OID.
+     */
+    static TSAN_QUALIFIER int current_uid = 1 << (sizeof(int) * 8 - 2);
+
+    return tsan_counter(&current_uid);
+}
+
 static int test_lock(void)
 {
     CRYPTO_RWLOCK *lock = CRYPTO_THREAD_lock_new();
@@ -207,6 +236,94 @@ static int test_atomic(void)
 
 static OSSL_LIB_CTX *multi_libctx = NULL;
 static int multi_success;
+static OSSL_PROVIDER *multi_provider[MAXIMUM_PROVIDERS + 1];
+static size_t multi_num_threads;
+static thread_t multi_threads[MAXIMUM_THREADS];
+
+static void multi_intialise(void)
+{
+    multi_success = 1;
+    multi_libctx = NULL;
+    multi_num_threads = 0;
+    memset(multi_threads, 0, sizeof(multi_threads));
+    memset(multi_provider, 0, sizeof(multi_provider));
+}
+
+static void thead_teardown_libctx(void)
+{
+    OSSL_PROVIDER **p;
+
+    for (p = multi_provider; *p != NULL; p++)
+        OSSL_PROVIDER_unload(*p);
+    OSSL_LIB_CTX_free(multi_libctx);
+    multi_intialise();
+}
+
+static int thread_setup_libctx(int libctx, const char *providers[])
+{
+    size_t n;
+
+    if (libctx && !TEST_true(test_get_libctx(&multi_libctx, NULL, config_file,
+                                             NULL, NULL)))
+        return 0;
+
+    if (providers != NULL)
+        for (n = 0; providers[n] != NULL; n++)
+            if (!TEST_size_t_lt(n, MAXIMUM_PROVIDERS)
+                || !TEST_ptr(multi_provider[n] = OSSL_PROVIDER_load(multi_libctx,
+                                                                    providers[n]))) {
+                thead_teardown_libctx();
+                return 0;
+            }
+    return 1;
+}
+
+static int teardown_threads(void)
+{
+    size_t i;
+
+    for (i = 0; i < multi_num_threads; i++)
+        if (!TEST_true(wait_for_thread(multi_threads[i])))
+            return 0;
+    return 1;
+}
+
+static int start_threads(size_t n, void (*thread_func)(void))
+{
+    size_t i;
+
+    if (!TEST_size_t_le(multi_num_threads + n, MAXIMUM_THREADS))
+        return 0;
+
+    for (i = 0 ; i < n; i++)
+        if (!TEST_true(run_thread(multi_threads + multi_num_threads++, thread_func)))
+            return 0;
+    return 1;
+}
+
+/* Template multi-threaded test function */
+static int thread_run_test(void (*main_func)(void),
+                           size_t num_threads, void (*thread_func)(void),
+                           int libctx, const char *providers[])
+{
+    int testresult = 0;
+
+    multi_intialise();
+    if (!thread_setup_libctx(libctx, providers)
+            || !start_threads(num_threads, thread_func))
+        goto err;
+
+    if (main_func != NULL)
+        main_func();
+
+    if (!teardown_threads()
+            || !TEST_true(multi_success))
+        goto err;
+    testresult = 1;
+ err:
+    thead_teardown_libctx();
+    return testresult;
+}
 
 static void thread_general_worker(void)
 {
@@ -334,21 +451,6 @@ static void thread_shared_evp_pkey(void)
         multi_success = 0;
 }
 
-static void thread_downgrade_shared_evp_pkey(void)
-{
-#ifndef OPENSSL_NO_DEPRECATED_3_0
-    /*
-     * This test is only relevant for deprecated functions that perform
-     * downgrading
-     */
-    if (EVP_PKEY_get0_RSA(shared_evp_pkey) == NULL)
-        multi_success = 0;
-#else
-    /* Shouldn't ever get here */
-    multi_success = 0;
-#endif
-}
-
 static void thread_provider_load_unload(void)
 {
     OSSL_PROVIDER *deflt = OSSL_PROVIDER_load(multi_libctx, "default");
@@ -360,107 +462,99 @@ static void thread_provider_load_unload(void)
     OSSL_PROVIDER_unload(deflt);
 }
 
-/*
- * Do work in multiple worker threads at the same time.
- * Test 0: General worker, using the default provider
- * Test 1: General worker, using the fips provider
- * Test 2: Simple fetch worker
- * Test 3: Worker downgrading a shared EVP_PKEY
- * Test 4: Worker using a shared EVP_PKEY
- * Test 5: Worker loading and unloading a provider
- */
-static int test_multi(int idx)
+static int test_multi_general_worker_default_provider(void)
 {
-    thread_t thread1, thread2;
-    int testresult = 0;
-    OSSL_PROVIDER *prov = NULL, *prov2 = NULL;
-    void (*worker)(void) = NULL;
-    void (*worker2)(void) = NULL;
-    EVP_MD *sha256 = NULL;
+    return thread_run_test(&thread_general_worker, 2, &thread_general_worker,
+                           1, default_provider);
+}
 
-    if (idx == 1 && !do_fips)
+static int test_multi_general_worker_fips_provider(void)
+{
+    if (!do_fips)
         return TEST_skip("FIPS not supported");
+    return thread_run_test(&thread_general_worker, 2, &thread_general_worker,
+                           1, fips_provider);
+}
 
-#ifdef OPENSSL_NO_DEPRECATED_3_0
-    if (idx == 3)
-        return TEST_skip("Skipping tests for deprected functions");
-#endif
+static int test_multi_fetch_worker(void)
+{
+    return thread_run_test(&thread_multi_simple_fetch,
+                           2, &thread_multi_simple_fetch, 1, default_provider);
+}
 
-    multi_success = 1;
-    if (!TEST_true(test_get_libctx(&multi_libctx, NULL, config_file,
-                                   NULL, NULL)))
-        return 0;
+static int test_multi_shared_pkey_common(void (*worker)(void))
+{
+    int testresult = 0;
 
-    prov = OSSL_PROVIDER_load(multi_libctx, (idx == 1) ? "fips" : "default");
-    if (!TEST_ptr(prov))
+    multi_intialise();
+    if (!thread_setup_libctx(1, do_fips ? fips_and_default_providers
+                                        : default_provider)
+            || !TEST_ptr(shared_evp_pkey = load_pkey_pem(privkey, multi_libctx))
+            || !start_threads(1, &thread_shared_evp_pkey)
+            || !start_threads(1, worker))
         goto err;
 
-    switch (idx) {
-    case 0:
-    case 1:
-        worker = thread_general_worker;
-        break;
-    case 2:
-        worker = thread_multi_simple_fetch;
-        break;
-    case 3:
-        worker2 = thread_downgrade_shared_evp_pkey;
-        /* fall through */
-    case 4:
-        /*
-         * If available we have both the default and fips providers for this
-         * test
-         */
-        if (do_fips
-                && !TEST_ptr(prov2 = OSSL_PROVIDER_load(multi_libctx, "fips")))
-            goto err;
-        if (!TEST_ptr(shared_evp_pkey = load_pkey_pem(privkey, multi_libctx)))
-            goto err;
-        worker = thread_shared_evp_pkey;
-        break;
-    case 5:
-        /*
-         * We ensure we get an md from the default provider, and then unload the
-         * provider. This ensures the provider remains around but in a
-         * deactivated state.
-         */
-        sha256 = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL);
-        OSSL_PROVIDER_unload(prov);
-        prov = NULL;
-        worker = thread_provider_load_unload;
-        break;
-    default:
-        TEST_error("Invalid test index");
-        goto err;
-    }
-    if (worker2 == NULL)
-        worker2 = worker;
+    thread_shared_evp_pkey();
 
-    if (!TEST_true(run_thread(&thread1, worker))
-            || !TEST_true(run_thread(&thread2, worker2)))
+    if (!teardown_threads()
+            || !TEST_true(multi_success))
         goto err;
-
-    worker();
-
     testresult = 1;
+ err:
+    EVP_PKEY_free(shared_evp_pkey);
+    thead_teardown_libctx();
+    return testresult;
+}
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+static void thread_downgrade_shared_evp_pkey(void)
+{
     /*
-     * Don't combine these into one if statement; must wait for both threads.
+     * This test is only relevant for deprecated functions that perform
+     * downgrading
      */
-    if (!TEST_true(wait_for_thread(thread1)))
-        testresult = 0;
-    if (!TEST_true(wait_for_thread(thread2)))
-        testresult = 0;
-    if (!TEST_true(multi_success))
-        testresult = 0;
+    if (EVP_PKEY_get0_RSA(shared_evp_pkey) == NULL)
+        multi_success = 0;
+}
+
+static int test_multi_downgrade_shared_pkey(void)
+{
+    return test_multi_shared_pkey_common(&thread_downgrade_shared_evp_pkey);
+}
+#endif
+
+static int test_multi_shared_pkey(void)
+{
+    return test_multi_shared_pkey_common(&thread_shared_evp_pkey);
+}
+
+static int test_multi_load_unload_provider(void)
+{
+    EVP_MD *sha256 = NULL;
+    OSSL_PROVIDER *prov = NULL;
+    int testresult = 0;
+
+    multi_intialise();
+    if (!thread_setup_libctx(1, NULL)
+            || !TEST_ptr(prov = OSSL_PROVIDER_load(multi_libctx, "default"))
+            || !TEST_ptr(sha256 = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL))
+            || !TEST_true(OSSL_PROVIDER_unload(prov)))
+        goto err;
+    prov = NULL;
+
+    if (!start_threads(2, &thread_provider_load_unload))
+        goto err;
 
+    thread_provider_load_unload();
+
+    if (!teardown_threads()
+            || !TEST_true(multi_success))
+        goto err;
+    testresult = 1;
  err:
-    EVP_MD_free(sha256);
     OSSL_PROVIDER_unload(prov);
-    OSSL_PROVIDER_unload(prov2);
-    OSSL_LIB_CTX_free(multi_libctx);
-    EVP_PKEY_free(shared_evp_pkey);
-    shared_evp_pkey = NULL;
-    multi_libctx = NULL;
+    EVP_MD_free(sha256);
+    thead_teardown_libctx();
     return testresult;
 }
 
@@ -469,21 +563,17 @@ static int test_multi(int idx)
  * run with a thread sanitizer, should crash if the core provider code
  * doesn't synchronize well enough.
  */
-#define MULTI_LOAD_THREADS 3
 static void test_multi_load_worker(void)
 {
     OSSL_PROVIDER *prov;
 
-    (void)TEST_ptr(prov = OSSL_PROVIDER_load(NULL, "default"));
-    (void)TEST_true(OSSL_PROVIDER_unload(prov));
+    if (!TEST_ptr(prov = OSSL_PROVIDER_load(multi_libctx, "default"))
+            || !TEST_true(OSSL_PROVIDER_unload(prov)))
+        multi_success = 0;
 }
 
 static int test_multi_default(void)
 {
-    thread_t thread1, thread2;
-    int testresult = 0;
-    OSSL_PROVIDER *prov = NULL;
-
     /* Avoid running this test twice */
     if (multidefault_run) {
         TEST_skip("multi default test already run");
@@ -491,34 +581,13 @@ static int test_multi_default(void)
     }
     multidefault_run = 1;
 
-    multi_success = 1;
-    multi_libctx = NULL;
-    prov = OSSL_PROVIDER_load(multi_libctx, "default");
-    if (!TEST_ptr(prov))
-        goto err;
-
-    if (!TEST_true(run_thread(&thread1, thread_multi_simple_fetch))
-            || !TEST_true(run_thread(&thread2, thread_multi_simple_fetch)))
-        goto err;
-
-    thread_multi_simple_fetch();
-
-    if (!TEST_true(wait_for_thread(thread1))
-            || !TEST_true(wait_for_thread(thread2))
-            || !TEST_true(multi_success))
-        goto err;
-
-    testresult = 1;
-
- err:
-    OSSL_PROVIDER_unload(prov);
-    return testresult;
+    return thread_run_test(&thread_multi_simple_fetch,
+                           2, &thread_multi_simple_fetch, 0, default_provider);
 }
 
 static int test_multi_load(void)
 {
-    thread_t threads[MULTI_LOAD_THREADS];
-    int i, res = 1;
+    int res = 1;
 
     /* The multidefault test must run prior to this test */
     if (!multidefault_run) {
@@ -526,13 +595,28 @@ static int test_multi_load(void)
         res = test_multi_default();
     }
 
-    for (i = 0; i < MULTI_LOAD_THREADS; i++)
-        (void)TEST_true(run_thread(&threads[i], test_multi_load_worker));
+    return thread_run_test(NULL, 3, &test_multi_load_worker, 0, NULL) && res;
+}
 
-    for (i = 0; i < MULTI_LOAD_THREADS; i++)
-        (void)TEST_true(wait_for_thread(threads[i]));
+static void test_obj_create_one(void)
+{
+    char tids[12], oid[40], sn[30], ln[30];
+    int id = get_new_uid();
+
+    BIO_snprintf(tids, sizeof(tids), "%d", id);
+    BIO_snprintf(oid, sizeof(oid), "1.3.6.1.4.1.16604.%s", tids);
+    BIO_snprintf(sn, sizeof(sn), "short-name-%s", tids);
+    BIO_snprintf(ln, sizeof(ln), "long-name-%s", tids);
+    if (!TEST_true(id = OBJ_create(oid, sn, ln))
+            || !TEST_true(OBJ_add_sigid(id, NID_sha3_256, NID_rsa)))
+        multi_success = 0;
+}
 
-    return res;
+static int test_obj_add(void)
+{
+    return thread_run_test(&test_obj_create_one,
+                           MAXIMUM_THREADS, &test_obj_create_one,
+                           1, default_provider);
 }
 
 typedef enum OPTION_choice {
@@ -589,7 +673,15 @@ int setup_tests(void)
     ADD_TEST(test_thread_local);
     ADD_TEST(test_atomic);
     ADD_TEST(test_multi_load);
-    ADD_ALL_TESTS(test_multi, 6);
+    ADD_TEST(test_multi_general_worker_default_provider);
+    ADD_TEST(test_multi_general_worker_fips_provider);
+    ADD_TEST(test_multi_fetch_worker);
+    ADD_TEST(test_multi_shared_pkey);
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+    ADD_TEST(test_multi_downgrade_shared_pkey);
+#endif
+    ADD_TEST(test_multi_load_unload_provider);
+    ADD_TEST(test_obj_add);
     return 1;
 }
 


More information about the openssl-commits mailing list