gh-113280: Always close socket if SSLSocket creation failed (GH-114659)

Co-authored-by: Thomas Grainger <tagrain@gmail.com>
This commit is contained in:
Serhiy Storchaka 2024-02-04 17:28:07 +02:00 committed by GitHub
parent ecabff98c4
commit 0ea366240b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 81 additions and 67 deletions

View file

@ -994,15 +994,16 @@ class SSLSocket(socket):
if context.check_hostname and not server_hostname:
raise ValueError("check_hostname requires server_hostname")
sock_timeout = sock.gettimeout()
kwargs = dict(
family=sock.family, type=sock.type, proto=sock.proto,
fileno=sock.fileno()
)
self = cls.__new__(cls, **kwargs)
super(SSLSocket, self).__init__(**kwargs)
sock_timeout = sock.gettimeout()
sock.detach()
# Now SSLSocket is responsible for closing the file descriptor.
try:
self._context = context
self._session = session
self._closed = False
@ -1042,10 +1043,6 @@ class SSLSocket(socket):
# Add the SSLError attributes that _ssl.c always adds.
notconn_pre_handshake_data_error.reason = reason
notconn_pre_handshake_data_error.library = None
try:
self.close()
except OSError:
pass
try:
raise notconn_pre_handshake_data_error
finally:
@ -1058,7 +1055,6 @@ class SSLSocket(socket):
self._connected = connected
if connected:
# create the SSL object
try:
self._sslobj = self._context._wrap_socket(
self, server_side, self.server_hostname,
owner=self, session=self._session,
@ -1069,8 +1065,11 @@ class SSLSocket(socket):
# non-blocking
raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
self.do_handshake()
except (OSError, ValueError):
except:
try:
self.close()
except OSError:
pass
raise
return self

View file

@ -2206,6 +2206,7 @@ def _test_get_server_certificate(test, host, port, cert=None):
sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port ,pem))
def _test_get_server_certificate_fail(test, host, port):
with warnings_helper.check_no_resource_warning(test):
try:
pem = ssl.get_server_certificate((host, port), ca_certs=CERTFILE)
except ssl.SSLError as x:
@ -3026,6 +3027,16 @@ class ThreadedTests(unittest.TestCase):
server_hostname="python.example.org") as s:
with self.assertRaises(ssl.CertificateError):
s.connect((HOST, server.port))
with ThreadedEchoServer(context=server_context, chatty=True) as server:
with warnings_helper.check_no_resource_warning(self):
with self.assertRaises(UnicodeError):
context.wrap_socket(socket.socket(),
server_hostname='.pythontest.net')
with ThreadedEchoServer(context=server_context, chatty=True) as server:
with warnings_helper.check_no_resource_warning(self):
with self.assertRaises(UnicodeDecodeError):
context.wrap_socket(socket.socket(),
server_hostname=b'k\xf6nig.idn.pythontest.net')
def test_wrong_cert_tls12(self):
"""Connecting when the server rejects the client's certificate
@ -4983,6 +4994,7 @@ class TestPreHandshakeClose(unittest.TestCase):
self.assertIsNone(wrap_error.library, msg="attr must exist")
finally:
# gh-108342: Explicitly break the reference cycle
with warnings_helper.check_no_resource_warning(self):
wrap_error = None
server = None
@ -5032,7 +5044,8 @@ class TestPreHandshakeClose(unittest.TestCase):
# socket; that fails if the connection is broken. It may seem pointless
# to test this. It serves as an illustration of something that we never
# want to happen... properly not happening.
with self.assertRaises(OSError):
with warnings_helper.check_no_resource_warning(self), \
self.assertRaises(OSError):
connection.request("HEAD", "/test", headers={"Host": "localhost"})
response = connection.getresponse()

View file

@ -0,0 +1,2 @@
Fix a leak of open socket in rare cases when error occurred in
:class:`ssl.SSLSocket` creation.