summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorJeffrey R. Van Voorst <jeff.vanvoorst@gmail.com>2024-09-30 19:43:50 (GMT)
committerGitHub <noreply@github.com>2024-09-30 19:43:50 (GMT)
commit7e7223e18f58ec48fb36a68fb75b5c5b7a45042a (patch)
treeb18a3d1639c87ae6e3d48b5e5cf3b58dee222e34 /Modules
parent1c0bd8bd00287d3bd6830aca87bb14e047192008 (diff)
downloadcpython-7e7223e18f58ec48fb36a68fb75b5c5b7a45042a.zip
cpython-7e7223e18f58ec48fb36a68fb75b5c5b7a45042a.tar.gz
cpython-7e7223e18f58ec48fb36a68fb75b5c5b7a45042a.tar.bz2
gh-116810: fix memory leak in ssl module (GH-123249)
Resolve a memory leak introduced in CPython 3.10's :mod:`ssl` when the :attr:`ssl.SSLSocket.session` property was accessed. Speeds up read and write access to said property by no longer unnecessarily cloning session objects via serialization. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Peter Bierma <zintensitydev@gmail.com> Co-authored-by: Gregory P. Smith <greg@krypto.org> Co-authored-by: Antoine Pitrou <antoine@python.org>
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_ssl.c76
1 files changed, 13 insertions, 63 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 1f5f021..f2d3b33 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -2251,6 +2251,17 @@ PySSL_dealloc(PySSLSocket *self)
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
if (self->ssl) {
+ // If we free the SSL socket object without having called SSL_shutdown,
+ // OpenSSL will invalidate the linked SSL session object. While this
+ // behavior is strictly RFC-compliant, it makes session reuse less
+ // likely and it would also break compatibility with older stdlib
+ // versions (which used an ugly workaround of duplicating the
+ // SSL_SESSION object).
+ // Therefore, we ensure the socket is marked as shutdown in any case.
+ //
+ // See elaborate explanation at
+ // https://github.com/python/cpython/pull/123249#discussion_r1766164530
+ SSL_set_shutdown(self->ssl, SSL_SENT_SHUTDOWN | SSL_get_shutdown(self->ssl));
SSL_free(self->ssl);
}
Py_XDECREF(self->Socket);
@@ -2795,48 +2806,6 @@ _ssl__SSLSocket_verify_client_post_handshake_impl(PySSLSocket *self)
#endif
}
-static SSL_SESSION*
-_ssl_session_dup(SSL_SESSION *session) {
- SSL_SESSION *newsession = NULL;
- int slen;
- unsigned char *senc = NULL, *p;
- const unsigned char *const_p;
-
- if (session == NULL) {
- PyErr_SetString(PyExc_ValueError, "Invalid session");
- goto error;
- }
-
- /* get length */
- slen = i2d_SSL_SESSION(session, NULL);
- if (slen == 0 || slen > 0xFF00) {
- PyErr_SetString(PyExc_ValueError, "i2d() failed");
- goto error;
- }
- if ((senc = PyMem_Malloc(slen)) == NULL) {
- PyErr_NoMemory();
- goto error;
- }
- p = senc;
- if (!i2d_SSL_SESSION(session, &p)) {
- PyErr_SetString(PyExc_ValueError, "i2d() failed");
- goto error;
- }
- const_p = senc;
- newsession = d2i_SSL_SESSION(NULL, &const_p, slen);
- if (newsession == NULL) {
- PyErr_SetString(PyExc_ValueError, "d2i() failed");
- goto error;
- }
- PyMem_Free(senc);
- return newsession;
- error:
- if (senc != NULL) {
- PyMem_Free(senc);
- }
- return NULL;
-}
-
static PyObject *
PySSL_get_session(PySSLSocket *self, void *closure) {
/* get_session can return sessions from a server-side connection,
@@ -2844,15 +2813,6 @@ PySSL_get_session(PySSLSocket *self, void *closure) {
PySSLSession *pysess;
SSL_SESSION *session;
- /* duplicate session as workaround for session bug in OpenSSL 1.1.0,
- * https://github.com/openssl/openssl/issues/1550 */
- session = SSL_get0_session(self->ssl); /* borrowed reference */
- if (session == NULL) {
- Py_RETURN_NONE;
- }
- if ((session = _ssl_session_dup(session)) == NULL) {
- return NULL;
- }
session = SSL_get1_session(self->ssl);
if (session == NULL) {
Py_RETURN_NONE;
@@ -2871,11 +2831,8 @@ PySSL_get_session(PySSLSocket *self, void *closure) {
}
static int PySSL_set_session(PySSLSocket *self, PyObject *value,
- void *closure)
- {
+ void *closure) {
PySSLSession *pysess;
- SSL_SESSION *session;
- int result;
if (!Py_IS_TYPE(value, get_state_sock(self)->PySSLSession_Type)) {
PyErr_SetString(PyExc_TypeError, "Value is not a SSLSession.");
@@ -2898,14 +2855,7 @@ static int PySSL_set_session(PySSLSocket *self, PyObject *value,
"Cannot set session after handshake.");
return -1;
}
- /* duplicate session */
- if ((session = _ssl_session_dup(pysess->session)) == NULL) {
- return -1;
- }
- result = SSL_set_session(self->ssl, session);
- /* free duplicate, SSL_set_session() bumps ref count */
- SSL_SESSION_free(session);
- if (result == 0) {
+ if (SSL_set_session(self->ssl, pysess->session) == 0) {
_setSSLError(get_state_sock(self), NULL, 0, __FILE__, __LINE__);
return -1;
}