Skip to content

Commit 51d184e

Browse files
gh-156166: Fix setting and deleting SSLContext._msg_callback
The setter released the old callback before validating the new value, so a failed assignment or a deletion removed it.
1 parent 8e96dd6 commit 51d184e

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
@@ -5598,6 +5598,18 @@ def msg_cb(conn, direction, version, content_type, msg_type, data):
55985598
with self.assertRaises(TypeError):
55995599
client_context._msg_callback = object()
56005600

5601+
# the attribute of the underlying C type accepts only a callable
5602+
# and cannot be deleted
5603+
descr = _ssl._SSLContext.__dict__['_msg_callback']
5604+
with self.assertRaises(TypeError):
5605+
descr.__set__(client_context, object())
5606+
# a failed assignment does not change the value
5607+
self.assertIs(client_context._msg_callback, msg_cb)
5608+
with self.assertRaisesRegex(AttributeError, 'cannot be deleted'):
5609+
descr.__delete__(client_context)
5610+
# a failed deletion does not change the value
5611+
self.assertIs(client_context._msg_callback, msg_cb)
5612+
56015613
def test_msg_callback_exception(self):
56025614
client_context, server_context, hostname = testing_context()
56035615

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
@@ -102,20 +102,28 @@ _PySSLContext_set_msg_callback(PyObject *op, PyObject *arg,
102102
void *Py_UNUSED(closure))
103103
{
104104
PySSLContext *self = PySSLContext_CAST(op);
105-
Py_CLEAR(self->msg_cb);
105+
if (arg == NULL) {
106+
PyErr_Format(PyExc_AttributeError,
107+
"attribute '_msg_callback' of '%.100s' objects "
108+
"cannot be deleted", Py_TYPE(op)->tp_name);
109+
return -1;
110+
}
111+
if (arg != Py_None && !PyCallable_Check(arg)) {
112+
PyErr_SetString(PyExc_TypeError,
113+
"not a callable object");
114+
return -1;
115+
}
116+
/* Releasing the old callback can run arbitrary code. */
117+
PyObject *old_cb = self->msg_cb;
106118
if (arg == Py_None) {
119+
self->msg_cb = NULL;
107120
SSL_CTX_set_msg_callback(self->ctx, NULL);
108121
}
109122
else {
110-
if (!PyCallable_Check(arg)) {
111-
SSL_CTX_set_msg_callback(self->ctx, NULL);
112-
PyErr_SetString(PyExc_TypeError,
113-
"not a callable object");
114-
return -1;
115-
}
116123
self->msg_cb = Py_NewRef(arg);
117124
SSL_CTX_set_msg_callback(self->ctx, _PySSL_msg_callback);
118125
}
126+
Py_XDECREF(old_cb);
119127
return 0;
120128
}
121129

0 commit comments

Comments
 (0)