【问题标题】:OpenSSL Session Reuse Multithread CrashOpenSSL 会话重用多线程崩溃
【发布时间】:2013-05-31 19:34:49
【问题描述】:

我有一个嵌入式应用程序,它使用 OpenSSL v0.9.7e 执行客户端 ssl 事务。我一直在使用与子线程共享的全局会话指针。它监控的每个 COM 端口都有一个线程。它在大多数情况下都有效。但是,每隔一段时间(每 500 个左右的事务)它就会在 SSL_connect() 内部崩溃。我做了一些探索,它似乎正在自行删除会话。因此,我怀疑其中一个线程在执行此操作时处于事务的中间。如果引用计数> 0,我会认为它不会删除它。

有谁知道是否可以通过全局共享会话来做到这一点?我是否应该每个线程(COM 端口)使用一个会话。这是代码。为简洁起见,我省略了所有错误检查。

    int send_ssl_post(){
  BIO *bio;
  SSL *ssl;
  int ret = -1;
  int sockfd;
  int sslRet;

  ssl = NULL;

  g_ctx = setup_client_ctx( );
  ssl = SSL_new( g_ctx );
  SSL_set_session( ssl, g_session );
  sockfd = tcpConnect( url, true );
  bio = BIO_new_socket( sockfd, BIO_NOCLOSE );
  SSL_set_bio( ssl, bio, bio ); // SSL_set_bio cannot fail
  sslRet = SSL_connect( ssl );

  // free the session. We may change sessions below
  if ( g_session != NULL ) {
     SSL_SESSION_free( g_session );
  }

  // SSL write
  ret = SSL_write( ssl, data, strlen( data ) );

  ret = SSL_read( ssl, resp, respSize ); // time-out logic should be in

  g_session = SSL_get1_session( ssl );

  if ( ssl != NULL ) {
     SSL_shutdown( ssl );
     SSL_free( ssl );
  }

  if ( sockfd > 0 ){
     close( sockfd ); 
  }

  ERR_remove_state( 0 ); // free the memory that did not freed (buggy in this SSL version)
  return 0;
}

我也有两个thead锁定回调函数:

static void locking_function( int mode, int n, const char * file, int line ) {
   if ( mode & CRYPTO_LOCK ){
      //logger( DEBUG, "CRYPTO Lock file: %s, line: %d, n: %d", file, line, n );
      pthread_mutex_lock( &ssl_mutex_array[n] );
   } else {
      //logger( DEBUG, "CRYPTO Unlock file: %s, line: %d, n: %d", file, line, n );
      pthread_mutex_unlock( &ssl_mutex_array[n] );
   }
}

static unsigned long id_function( void ) {
   //logger( DEBUG, "CRYPTO Id function");
   return ((unsigned long) pthread_self());
}

int setupSSLThreadLock( void ) {
   int i;

   //logger( DEBUG, "setupSSLThreadLock with %d number of locks", CRYPTO_num_locks() );
   ssl_mutex_array = OPENSSL_malloc( CRYPTO_num_locks( ) * sizeof (pthread_mutex_t) );

   if ( !ssl_mutex_array ){
      return 0;
   }

   for ( i = 0; i < CRYPTO_num_locks( ); i++ ){
      pthread_mutex_init( &ssl_mutex_array[i], NULL );
   }
   CRYPTO_set_id_callback( id_function );
   CRYPTO_set_locking_callback( locking_function );
   return 1;
}

int cleanupSSLThreadLock( void ) {
   int i;

   if ( ssl_mutex_array == NULL ){
      return 0;
   }
   CRYPTO_set_id_callback( NULL );
   CRYPTO_set_locking_callback( NULL );
   for ( i = 0; i < CRYPTO_num_locks( ); i++ ){
      pthread_mutex_destroy( &ssl_mutex_array[i] );
   }
   OPENSSL_free( ssl_mutex_array );
   ssl_mutex_array = NULL;
   return 1;
}

任何帮助将不胜感激。

【问题讨论】:

    标签: c linux openssl


    【解决方案1】:

    如果send_ssl_post() 由两个线程同时运行,你就有了比赛。考虑函数的这两个部分:

      SSL_set_session( ssl, g_session );
    

      // free the session. We may change sessions below
      if ( g_session != NULL ) {
         SSL_SESSION_free( g_session );
      }
    

    当一个线程在全局会话上调用SSL_SESSION_free() 和其他一些线程试图使用它来设置会话之间存在竞争。如果空闲先发生,则尝试使用它的线程将访问已释放的内存。

    您可以通过互斥来解决这场比赛。一个围绕SSL_set_session() 电话。

    acquire_session_lock();
    if ( g_session != NULL ) {
        SSL_set_session( ssl, g_session );
    }
    release_session_lock();
    

    SSL_SESSION_free() 周围的其他电话:

    acquire_session_lock();
    if ( g_session != NULL ) {
        SSL_SESSION_free( g_session );
        g_session = NULL;
    }
    release_session_lock();
    

    【讨论】:

    • 会话方案使用引用计数。我可能不正确地递增/递减(设置/释放),但是,只要我不递减太多次,应该没问题。在我看来,OpenSSL 经常删除 SSL_connect() 中的会话。那是它崩溃的时候。我不知道为什么它会删除它。但是,如果在我尝试控制引用计数时它确实自行删除了会话,那么就会出现严重问题。我一定是错过了什么。
    • SSL 服务器可以拒绝使用客户端提供的会话,从而强制进行完全握手。发生这种情况时,SSL_connect 会删除旧会话。
    • 那么,底线是什么?会话是空闲的并且在错误的地方接听电话还是线程不可能以这种方式共享会话。我认为在全局会话指针上设置互斥锁不是一个好主意。与会话重用相比,它可能会导致更大的延迟。
    • 会话重用从 SSL 握手中删除了一个 RTT 和一些加密计算时间。这至少是毫秒级的,除非您只在非常高速的本地网络上执行 SSL(或在同一台机器上通过环回)。在典型的 GHz 处理器上,锁争用将在微秒级。
    • 很抱歉问了这么多后续问题。但是,问题似乎是会话在 SSL_set_session() 和 SSL_connect() 之间的某处被删除。在我看来,仅将其锁定在 SSL_set_session 并不能解决问题。如果你也在 SSL_connect 周围加了一个锁,并且你有连接问题,你会阻塞其他线程,这对于这个应用程序是不可接受的。
    猜你喜欢
    • 1970-01-01
    • 2014-05-16
    • 2012-02-15
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多