[3.13] gh-156166: Fix setting and deleting SSLContext._msg_callback (GH-156167) (GH-156175)

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/3d937eb1f9ce0693624b6d06fee8597ed7622b8d
commit: 3d937eb1f9ce0693624b6d06fee8597ed7622b8d
branch: 3.13
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-21T16:48:12+03:00
summary:

[3.13] gh-156166: Fix setting and deleting SSLContext._msg_callback (GH-156167) (GH-156175)

The setter released the old callback before validating the new value,
so a failed assignment or a deletion removed it.

(cherry picked from commit 67f4d53425d4f7df559b4a0ba5bfb66e795854c7)

files:
A Misc/NEWS.d/next/Library/2026-08-21-13-30-00.gh-issue-156166.Xv8pQm.rst
M Lib/test/test_ssl.py
M Modules/_ssl/debughelpers.c

diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index aacc912b684e5c..1cca904e647e95 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -5228,6 +5228,18 @@ def msg_cb(conn, direction, version, content_type, msg_type, data):
         with self.assertRaises(TypeError):
             client_context._msg_callback = object()
 
+        # the attribute of the underlying C type accepts only a callable
+        # and cannot be deleted
+        descr = _ssl._SSLContext.__dict__['_msg_callback']
+        with self.assertRaises(TypeError):
+            descr.__set__(client_context, object())
+        # a failed assignment does not change the value
+        self.assertIs(client_context._msg_callback, msg_cb)
+        with self.assertRaisesRegex(AttributeError, 'cannot be deleted'):
+            descr.__delete__(client_context)
+        # a failed deletion does not change the value
+        self.assertIs(client_context._msg_callback, msg_cb)
+
     def test_msg_callback_tls12(self):
         client_context, server_context, hostname = testing_context()
         client_context.maximum_version = ssl.TLSVersion.TLSv1_2
diff --git a/Misc/NEWS.d/next/Library/2026-08-21-13-30-00.gh-issue-156166.Xv8pQm.rst b/Misc/NEWS.d/next/Library/2026-08-21-13-30-00.gh-issue-156166.Xv8pQm.rst
new file mode 100644
index 00000000000000..2417479d333e3e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-21-13-30-00.gh-issue-156166.Xv8pQm.rst
@@ -0,0 +1,3 @@
+:mod:`ssl`: A failed assignment or deletion of the ``_msg_callback``
+attribute of :class:`ssl.SSLContext` no longer removes the current callback.
+Deleting it now raises :exc:`AttributeError` instead of :exc:`TypeError`.
diff --git a/Modules/_ssl/debughelpers.c b/Modules/_ssl/debughelpers.c
index 9861f3b46ac005..40f653f4746d3d 100644
--- a/Modules/_ssl/debughelpers.c
+++ b/Modules/_ssl/debughelpers.c
@@ -95,20 +95,28 @@ _PySSLContext_get_msg_callback(PySSLContext *self, void *c) {
 
 static int
 _PySSLContext_set_msg_callback(PySSLContext *self, PyObject *arg, void *c) {
-    Py_CLEAR(self->msg_cb);
+    if (arg == NULL) {
+        PyErr_Format(PyExc_AttributeError,
+                     "attribute '_msg_callback' of '%.100s' objects "
+                     "cannot be deleted", Py_TYPE(self)->tp_name);
+        return -1;
+    }
+    if (arg != Py_None && !PyCallable_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "not a callable object");
+        return -1;
+    }
+    /* Releasing the old callback can run arbitrary code. */
+    PyObject *old_cb = self->msg_cb;
     if (arg == Py_None) {
+        self->msg_cb = NULL;
         SSL_CTX_set_msg_callback(self->ctx, NULL);
     }
     else {
-        if (!PyCallable_Check(arg)) {
-            SSL_CTX_set_msg_callback(self->ctx, NULL);
-            PyErr_SetString(PyExc_TypeError,
-                            "not a callable object");
-            return -1;
-        }
         self->msg_cb = Py_NewRef(arg);
         SSL_CTX_set_msg_callback(self->ctx, _PySSL_msg_callback);
     }
+    Py_XDECREF(old_cb);
     return 0;
 }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.