Recently, I got reports about a Java-based GUI client using JavaHL randomly crashing the VM. The problem was narrowed down to two RA sessions to an https:// URL being opened simultaneously in two separate threads (using different pools and different instances of callbacks etc., so it's not wrong usage of our libraries).
I managed to reproduce this with a small C program (attached), and narrowed this down further to the way Serf initializes the OpenSSL library: it calls (or rather, called) SSL_library_init without ensuring that this happened in a single-threaded context. This manifested as SSL_CTX_new returning a NULL context, with the following error: 53003:error:140A90A1:SSL routines:func(169):reason(161):/SourceCache/OpenSSL098/OpenSSL098-47.2/src/ssl/ssl_lib.c:1540: The problem is not platform-specific, and reproduces with serf-1.3.2. Earlier today, Bert committed a fix for this to Serf trunk in r2263. I rebuild Subversion and the test program with that, and the crash goes away. I also confirmed the fix with a JavaHL test case. IMO, the way the test program uses our libraries is valid. So I'd like to request that Bert's fix is back-ported to serf-1.x and a new release (serf-1.3.3?) made available. -- Brane -- Branko Čibej | Director of Subversion WANdisco // Non-Stop Data e. br...@wandisco.com
#include <apr_general.h> #include <apr_pools.h> #include <apr_thread_proc.h> #include <svn_error.h> #include <svn_ra.h> #include <svn_pools.h> #include <svn_cmdline.h> /* Replace this with real URL, username and password */ static const char url[] = "https://svn.apache.org/repos/asf"; static const char username[] = "jrandom"; static const char password[] = "rayjandom"; static svn_error_t * get_latest_revision(apr_pool_t *pool) { svn_ra_session_t *session; svn_ra_callbacks2_t *cbtable; svn_revnum_t latest_revnum; SVN_ERR(svn_ra_create_callbacks(&cbtable, pool)); SVN_ERR(svn_cmdline_create_auth_baton(&cbtable->auth_baton, TRUE /* non_interactive */, username, password, NULL, TRUE /* no_auth_cache */, TRUE /* trust_server_cert */, NULL, NULL, NULL, pool)); SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL, pool)); return svn_ra_get_latest_revnum(session, &latest_revnum, pool); } struct thread_baton { apr_pool_t *thread_pool; const char *threadid; }; static void* thread_func(apr_thread_t *thread, void *data) { struct thread_baton *tb = data; svn_error_t *err = get_latest_revision(tb->thread_pool); if (err) svn_handle_error2(err, stderr, FALSE, tb->threadid); return NULL; } static apr_thread_t* create_thread(const char *threadid, apr_pool_t *root_pool) { apr_thread_t *thread; apr_threadattr_t *tattr; apr_pool_t *thread_pool = svn_pool_create(root_pool); struct thread_baton *tb = apr_pcalloc(thread_pool, sizeof(*tb)); tb->thread_pool = thread_pool; tb->threadid = threadid; apr_threadattr_create(&tattr, thread_pool); apr_threadattr_detach_set(tattr, 0); apr_thread_create(&thread, tattr, thread_func, tb, thread_pool); return thread; } int main(void) { apr_thread_t *t1, *t2; svn_error_t *err; apr_status_t status; apr_pool_t *pool; if (apr_initialize() != APR_SUCCESS) { fprintf(stderr, "apr_initialize() failed.\n"); exit(1); } pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE)); err = svn_ra_initialize(pool); if (err) svn_handle_error2(err, stderr, TRUE, "parallel-ra-ssl-test: "); t1 = create_thread("parallel-ra-ssl-test T1: ", pool); t2 = create_thread("parallel-ra-ssl-test T2: ", pool); apr_thread_join(&status, t1); apr_thread_join(&status, t2); svn_pool_destroy(pool); apr_terminate(); return 0; }