Forráskód Böngészése

Merge branch 'nss_27451'

Nick Mathewson 5 éve
szülő
commit
e43ae24e7d

+ 13 - 2
src/core/mainloop/connection.c

@@ -638,8 +638,19 @@ connection_free_minimal(connection_t *conn)
 
   if (connection_speaks_cells(conn)) {
     or_connection_t *or_conn = TO_OR_CONN(conn);
-    tor_tls_free(or_conn->tls);
-    or_conn->tls = NULL;
+    if (or_conn->tls) {
+      if (! SOCKET_OK(conn->s)) {
+        /* The socket has been closed by somebody else; we must tell the
+         * TLS object not to close it. */
+        tor_tls_release_socket(or_conn->tls);
+      } else {
+        /* The tor_tls_free() call below will close the socket; we must tell
+         * the code below not to close it a second time. */
+        conn->s = TOR_INVALID_SOCKET;
+      }
+      tor_tls_free(or_conn->tls);
+      or_conn->tls = NULL;
+    }
     or_handshake_state_free(or_conn->handshake_state);
     or_conn->handshake_state = NULL;
     tor_free(or_conn->nickname);

+ 1 - 0
src/lib/tls/tortls.h

@@ -94,6 +94,7 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
                                       void (*cb)(tor_tls_t *, void *arg),
                                       void *arg);
 int tor_tls_is_server(tor_tls_t *tls);
+void tor_tls_release_socket(tor_tls_t *tls);
 void tor_tls_free_(tor_tls_t *tls);
 #define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls))
 int tor_tls_peer_has_cert(tor_tls_t *tls);

+ 37 - 0
src/lib/tls/tortls_nss.c

@@ -414,6 +414,43 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
   /* We don't support renegotiation-based TLS with NSS. */
 }
 
+/**
+ * Tell the TLS library that the underlying socket for <b>tls</b> has been
+ * closed, and the library should not attempt to free that socket itself.
+ */
+void
+tor_tls_release_socket(tor_tls_t *tls)
+{
+  if (! tls)
+    return;
+
+  /* NSS doesn't have the equivalent of BIO_NO_CLOSE.  If you replace the
+   * fd with something that's invalid, it causes a memory leak in PR_Close.
+   *
+   * If there were a way to put the PRFileDesc into the CLOSED state, that
+   * would prevent it from closing its fd -- but there doesn't seem to be a
+   * supported way to do that either.
+   *
+   * So instead: we make a new sacrificial socket, and replace the original
+   * socket with that one. This seems to be the best we can do, until we
+   * redesign the mainloop code enough to make this function unnecessary.
+   */
+  tor_socket_t sock =
+    tor_open_socket_nonblocking(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (!sock) {
+    log_warn(LD_NET, "Out of sockets when trying to shut down an NSS "
+             "connection");
+    return;
+  }
+
+  PRFileDesc *tcp = PR_GetIdentitiesLayer(tls->ssl, PR_NSPR_IO_LAYER);
+  if (BUG(! tcp)) {
+    return;
+  }
+
+  PR_ChangeFileDescNativeHandle(tcp, sock);
+}
+
 void
 tor_tls_impl_free_(tor_tls_impl_t *tls)
 {

+ 23 - 1
src/lib/tls/tortls_openssl.c

@@ -1048,7 +1048,7 @@ tor_tls_new(tor_socket_t sock, int isServer)
     goto err;
   }
   result->socket = sock;
-  bio = BIO_new_socket(sock, BIO_NOCLOSE);
+  bio = BIO_new_socket(sock, BIO_CLOSE);
   if (! bio) {
     tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO");
 #ifdef SSL_set_tlsext_host_name
@@ -1154,6 +1154,28 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
 #endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */
 }
 
+/**
+ * Tell the TLS library that the underlying socket for <b>tls</b> has been
+ * closed, and the library should not attempt to free that socket itself.
+ */
+void
+tor_tls_release_socket(tor_tls_t *tls)
+{
+  if (! tls)
+    return;
+
+  BIO *rbio, *wbio;
+  rbio = SSL_get_rbio(tls->ssl);
+  wbio = SSL_get_wbio(tls->ssl);
+
+  if (rbio) {
+    BIO_set_close(rbio, BIO_NOCLOSE);
+  }
+  if (wbio && wbio != rbio) {
+    BIO_set_close(wbio, BIO_NOCLOSE);
+  }
+}
+
 void
 tor_tls_impl_free_(tor_tls_impl_t *ssl)
 {