Author: Amaury Forgeot d'Arc <amaur...@gmail.com>
Branch: 
Changeset: r44092:007178224553
Date: 2011-05-12 08:06 +0200
http://bitbucket.org/pypy/pypy/changeset/007178224553/

Log:    Some openssl functions update global state, add the necessary locks
        to make them thread-safe.

diff --git a/lib-python/modified-2.7/test/test_ssl.py 
b/lib-python/modified-2.7/test/test_ssl.py
--- a/lib-python/modified-2.7/test/test_ssl.py
+++ b/lib-python/modified-2.7/test/test_ssl.py
@@ -839,6 +839,8 @@
                 c = socket.socket()
                 c.connect((HOST, port))
                 listener_gone.wait()
+                # XXX why is it necessary?
+                test_support.gc_collect()
                 try:
                     ssl_sock = ssl.wrap_socket(c)
                 except IOError:
diff --git a/pypy/module/_ssl/__init__.py b/pypy/module/_ssl/__init__.py
--- a/pypy/module/_ssl/__init__.py
+++ b/pypy/module/_ssl/__init__.py
@@ -31,3 +31,5 @@
     def startup(self, space):
         from pypy.rlib.ropenssl import init_ssl
         init_ssl()
+        from pypy.module._ssl.interp_ssl import setup_ssl_threads
+        setup_ssl_threads()
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -873,3 +873,37 @@
     finally:
         libssl_BIO_free(cert)
     
+# this function is needed to perform locking on shared data
+# structures. (Note that OpenSSL uses a number of global data
+# structures that will be implicitly shared whenever multiple threads
+# use OpenSSL.) Multi-threaded applications will crash at random if
+# it is not set.
+#
+# locking_function() must be able to handle up to CRYPTO_num_locks()
+# different mutex locks. It sets the n-th lock if mode & CRYPTO_LOCK, and
+# releases it otherwise.
+#
+# filename and line are the file number of the function setting the
+# lock. They can be useful for debugging.
+_ssl_locks = []
+
+def _ssl_thread_locking_function(mode, n, filename, line):
+    n = intmask(n)
+    if n < 0 or n >= len(_ssl_locks):
+        return
+
+    if intmask(mode) & CRYPTO_LOCK:
+        _ssl_locks[n].acquire(True)
+    else:
+        _ssl_locks[n].release()
+
+def _ssl_thread_id_function():
+    from pypy.module.thread import ll_thread
+    return ll_thread.get_ident()
+
+def setup_ssl_threads():
+    from pypy.module.thread import ll_thread
+    for i in range(libssl_CRYPTO_num_locks()):
+        _ssl_locks.append(ll_thread.allocate_lock())
+    libssl_CRYPTO_set_locking_callback(_ssl_thread_locking_function)
+    libssl_CRYPTO_set_id_callback(_ssl_thread_id_function)
diff --git a/pypy/rlib/ropenssl.py b/pypy/rlib/ropenssl.py
--- a/pypy/rlib/ropenssl.py
+++ b/pypy/rlib/ropenssl.py
@@ -86,6 +86,8 @@
     NID_subject_alt_name = 
rffi_platform.ConstantInteger("NID_subject_alt_name")
     GEN_DIRNAME = rffi_platform.ConstantInteger("GEN_DIRNAME")
 
+    CRYPTO_LOCK = rffi_platform.ConstantInteger("CRYPTO_LOCK")
+
     # Some structures, with only the fields used in the _ssl module
     X509_name_entry_st = rffi_platform.Struct('struct X509_name_entry_st',
                                               [('set', rffi.INT)])
@@ -142,6 +144,15 @@
 
 ssl_external('SSL_load_error_strings', [], lltype.Void)
 ssl_external('SSL_library_init', [], rffi.INT)
+ssl_external('CRYPTO_num_locks', [], rffi.INT)
+ssl_external('CRYPTO_set_locking_callback',
+             [lltype.Ptr(lltype.FuncType(
+                [rffi.INT, rffi.INT, rffi.CCHARP, rffi.INT], lltype.Void))],
+             lltype.Void)
+ssl_external('CRYPTO_set_id_callback',
+             [lltype.Ptr(lltype.FuncType([], rffi.INT))],
+             lltype.Void)
+             
 if HAVE_OPENSSL_RAND:
     ssl_external('RAND_add', [rffi.CCHARP, rffi.INT, rffi.DOUBLE], lltype.Void)
     ssl_external('RAND_status', [], rffi.INT)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to