Skip to content

Commit e87ba03

Browse files
gh-156166: Fix setting and deleting SSLContext._msg_callback (GH-156167)
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 4f3afba commit e87ba03

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

5231+
# the attribute of the underlying C type accepts only a callable
5232+
# and cannot be deleted
5233+
descr = _ssl._SSLContext.__dict__['_msg_callback']
5234+
with self.assertRaises(TypeError):
5235+
descr.__set__(client_context, object())
5236+
# a failed assignment does not change the value
5237+
self.assertIs(client_context._msg_callback, msg_cb)
5238+
with self.assertRaisesRegex(AttributeError, 'cannot be deleted'):
5239+
descr.__delete__(client_context)
5240+
# a failed deletion does not change the value
5241+
self.assertIs(client_context._msg_callback, msg_cb)
5242+
52315243
def test_msg_callback_tls12(self):
52325244
client_context, server_context, hostname = testing_context()
52335245
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
@@ -95,20 +95,28 @@ _PySSLContext_get_msg_callback(PySSLContext *self, void *c) {
9595

9696
static int
9797
_PySSLContext_set_msg_callback(PySSLContext *self, PyObject *arg, void *c) {
98-
Py_CLEAR(self->msg_cb);
98+
if (arg == NULL) {
99+
PyErr_Format(PyExc_AttributeError,
100+
"attribute '_msg_callback' of '%.100s' objects "
101+
"cannot be deleted", Py_TYPE(self)->tp_name);
102+
return -1;
103+
}
104+
if (arg != Py_None && !PyCallable_Check(arg)) {
105+
PyErr_SetString(PyExc_TypeError,
106+
"not a callable object");
107+
return -1;
108+
}
109+
/* Releasing the old callback can run arbitrary code. */
110+
PyObject *old_cb = self->msg_cb;
99111
if (arg == Py_None) {
112+
self->msg_cb = NULL;
100113
SSL_CTX_set_msg_callback(self->ctx, NULL);
101114
}
102115
else {
103-
if (!PyCallable_Check(arg)) {
104-
SSL_CTX_set_msg_callback(self->ctx, NULL);
105-
PyErr_SetString(PyExc_TypeError,
106-
"not a callable object");
107-
return -1;
108-
}
109116
self->msg_cb = Py_NewRef(arg);
110117
SSL_CTX_set_msg_callback(self->ctx, _PySSL_msg_callback);
111118
}
119+
Py_XDECREF(old_cb);
112120
return 0;
113121
}
114122

0 commit comments

Comments
 (0)