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;
}

Reply via email to