5

I've been reading about the requirement that if OpenSSL is used in a multi-threaded application, you have to register a thread identification function (and also a mutex creation function) with OpenSSL.

On Linux, according to the example provided by OpenSSL, a thread is normally identified by registering a function like this:

static unsigned long id_function(void){
    return (unsigned long)pthread_self();
}

pthread_self() returns a pthread_t, and this works on Linux since pthread_t is just a typedef of unsigned long.

On Windows pthreads, FreeBSD, and other operating systems, pthread_t is a struct, with the following structure:

struct {
    void * p;                   /* Pointer to actual object */
    unsigned int x;             /* Extra information - reuse count etc */ 
}

This can't be simply cast to an unsigned long, and when I try to do so, it throws a compile error. I tried taking the void *p and casting that to an unsigned long, on the theory that the memory pointer should be consistent and unique across threads, but this just causes my program to crash a lot.

What can I register with OpenSSL as the thread identification function when using Windows pthreads or FreeBSD or any of the other operating systems like this?

Also, as an additional question:
Does anyone know if this also needs to be done if OpenSSL is compiled into and used with QT, and if so how to register QThreads with OpenSSL? Surprisingly, I can't seem to find the answer in QT's documentation.

Nantucket
  • 1,647
  • 3
  • 14
  • 25

3 Answers3

6

I will just put this code here. It is not panacea, as it doesn't deal with FreeBSD, but it is helpful in most cases when all you need is to support Windows and and say Debian. Of course, the clean solution assumes usage of CRYPTO_THREADID_* family introduced recently. (to give an idea, it has a CRYPTO_THREADID_cmp callback, which can be mapped to pthread_equal)

#include <pthread.h>
#include <openssl/err.h>

#if defined(WIN32)
    #define MUTEX_TYPE            HANDLE
    #define MUTEX_SETUP(x)        (x) = CreateMutex(NULL, FALSE, NULL)
    #define MUTEX_CLEANUP(x)      CloseHandle(x)
    #define MUTEX_LOCK(x)         WaitForSingleObject((x), INFINITE)
    #define MUTEX_UNLOCK(x)       ReleaseMutex(x)
    #define THREAD_ID             GetCurrentThreadId()
#else
    #define MUTEX_TYPE            pthread_mutex_t
    #define MUTEX_SETUP(x)        pthread_mutex_init(&(x), NULL)
    #define MUTEX_CLEANUP(x)      pthread_mutex_destroy(&(x))
    #define MUTEX_LOCK(x)         pthread_mutex_lock(&(x))
    #define MUTEX_UNLOCK(x)       pthread_mutex_unlock(&(x))
    #define THREAD_ID             pthread_self()
#endif

/* This array will store all of the mutexes available to OpenSSL. */ 
static MUTEX_TYPE *mutex_buf=NULL;

static void locking_function(int mode, int n, const char * file, int line)
{
    if (mode & CRYPTO_LOCK)
        MUTEX_LOCK(mutex_buf[n]);
    else
        MUTEX_UNLOCK(mutex_buf[n]);
}

static unsigned long id_function(void)
{
    return ((unsigned long)THREAD_ID);
}

int thread_setup(void)
{
    int i;

    mutex_buf = malloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
    if (!mutex_buf)
        return 0;
    for (i = 0;  i < CRYPTO_num_locks(  );  i++)
        MUTEX_SETUP(mutex_buf[i]);
    CRYPTO_set_id_callback(id_function);
    CRYPTO_set_locking_callback(locking_function);
    return 1;
}

int thread_cleanup(void)
{
    int i;
    if (!mutex_buf)
        return 0;
    CRYPTO_set_id_callback(NULL);
    CRYPTO_set_locking_callback(NULL);
    for (i = 0;  i < CRYPTO_num_locks(  );  i++)
        MUTEX_CLEANUP(mutex_buf[i]);
    free(mutex_buf);
    mutex_buf = NULL;
    return 1;
}
Tosha
  • 998
  • 1
  • 10
  • 22
  • Right now I am working to enable using of multiple threads with my OpenSSL app. and I whould like to understand a bit more about the set_id callback: in the openSSL docs is written that if we dont implemnt this function, then a default implementation is used - do you know what is the default imn windows? is it diffrent from you implementation? and do yu know somthing about the dynamic locks callbacks - do we have to implemnt it too? thanks! – RRR Oct 11 '12 at 13:52
  • @RRR I second all your questions. Dynamic locks seem pretty new in the API, and most of the threading examples with OpenSSL on the internet are outdated. Looks like it should still work to enable backwards compatibility, but dynlocks might add better performance - that's my hunch. – Tosha Oct 11 '12 at 18:15
1

I only can answer the Qt part. Use QThread::currentThreadId(), or even QThread::currentThread() as the pointer value should be unique.

Dr. Snoopy
  • 55,122
  • 7
  • 121
  • 140
  • I don't think currentThreadId() would work based on the warnings in the documentation. For currentThread, are you suggesting to cast the memory pointer into a long? Also, I take it this means that you have to do all this stuff even in QT applications? – Nantucket Aug 05 '10 at 18:11
  • Yes, i'm suggesting that. It's ugly, but i don't see any other way. – Dr. Snoopy Aug 05 '10 at 18:13
1

From the OpenSSL doc you linked:

threadid_func(CRYPTO_THREADID *id) is needed to record the currently-executing thread's identifier into id. The implementation of this callback should not fill in id directly, but should use CRYPTO_THREADID_set_numeric() if thread IDs are numeric, or CRYPTO_THREADID_set_pointer() if they are pointer-based. If the application does not register such a callback using CRYPTO_THREADID_set_callback(), then a default implementation is used - on Windows and BeOS this uses the system's default thread identifying APIs, and on all other platforms it uses the address of errno. The latter is satisfactory for thread-safety if and only if the platform has a thread-local error number facility.

As shown providing your own ID is really only useful if you can provide a better ID than OpenSSL's default implementation.

The only fail-safe way to provide IDs, when you don't know whether pthread_t is a pointer or an integer, is to maintain your own per-thread IDs stored as a thread-local value.

jowo
  • 1,981
  • 17
  • 20