【问题标题】:Possible data race conditions in OpenSSL libcrypto.1.0.0 CRYPTO_malloc() reported by HelgrindHelgrind 报告的 OpenSSL libcrypto.1.0.0 CRYPTO_malloc() 中可能的数据竞争条件
【发布时间】:2014-03-22 14:01:22
【问题描述】:

我在使用 Helgrind 测试多线程 (pthreads) 应用程序时遇到问题,报告 libcrypto.1.0.0 中的 CRYPTO_malloc() 中可能存在数百个数据竞争。

我已阅读并完全理解所有可用的文档,包括 Linux 网络编程权威指南第 255-259 页和http://www.openssl.org/docs/crypto/threads.html

我的代码正在初始化静态和动态锁结构和回调,并注册线程ID回调:

CRYPTO_THREADID_set_callback(ssl_mitm_thread_id);
CRYPTO_set_dynlock_create_callback(ssl_mitm_dyn_create_lock);
CRYPTO_set_dynlock_lock_callback(ssl_mitm_dyn_locking_callback);
CRYPTO_set_dynlock_destroy_callback(ssl_mitm_dyn_destroy_lock);
ssl_mitm_lock_mutexes = (pthread_mutex_t*)malloc(CRYPTO_num_locks() *
  sizeof(pthread_mutex_t));
for(i = 0; i < CRYPTO_num_locks(); i++) {
  pthread_mutex_init(&ssl_mitm_lock_mutexes[i], NULL);
}
CRYPTO_set_locking_callback(ssl_mitm_locking_callback);

SSL_library_init();
SSL_load_error_strings();
...etc...

我在加锁回调函数中添加了debug语句,openSSL只是调用了已弃用的CRYPTO_set_locking_callback(),并没有使用新的动态加锁接口。

即便如此,它还是调用了旧的静态锁定函数,因此正在尝试线程同步,但 Helgrind 仍然认为存在很多可能的数据竞争。大多数(如果不是全部)都在 CRYPTO_malloc() 中。

那么,这个 Helgrind 是不是过于狂热了,还是它是 OpenSSL/libcrypto 中的一个错误?

例如

==20093== ----------------------------------------------------------------
==20093==
==20093== Possible data race during write of size 4 at 0x63BA368 by thread #3
==20093== Locks held: none
==20093==    at 0x604F0FE: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==20093==    by 0x57F4186: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20093==    by 0x57D20C4: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20093==    by 0x57D62E1: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20093==    by 0x57DF0B6: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
[snip] (SSL_connect() called)
==20093==
==20093== This conflicts with a previous write of size 4 by thread #6
==20093== Locks held: none
==20093==    at 0x604F0FE: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==20093==    by 0x57E1673: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20093==    by 0x57DF359: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
[snip] (SSL_connect() called)
==20093==
==20093== ----------------------------------------------------------------

从 SSL_read、SSL_write、SSL_connect 和 SSL_accept 等调用的 CRYPTO_malloc() 中还有其他数据竞争示例。

【问题讨论】:

    标签: c multithreading openssl


    【解决方案1】:

    下面片段中的最后一行是 Helgrind 抱怨的代码行:

    void *CRYPTO_malloc(int num, const char *file, int line)
            {
            void *ret = NULL;
    
            if (num <= 0) return NULL;
    
            allow_customize = 0;
    

    allow_customize 全局变量在静态初始化中被初始化为1。此值用于确保在调用分配例程本身之后不调用 malloc 自定义函数(CRYPTO_set_mem... 和变体)。这个设置为值0 是在锁之外完成的。从技术上讲,如果一个线程尝试调用CRYPTO_...alloc 函数而另一个线程尝试为CRYPTO 设置自定义函数,则存在竞争条件,但这可以被视为应用程序错误。

    【讨论】:

    • 我注意到 OpenSSL 文档中的注释,但是,我正在按照您的建议设置 lock_function 回调并初始化锁,但 Helgrind 仍然抱怨可能的数据竞争。此外,正如我在原始帖子中所说,将调试 printfs 添加到locking_function 清楚地显示OpenSSL 调用它,但Helgrind 仍然抱怨。
    • 也许我应该发布到 openssl-dev?
    • @Dan:我之前没看到你提供了静态锁定回调。
    • (但是您编辑了答案,因此我的原始评论没有意义)。谢谢,分配 allow_customize = 0 似乎确实解释了绝大多数可能的数据竞争。但是,在共享 SSL_CTX 中调用 SSL_accept() 之后还有一些比赛。我曾认为您可以在多个线程之间安全地共享一个公共 SSL_CTX,也许它是另一个 (if(initialised = 0)) 谓词,其语义与此 allow_customize 变量相似,可以忽略,但最好考虑到所有内容。
    • 更新:我认为 SSL_accept/SSL_connect 调用链中的竞赛引用了两个常见的 SSL_CTX(一个用于客户端,一个用于服务器端)可能是由于 SSL CTX 会话缓存,我在创建后尝试禁用它使用 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF) 的新 SSL_CTX,但仍然会发生两个 Value4 竞赛,也许它们只是从未使用过的引用计数,所以我想我现在回家了。
    【解决方案2】:

    这里是CRYPTO_malloc 的定义:

    $ grep -R CRYPTO_malloc *
    ...
    crypto/mem.c:void *CRYPTO_malloc_locked(int num, const char *file, int line)
    crypto/mem.c:void *CRYPTO_malloc(int num, const char *file, int line)
    ...
    

    CRYPTO_malloc_locked 试图将页面保留在内存中(而不是写入swap);而不是线程安全的分配器。

    CRYPTO_malloc 就是这样做的:

    void *CRYPTO_malloc(int num, const char *file, int line)
        {
        void *ret = NULL;
    
        ...
        ret = malloc_ex_func(num,file,line);
    
        ...
        return ret;
        }
    

    malloc_ex_funcCRYPTO_set_mem_functions 中设置为default_malloc_ex。和default_malloc_ex。但我似乎找不到default_malloc_ex 的实现。

    但是,CRYPTO_malloc_init 具有以下内容:

    #define CRYPTO_malloc_init()    CRYPTO_set_mem_functions( \
        malloc, realloc, free)
    

    所以看起来 C 运行时的 malloc 正在被使用。

    您使用的 OpenSSL 版本是否支持线程?您可以通过包含&lt;openssl/openssl.con.h&gt; 进行测试,并测试是否定义了OPENSSL_THREADS

    这也应该有效:

    $ cat /usr/local/ssl/include/openssl/opensslconf.h | grep -B 1 -A 1 -i thread
    
    #ifndef OPENSSL_THREADS
    # define OPENSSL_THREADS
    #endif
    

    【讨论】:

    • 谢谢,我正在检查 OPENSSL_THREADS 的定义,如果没有定义则拒绝编译。
    • 我认为 jxh 可能是对的。我没有查allow_customizeCRYPTO_malloc的朋友,所以我没有把他们叫出来。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 2017-07-17
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    相关资源
    最近更新 更多