From 801a41c7c927ea310808a0f0bfdfa881f8fa5209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:26:55 +0100 Subject: [PATCH 1/8] fix UBSan failures for `PySSLContext` --- Modules/_ssl.c | 35 ++++++++++++++++++++++------------- Modules/_ssl/debughelpers.c | 18 ++++++++++++++---- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 85e917fbbb7093..87fd1cd2ce67ea 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -312,6 +312,8 @@ typedef struct { #endif } PySSLContext; +#define PySSLContext_CAST(op) ((PySSLContext *)(op)) + typedef struct { int ssl; /* last seen error from SSL */ int c; /* last seen error from libc */ @@ -3278,8 +3280,9 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) } static int -context_traverse(PySSLContext *self, visitproc visit, void *arg) +context_traverse(PyObject *op, visitproc visit, void *arg) { + PySSLContext *self = PySSLContext_CAST(op); Py_VISIT(self->set_sni_cb); Py_VISIT(self->msg_cb); Py_VISIT(Py_TYPE(self)); @@ -3287,8 +3290,9 @@ context_traverse(PySSLContext *self, visitproc visit, void *arg) } static int -context_clear(PySSLContext *self) +context_clear(PyObject *op) { + PySSLContext *self = PySSLContext_CAST(op); Py_CLEAR(self->set_sni_cb); Py_CLEAR(self->msg_cb); Py_CLEAR(self->keylog_filename); @@ -3306,15 +3310,16 @@ context_clear(PySSLContext *self) } static void -context_dealloc(PySSLContext *self) +context_dealloc(PyObject *op) { + PySSLContext *self = PySSLContext_CAST(op); PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - context_clear(self); + (void)context_clear(op); SSL_CTX_free(self->ctx); PyMem_FREE(self->alpn_protocols); - Py_TYPE(self)->tp_free(self); + tp->tp_free(self); Py_DECREF(tp); } @@ -3908,7 +3913,9 @@ _ssl__SSLContext_check_hostname_set_impl(PySSLContext *self, PyObject *value) } static PyObject * -get_post_handshake_auth(PySSLContext *self, void *c) { +get_post_handshake_auth(PyObject *op, void *Py_UNUSED(closure)) +{ + PySSLContext *self = PySSLContext_CAST(op); #if defined(PySSL_HAVE_POST_HS_AUTH) return PyBool_FromLong(self->post_handshake_auth); #else @@ -3918,7 +3925,9 @@ get_post_handshake_auth(PySSLContext *self, void *c) { #if defined(PySSL_HAVE_POST_HS_AUTH) static int -set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { +set_post_handshake_auth(PyObject *op, PyObject *arg, void *Py_UNUSED(closure)) +{ + PySSLContext *self = PySSLContext_CAST(op); if (arg == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; @@ -5197,18 +5206,18 @@ static PyGetSetDef context_getsetlist[] = { _SSL__SSLCONTEXT__HOST_FLAGS_GETSETDEF _SSL__SSLCONTEXT_MINIMUM_VERSION_GETSETDEF _SSL__SSLCONTEXT_MAXIMUM_VERSION_GETSETDEF - {"keylog_filename", (getter) _PySSLContext_get_keylog_filename, - (setter) _PySSLContext_set_keylog_filename, NULL}, - {"_msg_callback", (getter) _PySSLContext_get_msg_callback, - (setter) _PySSLContext_set_msg_callback, NULL}, + {"keylog_filename", _PySSLContext_get_keylog_filename, + _PySSLContext_set_keylog_filename, NULL}, + {"_msg_callback", _PySSLContext_get_msg_callback, + _PySSLContext_set_msg_callback, NULL}, _SSL__SSLCONTEXT_SNI_CALLBACK_GETSETDEF #if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3) _SSL__SSLCONTEXT_NUM_TICKETS_GETSETDEF #endif _SSL__SSLCONTEXT_OPTIONS_GETSETDEF - {"post_handshake_auth", (getter) get_post_handshake_auth, + {"post_handshake_auth", get_post_handshake_auth, #if defined(PySSL_HAVE_POST_HS_AUTH) - (setter) set_post_handshake_auth, + set_post_handshake_auth, #else NULL, #endif diff --git a/Modules/_ssl/debughelpers.c b/Modules/_ssl/debughelpers.c index 318c045a0eec3c..e4ff8b6e255adb 100644 --- a/Modules/_ssl/debughelpers.c +++ b/Modules/_ssl/debughelpers.c @@ -85,7 +85,9 @@ _PySSL_msg_callback(int write_p, int version, int content_type, static PyObject * -_PySSLContext_get_msg_callback(PySSLContext *self, void *c) { +_PySSLContext_get_msg_callback(PyObject *op, void *Py_UNUSED(closure)) +{ + PySSLContext *self = PySSLContext_CAST(op); if (self->msg_cb != NULL) { return Py_NewRef(self->msg_cb); } else { @@ -94,7 +96,10 @@ _PySSLContext_get_msg_callback(PySSLContext *self, void *c) { } static int -_PySSLContext_set_msg_callback(PySSLContext *self, PyObject *arg, void *c) { +_PySSLContext_set_msg_callback(PyObject *op, PyObject *arg, + void *Py_UNUSED(closure)) +{ + PySSLContext *self = PySSLContext_CAST(op); Py_CLEAR(self->msg_cb); if (arg == Py_None) { SSL_CTX_set_msg_callback(self->ctx, NULL); @@ -153,7 +158,9 @@ _PySSL_keylog_callback(const SSL *ssl, const char *line) } static PyObject * -_PySSLContext_get_keylog_filename(PySSLContext *self, void *c) { +_PySSLContext_get_keylog_filename(PyObject *op, void *Py_UNUSED(closure)) +{ + PySSLContext *self = PySSLContext_CAST(op); if (self->keylog_filename != NULL) { return Py_NewRef(self->keylog_filename); } else { @@ -162,7 +169,10 @@ _PySSLContext_get_keylog_filename(PySSLContext *self, void *c) { } static int -_PySSLContext_set_keylog_filename(PySSLContext *self, PyObject *arg, void *c) { +_PySSLContext_set_keylog_filename(PyObject *op, PyObject *arg, + void *Py_UNUSED(closure)) +{ + PySSLContext *self = PySSLContext_CAST(op); FILE *fp; /* Reset variables and callback first */ SSL_CTX_set_keylog_callback(self->ctx, NULL); From e1c502056357e1845c5ac90370caec30d9d805d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:28:20 +0100 Subject: [PATCH 2/8] fix UBSan failures for `PySSLSocket` --- Modules/_ssl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 87fd1cd2ce67ea..974522aa7e7699 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -339,6 +339,8 @@ typedef struct { PyObject *exc; } PySSLSocket; +#define PySSLSocket_CAST(op) ((PySSLSocket *)(op)) + typedef struct { PyObject_HEAD BIO *bio; @@ -2319,23 +2321,26 @@ _ssl__SSLSocket_owner_set_impl(PySSLSocket *self, PyObject *value) } static int -PySSL_traverse(PySSLSocket *self, visitproc visit, void *arg) +PySSL_traverse(PyObject *op, visitproc visit, void *arg) { + PySSLSocket *self = PySSLSocket_CAST(op); Py_VISIT(self->exc); Py_VISIT(Py_TYPE(self)); return 0; } static int -PySSL_clear(PySSLSocket *self) +PySSL_clear(PyObject *op) { + PySSLSocket *self = PySSLSocket_CAST(op); Py_CLEAR(self->exc); return 0; } static void -PySSL_dealloc(PySSLSocket *self) +PySSL_dealloc(PyObject *op) { + PySSLSocket *self = PySSLSocket_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); if (self->ssl) { From e101a98ba00594d1681280aba2f3b29d2d5c481d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:29:28 +0100 Subject: [PATCH 3/8] fix UBSan failures for `PySSLMemoryBIO` --- Modules/_ssl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 974522aa7e7699..93f9c5cf3fbbe8 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -347,6 +347,8 @@ typedef struct { int eof_written; } PySSLMemoryBIO; +#define PySSLMemoryBIO_CAST(op) ((PySSLMemoryBIO *)(op)) + typedef struct { PyObject_HEAD SSL_SESSION *session; @@ -5314,19 +5316,20 @@ _ssl_MemoryBIO_impl(PyTypeObject *type) } static int -memory_bio_traverse(PySSLMemoryBIO *self, visitproc visit, void *arg) +memory_bio_traverse(PyObject *op, visitproc visit, void *arg) { - Py_VISIT(Py_TYPE(self)); + Py_VISIT(Py_TYPE(op)); return 0; } static void -memory_bio_dealloc(PySSLMemoryBIO *self) +memory_bio_dealloc(PyObject *op) { + PySSLMemoryBIO *self = PySSLMemoryBIO_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - BIO_free(self->bio); - Py_TYPE(self)->tp_free(self); + (void)BIO_free(self->bio); + tp->tp_free(self); Py_DECREF(tp); } From d734c1e8cb8258ff27b25f28f1e3d098de1bda30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:33:27 +0100 Subject: [PATCH 4/8] fix UBSan failures for `PySSLSession` --- Modules/_ssl.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 93f9c5cf3fbbe8..8a397e695407f5 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -355,6 +355,8 @@ typedef struct { PySSLContext *ctx; } PySSLSession; +#define PySSLSession_CAST(op) ((PySSLSession *)(op)) + static inline _PySSLError _PySSL_errno(int failed, const SSL *ssl, int retcode) { _PySSLError err = { 0 }; @@ -5509,8 +5511,9 @@ static PyType_Spec PySSLMemoryBIO_spec = { */ static void -PySSLSession_dealloc(PySSLSession *self) +PySSLSession_dealloc(PyObject *op) { + PySSLSession *self = PySSLSession_CAST(op); PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); @@ -5531,7 +5534,8 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op) } int result; - PyTypeObject *sesstype = ((PySSLSession*)left)->ctx->state->PySSLSession_Type; + _sslmodulestate *state = get_state_obj(left); + PyTypeObject *sesstype = state->PySSLSession_Type; if (!Py_IS_TYPE(left, sesstype) || !Py_IS_TYPE(right, sesstype)) { Py_RETURN_NOTIMPLEMENTED; @@ -5581,16 +5585,18 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op) } static int -PySSLSession_traverse(PySSLSession *self, visitproc visit, void *arg) +PySSLSession_traverse(PyObject *op, visitproc visit, void *arg) { + PySSLSession *self = PySSLSession_CAST(op); Py_VISIT(self->ctx); Py_VISIT(Py_TYPE(self)); return 0; } static int -PySSLSession_clear(PySSLSession *self) +PySSLSession_clear(PyObject *op) { + PySSLSession *self = PySSLSession_CAST(op); Py_CLEAR(self->ctx); return 0; } From fe9f918a769c95edbfd9e6ed5ea358bd2c443e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:35:28 +0100 Subject: [PATCH 5/8] Do not use `_` + capital letter in cast macros as it is also UB. --- Modules/_ssl/cert.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/_ssl/cert.c b/Modules/_ssl/cert.c index c11ed8e3a282e6..f2e7be896687c8 100644 --- a/Modules/_ssl/cert.c +++ b/Modules/_ssl/cert.c @@ -153,13 +153,13 @@ _x509name_print(_sslmodulestate *state, X509_NAME *name, int indent, unsigned lo * PySSLCertificate_Type */ -#define _PySSLCertificate_CAST(op) ((PySSLCertificate *)(op)) +#define PySSLCertificate_CAST(op) ((PySSLCertificate *)(op)) static PyObject * certificate_repr(PyObject *op) { PyObject *osubject, *result; - PySSLCertificate *self = _PySSLCertificate_CAST(op); + PySSLCertificate *self = PySSLCertificate_CAST(op); /* subject string is ASCII encoded, UTF-8 chars are quoted */ osubject = _x509name_print( @@ -181,7 +181,7 @@ certificate_repr(PyObject *op) static Py_hash_t certificate_hash(PyObject *op) { - PySSLCertificate *self = _PySSLCertificate_CAST(op); + PySSLCertificate *self = PySSLCertificate_CAST(op); if (self->hash == (Py_hash_t)-1) { unsigned long hash; hash = X509_subject_name_hash(self->cert); @@ -198,7 +198,7 @@ static PyObject * certificate_richcompare(PyObject *lhs, PyObject *rhs, int op) { int cmp; - PySSLCertificate *self = _PySSLCertificate_CAST(lhs); + PySSLCertificate *self = PySSLCertificate_CAST(lhs); _sslmodulestate *state = get_state_cert(self); if (Py_TYPE(rhs) != state->PySSLCertificate_Type) { @@ -219,7 +219,7 @@ certificate_richcompare(PyObject *lhs, PyObject *rhs, int op) static void certificate_dealloc(PyObject *op) { - PySSLCertificate *self = _PySSLCertificate_CAST(op); + PySSLCertificate *self = PySSLCertificate_CAST(op); PyTypeObject *tp = Py_TYPE(self); X509_free(self->cert); (void)Py_TYPE(self)->tp_free(self); From 62cfadaeeb6d753e95e725ee9872dbd1d6600128 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 5 Mar 2025 09:10:28 +0100 Subject: [PATCH 6/8] Assume (and then assert, a bit too late) that left is of the right type --- Modules/_ssl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 8a397e695407f5..5dcd8635ea686e 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -5534,10 +5534,12 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op) } int result; - _sslmodulestate *state = get_state_obj(left); - PyTypeObject *sesstype = state->PySSLSession_Type; - if (!Py_IS_TYPE(left, sesstype) || !Py_IS_TYPE(right, sesstype)) { + PySSLSession *self = PySSLSession_CAST(left); + PyTypeObject *sesstype = Py_TYPE(self); + assert(sesstype == self->ctx->state->PySSLSession_Type); + + if (!Py_IS_TYPE(right, sesstype)) { Py_RETURN_NOTIMPLEMENTED; } @@ -5546,8 +5548,7 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op) } else { const unsigned char *left_id, *right_id; unsigned int left_len, right_len; - left_id = SSL_SESSION_get_id(((PySSLSession *)left)->session, - &left_len); + left_id = SSL_SESSION_get_id(self->session, &left_len); right_id = SSL_SESSION_get_id(((PySSLSession *)right)->session, &right_len); if (left_len == right_len) { From dc0a5211058387aaed467bcb39c4fd4c2a130442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 16 Mar 2025 14:23:36 +0100 Subject: [PATCH 7/8] Victor's review (part 1) --- Modules/_ssl.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 5dcd8635ea686e..e5ef92d9343c65 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -5318,9 +5318,9 @@ _ssl_MemoryBIO_impl(PyTypeObject *type) } static int -memory_bio_traverse(PyObject *op, visitproc visit, void *arg) +memory_bio_traverse(PyObject *self, visitproc visit, void *arg) { - Py_VISIT(Py_TYPE(op)); + Py_VISIT(Py_TYPE(self)); return 0; } @@ -5535,11 +5535,9 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op) int result; - PySSLSession *self = PySSLSession_CAST(left); - PyTypeObject *sesstype = Py_TYPE(self); - assert(sesstype == self->ctx->state->PySSLSession_Type); + PyTypeObject *sesstype = PySSLSession_CAST(left)->ctx->state->PySSLSession_Type; - if (!Py_IS_TYPE(right, sesstype)) { + if (!Py_IS_TYPE(left, sesstype) || !Py_IS_TYPE(right, sesstype)) { Py_RETURN_NOTIMPLEMENTED; } @@ -5548,8 +5546,9 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op) } else { const unsigned char *left_id, *right_id; unsigned int left_len, right_len; - left_id = SSL_SESSION_get_id(self->session, &left_len); - right_id = SSL_SESSION_get_id(((PySSLSession *)right)->session, + left_id = SSL_SESSION_get_id(PySSLSession_CAST(left)->session, + &left_len); + right_id = SSL_SESSION_get_id(PySSLSession_CAST(right)->session, &right_len); if (left_len == right_len) { result = memcmp(left_id, right_id, left_len); From 9a4079cc08dffcebc4560b764ecdbb2080d97029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 16 Mar 2025 17:53:58 +0100 Subject: [PATCH 8/8] Update Modules/_ssl.c --- Modules/_ssl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index e5ef92d9343c65..af67e980dd7933 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -5534,7 +5534,6 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op) } int result; - PyTypeObject *sesstype = PySSLSession_CAST(left)->ctx->state->PySSLSession_Type; if (!Py_IS_TYPE(left, sesstype) || !Py_IS_TYPE(right, sesstype)) {