Skip to content

gh-146207: Add support for OpenSSL 4.0.0 alpha1#146217

Open
vstinner wants to merge 8 commits intopython:mainfrom
vstinner:openssl4
Open

gh-146207: Add support for OpenSSL 4.0.0 alpha1#146217
vstinner wants to merge 8 commits intopython:mainfrom
vstinner:openssl4

Conversation

@vstinner
Copy link
Member

@vstinner vstinner commented Mar 20, 2026

OpenSSL 4.0.0 alpha1 no longer defines the symbols:

  • SSLv3_method
  • TLSv1_method
  • TLSv1_1_method
  • TLSv1_2_method

OpenSSL 4.0.0 alpha1 no longer defines the symbols:

* SSLv3_method
* TLSv1_method
* TLSv1_1_method
* TLSv1_2_method
@vstinner
Copy link
Member Author

I didn't test my own change on OpenSSL 4.0.0 alpha1, I only asked @heitbaum to test my change.

@zware
Copy link
Member

zware commented Mar 20, 2026

@vstinner This patch (should, probably :) ) be enough to get you a test in CI, even if we don't want to merge it yet:

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2fa2ab768dc..40feaef01ce 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -275,6 +275,7 @@ jobs:
           - { name: openssl, version: 3.4.4 }
           - { name: openssl, version: 3.5.5 }
           - { name: openssl, version: 3.6.1 }
+          - { name: openssl, version: 4.0.0-alpha1 }
           ## AWS-LC
           - { name: aws-lc, version: 1.68.0 }
     env:

@vstinner
Copy link
Member Author

I applied @zware's patch: Tests / Ubuntu SSL tests (ubuntu-24.04, openssl, 4.0.0-alpha1) job is currently running.

@vstinner This patch (should, probably :) ) be enough to get you a test in CI, even if we don't want to merge it yet

Yeah, I will revert this patch once we get the openssl, 4.0.0-alpha1 CI job result.

@zware
Copy link
Member

zware commented Mar 20, 2026

Not as simple as hoped :(. I can reproduce the CI failure locally, though; there's a missing symlink (or openssl is just looking in the wrong place) for lib -> lib64 in the openssl install directory.

With that, I get these failures:

2 tests failed:                   
    test_ssl test_urllib2_localnet
                                               
43 tests OK.
                                               
0:00:23 load avg: 3.07 Re-running 2 failed tests in verbose mode in subprocesses
0:00:23 load avg: 3.07 Run 2 tests in parallel using 2 worker processes
0:00:24 load avg: 3.07 [1/2/1] test_ssl failed (1 failure)
Re-running test_ssl in verbose mode (matching: test_openssl_version)
test_ssl: testing with 'OpenSSL 4.0.0-alpha1 10 Mar 2026' (4, 0, 0, 0, 0)
          under 'Linux-6.19.7-200.fc43.x86_64-x86_64-with-glibc2.42'                                                                                                                         
          HAS_SNI = True   
          OP_ALL = 0x80000050
          OP_NO_TLSv1_1 = 0x10000000
test_openssl_version (test.test_ssl.BasicSocketTests.test_openssl_version) ... FAIL
                                               
======================================================================
FAIL: test_openssl_version (test.test_ssl.BasicSocketTests.test_openssl_version)
----------------------------------------------------------------------
Traceback (most recent call last):                                                                                                                                                           
  File "/path/to/cpython/bump_multissl_awslc/Lib/test/test_ssl.py", line 590, in test_openssl_version
    self.assertLess(n, 0x40000000)         
    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^                                                                                                                                                           
AssertionError: 1073741824 not less than 1073741824     
                                                                                              
----------------------------------------------------------------------                                                                                                                       
Ran 1 test in 0.007s                      
FAILED (failures=1)                                                                                                                                                                          
test test_ssl failed
0:00:24 load avg: 3.07 [2/2/2] test_urllib2_localnet failed (2 errors)                                                                                                                       
Re-running test_urllib2_localnet in verbose mode (matching: test_https, test_https_sni)
test_https (test.test_urllib2_localnet.TestUrlopen.test_https) ... ERROR
stopping HTTPS server                                                                         
joining HTTPS thread                 
test_https_sni (test.test_urllib2_localnet.TestUrlopen.test_https_sni) ... ERROR
stopping HTTPS server            
joining HTTPS thread     
                                               
======================================================================
ERROR: test_https (test.test_urllib2_localnet.TestUrlopen.test_https)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/path/to/cpython/bump_multissl_awslc/Lib/test/test_urllib2_localnet.py", line 569, in test_https
    data = self.urlopen("https://localhost:%s/bizarre" % handler.port, context=context)
  File "/path/to/cpython/bump_multissl_awslc/Lib/test/test_urllib2_localnet.py", line 473, in urlopen
    l.extend(f.readlines(200))
             ~~~~~~~~~~~^^^^^
  File "/path/to/cpython/bump_multissl_awslc/Lib/http/client.py", line 714, in readline
    result = self.fp.readline(limit)
  File "/path/to/cpython/bump_multissl_awslc/Lib/socket.py", line 734, in readinto
    return self._sock.recv_into(b)
           ~~~~~~~~~~~~~~~~~~~~^^^
  File "/path/to/cpython/bump_multissl_awslc/Lib/ssl.py", line 1355, in recv_into
    return self.read(nbytes, buffer)
           ~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/path/to/cpython/bump_multissl_awslc/Lib/ssl.py", line 1154, in read
    return self._sslobj.read(len, buffer)
           ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
ssl.SSLError: A failure in the SSL library occurred (_ssl.c:2977)

======================================================================
ERROR: test_https_sni (test.test_urllib2_localnet.TestUrlopen.test_https_sni)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/path/to/cpython/bump_multissl_awslc/Lib/test/test_urllib2_localnet.py", line 585, in test_https_sni
    self.urlopen("https://localhost:%s" % handler.port, context=context)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/cpython/bump_multissl_awslc/Lib/test/test_urllib2_localnet.py", line 473, in urlopen
    l.extend(f.readlines(200))
             ~~~~~~~~~~~^^^^^
  File "/path/to/cpython/bump_multissl_awslc/Lib/http/client.py", line 714, in readline
    result = self.fp.readline(limit)
  File "/path/to/cpython/bump_multissl_awslc/Lib/socket.py", line 734, in readinto
    return self._sock.recv_into(b)
           ~~~~~~~~~~~~~~~~~~~~^^^
  File "/path/to/cpython/bump_multissl_awslc/Lib/ssl.py", line 1355, in recv_into
    return self.read(nbytes, buffer)
           ~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/path/to/cpython/bump_multissl_awslc/Lib/ssl.py", line 1154, in read
    return self._sslobj.read(len, buffer)
           ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
ssl.SSLError: A failure in the SSL library occurred (_ssl.c:2977)

----------------------------------------------------------------------
Ran 2 tests in 0.122s

FAILED (errors=2)
test test_urllib2_localnet failed
2 tests failed again:
    test_ssl test_urllib2_localnet

@vstinner vstinner marked this pull request as draft March 20, 2026 16:42
@vstinner
Copy link
Member Author

I tested multissltests.py using:

python3 Tools/ssl/multissltests.py --steps=library --base-directory "$PWD/multissl" --openssl '4.0.0-alpha1' --system Linux
./configure --with-pydebug --with-openssl=$PWD/multissl/openssl/4.0.0-alpha1/
make

I fixed a few more issues, but make fails with OPENSSL_sk_set_thunks missing symbol:

[ERROR] _ssl failed to import: /home/vstinner/python/main/build/lib.linux-x86_64-3.15/_ssl.cpython-315d-x86_64-linux-gnu.so: undefined symbol: OPENSSL_sk_set_thunks
[ERROR] _ssl (/home/vstinner/python/main/build/lib.linux-x86_64-3.15/_ssl.cpython-315d-x86_64-linux-gnu.so) is missing

@vstinner
Copy link
Member Author

there's a missing symlink (or openssl is just looking in the wrong place) for lib -> lib64 in the openssl install directory.

I fixed that with a5152be.

@heitbaum
Copy link
Contributor

Tested results in #146207 (comment)

@vstinner vstinner marked this pull request as ready for review March 21, 2026 09:50
@vstinner vstinner added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes labels Mar 21, 2026
@vstinner
Copy link
Member Author

@picnixz @gpshead @zware: Would you mind to review this change?

I reverted the change to run "Tests / Ubuntu SSL tests (ubuntu-24.04, openssl, 4.0.0-alpha1)" CI job. I don't think that we should test alpha versions in our CI.

There are still two test_urllib2_localnet failures that I failed to fix. I cannot test OpenSSL 4 locally (on Fedora), I get an error about the OPENSSL_sk_set_thunks symbol (the CI wasn't impacted by this issue). IMO this PR is good enough for a first step to support OpenSSL 4.

Also, I think that we should backport the change to Python 3.13 and 3.14 to also prepare these branches to OpenSSL 4.

@picnixz
Copy link
Member

picnixz commented Mar 21, 2026

I am not very fond of adding support in stable branches for an alpha version for now as I do not know if/how the ABI could change.

I do not mind having a presupport in 3.15 so that we can get feedback during our beta but I will take a look at what is planned in OpenSSL once I am at home.

#error Unsupported OpenSSL version
#endif

#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it possible to retain those constants using the OpenSSL compatibility features or are these constants entirely gone?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 4 methods are gone but I cannot find OPENSSL_NO_ macro in the public header files:

$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep SSLv3_method
$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep TLSv1_method
$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep TLSv1_1_method
$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep TLSv1_2_method
$ grep OPENSSL_NO_SSL3 multissl/openssl/4.0.0-alpha1/include/openssl/ -R
$ grep OPENSSL_NO_TLS1 multissl/openssl/4.0.0-alpha1/include/openssl/ -R
$ grep OPENSSL_NO_TLS1_1 multissl/openssl/4.0.0-alpha1/include/openssl/ -R
$ grep OPENSSL_NO_TLS1_2 multissl/openssl/4.0.0-alpha1/include/openssl/ -R

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think more about it, I think those macros are defined by your build configuration and the generated headers. I never remember which macro is defined explicitly through other macros and which macros are defined dynamically after the configure step

# define OPENSSL_NO_TLS1
# define OPENSSL_NO_TLS1_1
# define OPENSSL_NO_TLS1_2
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot comment on the lines above this block but we need to check if mnemonics changed in 4.0 as well and generate dedicated data. I would still generate dedicated data just because the major version was bumped.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect that OpenSSL will still change before the OpenSSL 4.0 final release. Can we wait for the final release before generating the "ssl data" header file?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we can.

def _post_install(self):
if self.version.startswith("3."):
if self.version.startswith(("3.", "4.")):
self._post_install_3xx()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the future, it is better to have two methods for post install, one for 3.x and one for 4.x

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the script does the same operations on OpenSSL 3 and OpenSSL 4. Is it worth it to have two methods which do the same operations?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say yes as we do not know what will be needed with 4.x (I do not mind having the 4xx call the 3xx with a comment saying that for now it is identical).

I will give you an answer about it once I looked at the OpenSSL roadmap so ok for leaving it that way for now.

vstinner and others added 2 commits March 21, 2026 11:58
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Copy link
Member Author

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the PR to define also the _METHOD macros:

+#  define OPENSSL_NO_SSL3_METHOD
+#  define OPENSSL_NO_TLS1_METHOD
+#  define OPENSSL_NO_TLS1_1_METHOD
+#  define OPENSSL_NO_TLS1_2_METHOD

#error Unsupported OpenSSL version
#endif

#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 4 methods are gone but I cannot find OPENSSL_NO_ macro in the public header files:

$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep SSLv3_method
$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep TLSv1_method
$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep TLSv1_1_method
$ nm multissl/openssl/4.0.0-alpha1/lib64/libssl.so.4 |grep TLSv1_2_method
$ grep OPENSSL_NO_SSL3 multissl/openssl/4.0.0-alpha1/include/openssl/ -R
$ grep OPENSSL_NO_TLS1 multissl/openssl/4.0.0-alpha1/include/openssl/ -R
$ grep OPENSSL_NO_TLS1_1 multissl/openssl/4.0.0-alpha1/include/openssl/ -R
$ grep OPENSSL_NO_TLS1_2 multissl/openssl/4.0.0-alpha1/include/openssl/ -R

# define OPENSSL_NO_TLS1
# define OPENSSL_NO_TLS1_1
# define OPENSSL_NO_TLS1_2
#endif
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect that OpenSSL will still change before the OpenSSL 4.0 final release. Can we wait for the final release before generating the "ssl data" header file?

def _post_install(self):
if self.version.startswith("3."):
if self.version.startswith(("3.", "4.")):
self._post_install_3xx()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the script does the same operations on OpenSSL 3 and OpenSSL 4. Is it worth it to have two methods which do the same operations?

@picnixz
Copy link
Member

picnixz commented Mar 21, 2026

According to the manpage, for the OPENSSL_sk_set_thunks(), the function is public by necessity but is actually an internal function and should not be used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants