在使用OpenSSL的多线程守护进程中防止内存泄漏

时间:2019-05-10 09:43:17

标签: c multithreading openssl

我的应用程序(多线程守护进程)广泛使用OpenSSL函数,包括SSL_Read,SSL_Write,SSL证书的生成和验证等。 当前已链接到OpenSSL 1.0.2xx 该应用程序提供网络服务,侦听接受连接,还创建与远程对等方的连接。

我遇到的一个典型问题是内存利用率的不断提高。 谷歌搜索,我意识到基本的需要在线程退出之前调用ERR_remove_thread_state(NULL)。 我还意识到需要间歇性地调用ERR_clear_error()。

但是,每次应用程序创建超过1024个并发线程时,我仍然会注意到出现峰值。

以下代码是我到目前为止所学内容的实现。 SSL_thread_setup()基本上是OpenSSL Wiki中的示例的改编版,我只是通过一次调用来调用所有相关事物的初始化。 SSL_pthreads_locking_callback()按照OpenSSL软件包中的示例进行。 SSL_Mem_clean()是一种与ssl中间错误队列相关的内存刷新程序。 SSL_Threads_clean()用于在线程退出之前正确清理它,以防止任何内存泄漏。

#if 0
/*
Actual code to handle initialization of OpenSSL, in a multi-threaded daemon application
*/
#endif

#ifdef __FD_SETSIZE
#undef __FD_SETSIZE
#endif

#ifdef IS_64BIT
#define __FD_SETSIZE 65472
#else
#define __FD_SETSIZE 8192
#endif

#ifndef FD_SETSIZE
#define FD_SETSIZE __FD_SETSIZE
#endif

pthread_mutex_t _ssl_lock = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t *lock_cs;
long *lock_count;

/*
Initialize OpenSSL libraries and setup for multi-threading
*/
void SSL_thread_setup(void)
{
    int i;

    int x = -1;
    char buf[1024] = "";

    lock_cs=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));

    lock_count=(long *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));

    for (i=0; i<CRYPTO_num_locks(); i++) {
            lock_count[i]=0;
            pthread_mutex_init(&(lock_cs[i]),NULL);
    }

    CRYPTO_set_id_callback((unsigned long (*)())SSL_pthreads_thread_id);
    CRYPTO_set_locking_callback((void (*)(int, int, const char *, int)) SSL_pthreads_locking_callback);

    for (x = 0; x < sizeof(buf); x++)
        buf[x] = rand() % 255;

    RAND_seed(buf, sizeof(buf));

    SSL_load_error_strings ();
    SSL_library_init ();
    OpenSSL_add_all_algorithms ();
    OPENSSL_config (NULL);
}

void SSL_pthreads_locking_callback(int mode, int type, const char *file, int line)
{
    if (mode & CRYPTO_LOCK) {
        pthread_mutex_lock(&(lock_cs[type]));
        lock_count[type]++;
    } else
        pthread_mutex_unlock(&(lock_cs[type]));
}

unsigned long SSL_pthreads_thread_id(void)
{
    return (unsigned long)pthread_self();   
}

/*
Intermediate SSL error queue cleaner
*/
void SSL_Mem_clean()
{
    ERR_clear_error();
    ERR_remove_thread_state(NULL);
//  ERR_free_strings(); // crashes
}

/*
Prevent typical "memory leaks" due to multi-threading
*/
void SSL_Threads_clean()
{
#ifndef OPENSSL_NO_ENGINE
//              ENGINE_cleanup();
#endif
        ERR_free_strings();
        ERR_clear_error();
        ERR_remove_thread_state(NULL);

        pthread_mutex_lock(&_ssl_lock);
        FIPS_mode_set(0);// good
//      ENGINE_cleanup(); // crashes

//      EVP_cleanup(); // This causes crashes
//      CRYPTO_cleanup_all_ex_data(); // This causes crashes
        pthread_mutex_unlock(&_ssl_lock);       
}   

这是我的用法:

#if 0
The dummy code below just exemplifies the usage of:
    SSL_thread_setup()
    SSL_Mem_clean()
    SSL_Threads_clean()
#endif

/*
Generic main of a daemon process, that may spawn multiple threads
*/
int main ()
{
    /* do any pre-forking activity */

    /* setup the signal handlers */
    s_signal_setup();

    SSL_thread_setup(); 

    /* initialize threads array */  
    threads_init();

    fpid = fork();

    switch (fpid) {
    case -1:
        fprintf(stderr, "first fork: failed\n");
        exit(EXIT_FAILURE);
        break;
    case 0:
        chdir("/");
        setsid();
        umask(0);

        fprintf(stdout, "%s:%d fork: \n", __FUNCTION__ , __LINE__ );

        fpid = fork();

        switch (fpid) {
        case -1:
            fprintf(stderr, "second fork: failed\n");
            exit(EXIT_FAILURE);
            break;
        case 0:
            umask(022);
            fix_gid();
            fix_uid();

            umask(027);
            /* call the main purpose of the application */
            mainloop();
            break;
        }
    }

    return 0;
}

/*
Called when a thread exits to ensure cleaning to prevent any memory leaks
*/
void thread_release ()
{
    SSL_Threads_clean();
    pthread_exit(0);
}


/*
The actual process of the application
*/
void mainloop ()
{
    void *data_structure;

    while (1) {

        data_structure = get_new_data();

        process_new_data(data_structure);       
    }
}

/*
call to process data in a thread
*/
void process_new_data(void *data_structure)
{
    if (!found_unused_idle_thread (data_structure) ) {

        create_a_detatched_thread((void *) threaded_worker_function, data_structure);
    }

    SSL_Mem_clean();
}

/*
a detatched thread, that may be reused, to process data
*/
void threaded_worker_function(void *data_structure)
{
    while (1) {

        SSL_Mem_clean();

        /* process data_structure */
        some_process(data_structure);

        if (timed_signal_wait_for_new_data)
            continue;

        thread_release();
    }
}

/*
enable caller to pass new data to an unused/idle thread
*/
bool found_unused_idle_thread (data_structure)
{
    if (have_unused_thread) {

        assign_to_waiting_thread(data_structure);
        signal_waiting_thread_to_resume();
        SSL_Mem_clean();
        return true;
    }

    return false;
}

/*
The primary function in a thread that may cause invocation of other functions requiring OpenSSL 
*/
void some_process(data_structure)
{
    /* calls various sub-routines */
    sub_routine();
    SSL_Mem_clean();
}

/*
a child function called that may use OpenSSL functions
*/
void sub_routine()
{
    /* calls further sub-routines, 
    each sub-routine calls SSL_Mem_clean()

    */

    SSL_Mem_clean();
}

我缺少什么吗?还是可以更好?

问题:是否从正确的位置调用SSL_thread_setup()?分叉后应该更应该称呼它吗?

问题:SSL_thread_setup()中的事物顺序正确吗?缺少什么吗?

我敢肯定,这里有一些好的技巧可以发现需要改进的地方。

谢谢。

0 个答案:

没有答案