OpenSSL и потоки в Perl XS

Я работаю с OpenSSL внутри модуля Perl-XS. У меня есть C-функция, которая вызывает функции OpenSSL-API. Это очень просто для целей тестирования (инициализация openssl, чтение ключа, создание RSA-объекта и его использование, без аргументов). Никаких больших проверок не требуется, адреса и память в порядке.

XS — это стандартные h2xs с -lssl и -lcrypto в Makefile.

void _foo (void)
{
    unsigned char key [3000];
    memset (key, 0, 3000);
    printf ("\ninit=%d", SSL_library_init ());   // init
    FILE *f = fopen ("key.key","r");
    printf ("\nf=%d", f);
    int keysize = fread (key, 1, 3000, f);   // readin
    printf ("\nn=%d",keysize);
    fclose (f);
    printf ("\nkey=%s", key);

    BIO *bio = BIO_new_mem_buf (key, keysize);
    printf ("\nbio=%ld", bio);
    RSA *pk = (RSA *) PEM_read_bio_RSAPrivateKey (bio, NULL, NULL, NULL);
    printf ("\npk=%ld", pk);

    printf ("\nsz=%d" ,RSA_size(pk));  // ***** crash here if in a perl-thread
    printf ("\n\n");
}

Это работает, если я использую чистый C/C++. Также нормально, если он у меня есть в XS-модуле и я использую его в Perl вне потока. Но он падает, если он у меня в Perl и внутри потока.

Теперь я бы сказал, что это потому, что у меня нет обработки потоков. Но если я загляну в другие Perl-модули (например, Crypt::OpenSSL::RSA), я не найду там и специальной обработки потоков.

Я новичок в XS и, возможно, что-то упускаю. Может кто подскажет, спасибо!


person chris01    schedule 24.08.2016    source источник
comment
Что такое чистый C/C++?   -  person too honest for this site    schedule 25.08.2016
comment
Теперь я бы сказал, что это потому, что у меня нет обработки потоков... - Возможно, вы правы. Если я взгляну на другие Perl-модули (например, Crypt::OpenSSL::RSA), я не найду там и специальной обработки потоков... - Вам не нужно следовать им над утесом.   -  person jww    schedule 25.08.2016
comment
Посмотрел C::O::RSA. Если это работает в потоках, то да, у вас тоже должно быть. Он проверяет, являются ли bio или pk NULL, но я предполагаю, что вы убедились, что ни bio, ни pk не являются NULL.   -  person ikegami    schedule 25.08.2016
comment
Я сказал, что адреса и указатели в порядке. Нет проблем с NULL или чем-то еще.   -  person chris01    schedule 25.08.2016


Ответы (2)


printf ("\ninit=%d", SSL_library_init ());   // init

Из документации SSL_library_init:

SSL_library_init() должен вызываться до того, как произойдут какие-либо другие действия. SSL_library_init() не является реентерабельной.

Это означает, что вам лучше вызвать SSL_library_init один раз перед запуском потоков или, по крайней мере, убедиться, что у вас есть правильная блокировка и что эта функция не вызывается несколько раз, в том числе не вызывается из других модулей, которые вы можете использовать. Такие модули, как Net::SSLeay, на самом деле заботятся об этом как видно из исходного кода.

person Steffen Ullrich    schedule 25.08.2016
comment
Теперь я убедился, что инициализация вызывается только один раз, а не внутри потока. Но это не имело значения. Я надеялся, что мне не нужна MUTEX-блокировка потоков OpenSSL. Поэтому я сравнивал с этим другим модулем. - person chris01; 25.08.2016
comment
@chris: с кодом, который вы пока показали, невозможно сказать, что именно происходит и какие взаимодействия могут происходить с Perl или другими модулями. Чтобы получить более качественную помощь, предоставьте полностью работающий минимальный пример, позволяющий воспроизвести вашу проблему, см. stackoverflow.com/help/mcve . - person Steffen Ullrich; 25.08.2016
comment
Да, я согласен. Я добавляю блокировку мьютекса в свой код, и он все еще не работает. Та же проблема. Выложу образец. Спасибо! - person chris01; 25.08.2016

Я узнал ответ. Я не уверен в этом, и мне это не нравится, но это кажется законным.

У меня много openssl-includes в моем xs. Но я не включал ssl.h.

#include <openssl/ssl.h>

Нет ошибок компиляции, нет ошибок компоновщика. Только ошибка сегментации, если я использую его в потоках. Работает даже вне потоков.

Если я включаю h-файл, все работает нормально. Даже без добавления openssl-mutex-thread-handling.

Выяснилось после того, как я попытался сделать свой образец «красивым», чтобы опубликовать его здесь. Это немного странно... Я бы предпочел ошибку компилятора...

Но это объясняет, почему другой модуль работает.

Спасибо за помощь!!

person chris01    schedule 25.08.2016
comment
Если я включаю h-файл, все работает нормально. Даже без добавления openssl-mutex-thread-handling... - это не имеет смысла. - person jww; 26.08.2016
comment
Мой код не работал в потоках. Это было нормально вне потоков. OpenSSL предоставляет функции для обработки многопоточности (openssl.org/docs/man1.0.2 /crypto/threads.html). Я думал, что причина проблемы в том, что я ими не пользовался. Но это все еще не работало после того, как я добавил предложенную обработку. Надеюсь теперь понятно. - person chris01; 26.08.2016
comment
Вы написали в своем вопросе: Это работает, если я использую чистый C/C++. Работало ли это в вашем чистом тесте C/C++ без включения h-файла? - person dolmen; 30.08.2016
comment
Под чистым C я подразумеваю XS. Это на C, и если я пробовал C-код, он работал. Я не пробовал это в C с потоками. Но тогда как XS с perl-потоками. - person chris01; 30.08.2016