2

I have a huge code (its my school project) where I use Openssl. Everything was working perfeclty, util I decided I will go multithreaded. I chose openmp as my threading environment, since its very simple and pretty easy to learn (at least the basis I need are easy).

My whole code looks like this:

struct mystr[10];
omp_set_num_threads(10);
#pragma omp parallel 
{
    mystr[omp_get_thread_num()] = call_fun_which_uses_openssl();
}
CRYPTO_cleanup_all_ex_data();

My call_fun_which_uses_openssl function uses low level openssls api functions (MD4((unsigned char*)&string, strlen(string), (unsigned char*)&digest); instead of MD4_CTX ctx etc). My function uses RSA, DSA, ... and does not have access to any global variables, every variable it uses is declared inside call_fun_which_uses_openssl, so doing a multithreading like I did should guarantee that those variables remain private. Although, I sometimes have seg faults with this code. I read that CRYPTO_cleanup_all_ex_data is not thread-safe (but I need it because of memory leaks) but I use it outside the parallel region, right? When I remove openmp calls everything works every time so there must be an issue with multithreading and openssl. Any ideas how to solve it?

In my opinion it should work, beacuse a threaded call of call_fun_which_uses_openssl creates its own private variables for every thread and there should be no problem ... Please, help :)

jww
  • 97,681
  • 90
  • 411
  • 885
mirx
  • 606
  • 1
  • 9
  • 21
  • 1
    I'm not sure that you can assume that OpenSSL is thread-safe by default. If it uses any kind of internal state, concurrently accessing the state from different OpenMP threads is likely to cause issues. See [this](http://www.openssl.org/docs/crypto/threads.html). I might also recommend converting that parallel block to a for loop with `#pragma omp parallel for`, and removing the `omp_set_num_threads`. This way, you can remove the OpenMP behavior by just commenting out the `#pragma` line to test for expected behavior. I also think this better suits the way OpenMP is meant to distribute work. – millinon Apr 07 '14 at 07:43
  • @millinon: will take a look at it for a while, but is there any easy way to use `openmp` and `openssl`? my project is really huge and I wont start it again and rewrite everything just to use `pthreads` instead of elegant `openmp` calls. – mirx Apr 07 '14 at 07:46
  • I don't understand the line `struct mystr[10]`. Where is the the type? – Z boson Apr 07 '14 at 07:51
  • 2
    I haven't done it, but it looks like you can stick with OpenMP by using OpenSSL's threading functions to register function callbacks for mutual exclusion. Read about it [here](https://www.openssl.org/docs/crypto/threads.html). – millinon Apr 07 '14 at 07:53
  • 1
    @Zboson it looks like we can assume that the type of the struct is whatever the function returns, which was left unspecified. – millinon Apr 07 '14 at 07:54
  • @millinon: yes, ur right. I havent pasted the whole code, if it necessary I can paste a little more;) Do you have any clue how to start with `openmp` and `openssl`, any working, even tiny working code example? Site you provided its a bit complicated :( Will be vewry grateful, just to have something to start with :) – mirx Apr 07 '14 at 09:48

1 Answers1

4

Take a look at the example openssl/crypto/threads/mttest.c in the OpenSSL source code tree. Basically, you have to provide two callback functions:

  • one that implements locking and unlocking and
  • one that returns a unique thread ID.

Both are easily implemented using OpenMP:

omp_lock_t *locks;

// Locking callback
void openmp_locking_callback(int mode, int type, char *file, int line)
{
  if (mode & CRYPTO_LOCK)
  {
    omp_set_lock(&locks[type]);
  }
  else
  {
    omp_unset_lock(&locks[type]);
  }
}

// Thread ID callback
unsigned long openmp_thread_id(void)
{
  return (unsigned long)omp_get_thread_num();
}

Before you start, the locks have to be initialised and callbacks set:

void openmp_thread_setup(void)
{
  int i;

  locks = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(omp_lock_t));
  for (i=0; i<CRYPTO_num_locks(); i++)
  {
    omp_init_lock(&locks[i]);
  }

  CRYPTO_set_id_callback((unsigned long (*)())openmp_thread_id);
  CRYPTO_set_locking_callback((void (*)())openmp_locking_callback);
}

void openmp_thread_cleanup(void)
{
  int i;

  CRYPTO_set_id_callback(NULL);
  CRYPTO_set_locking_callback(NULL);
  for (i=0; i<CRYPTO_num_locks(); i++)
    omp_destroy_lock(&locks[i]);
  OPENSSL_free(locks);
}

You should call openmp_thread_setup() once in your program somewhere before the first parallel region and you should call openmp_thread_cleanup() once after the last parallel region:

// Program start

openmp_thread_setup();

#pragma omp parallel
{
   // Parallel OpenSSL calls are now data race free
}

#pragma omp parallel
{
   // Parallel OpenSSL calls are now data race free
}

#pragma omp parallel
{
   // Parallel OpenSSL calls are now data race free
}

openmp_thread_cleanup();

// Program should end now
Hristo Iliev
  • 72,659
  • 12
  • 135
  • 186
  • Wow, you're amazing! And thats simply it? I dont need anything else, just the code you provided? (Im at school now but test it as soon as I get back home :)) – mirx Apr 07 '14 at 10:41
  • 1
    That's just a translation of the code from the sample to using OpenMP primitives. I have no idea if it actually works or not, but since it is in the OpenSSL source tree, it should probably work. – Hristo Iliev Apr 07 '14 at 10:42
  • tested your code - there should be `lock` instead of `lock_cs` in `OPENSSL_free(lock_cs);` but who cares - it seems to work, thank you! :) Will test it a little more in few days, do you mind if I ask further questions here if I will have any? cheers! – mirx Apr 09 '14 at 10:08
  • Just open new questions if they are sufficiently different. (I've overlooked the `OPENSSL_free(lock_cs);` statement while translating the code sample...) – Hristo Iliev Apr 09 '14 at 11:02