Skip to content

Commit e810e1a

Browse files
[3.14] gh-156166: Fix setting and deleting SSLContext._msg_callback (GH-156167) (GH-156174)
The setter released the old callback before validating the new value, so a failed assignment or a deletion removed it. (cherry picked from commit 67f4d53)
1 parent 31e1476 commit e810e1a

3 files changed

Lines changed: 30 additions & 7 deletions

File tree

Lib/test/test_ssl.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5266,6 +5266,18 @@ def msg_cb(conn, direction, version, content_type, msg_type, data):
52665266
with self.assertRaises(TypeError):
52675267
client_context._msg_callback = object()
52685268

5269+
# the attribute of the underlying C type accepts only a callable
5270+
# and cannot be deleted
5271+
descr = _ssl._SSLContext.__dict__['_msg_callback']
5272+
with self.assertRaises(TypeError):
5273+
descr.__set__(client_context, object())
5274+
# a failed assignment does not change the value
5275+
self.assertIs(client_context._msg_callback, msg_cb)
5276+
with self.assertRaisesRegex(AttributeError, 'cannot be deleted'):
5277+
descr.__delete__(client_context)
5278+
# a failed deletion does not change the value
5279+
self.assertIs(client_context._msg_callback, msg_cb)
5280+
52695281
def test_msg_callback_tls12(self):
52705282
client_context, server_context, hostname = testing_context()
52715283
client_context.maximum_version = ssl.TLSVersion.TLSv1_2
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`ssl`: A failed assignment or deletion of the ``_msg_callback``
2+
attribute of :class:`ssl.SSLContext` no longer removes the current callback.
3+
Deleting it now raises :exc:`AttributeError` instead of :exc:`TypeError`.

Modules/_ssl/debughelpers.c

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,28 @@ _PySSLContext_set_msg_callback(PyObject *op, PyObject *arg,
100100
void *Py_UNUSED(closure))
101101
{
102102
PySSLContext *self = PySSLContext_CAST(op);
103-
Py_CLEAR(self->msg_cb);
103+
if (arg == NULL) {
104+
PyErr_Format(PyExc_AttributeError,
105+
"attribute '_msg_callback' of '%.100s' objects "
106+
"cannot be deleted", Py_TYPE(op)->tp_name);
107+
return -1;
108+
}
109+
if (arg != Py_None && !PyCallable_Check(arg)) {
110+
PyErr_SetString(PyExc_TypeError,
111+
"not a callable object");
112+
return -1;
113+
}
114+
/* Releasing the old callback can run arbitrary code. */
115+
PyObject *old_cb = self->msg_cb;
104116
if (arg == Py_None) {
117+
self->msg_cb = NULL;
105118
SSL_CTX_set_msg_callback(self->ctx, NULL);
106119
}
107120
else {
108-
if (!PyCallable_Check(arg)) {
109-
SSL_CTX_set_msg_callback(self->ctx, NULL);
110-
PyErr_SetString(PyExc_TypeError,
111-
"not a callable object");
112-
return -1;
113-
}
114121
self->msg_cb = Py_NewRef(arg);
115122
SSL_CTX_set_msg_callback(self->ctx, _PySSL_msg_callback);
116123
}
124+
Py_XDECREF(old_cb);
117125
return 0;
118126
}
119127

0 commit comments

Comments
 (0)