We should upgrade python version instead of picking patches included in latest tag. https://github.com/python/cpython/releases/tag/v3.12.15
I'll probably give it a try over weekend. Peter > -----Original Message----- > From: [email protected] <openembedded- > [email protected]> On Behalf Of Joao Marcos Costa via > lists.openembedded.org > Sent: Friday, October 2, 2026 2:40 PM > To: [email protected] > Cc: [email protected]; [email protected]; João Marcos Costa > (Schneider Electric) <[email protected]> > Subject: [OE-core] [scarthgap][PATCH] python3: fix CVE-2026-19445 > > From: João Marcos Costa (Schneider Electric) <[email protected]> > > This critical vulnerability [1] was fixed originally on 3.14, and then > backported to > 3.12 through a PR (already merged) [2]: > > "[3.12] gh-156293: Use-after-free for server-side SSLContext with > sni_callback" > > Backport the fix to oe-core's v3.12.14. > > [1] https://security-tracker.debian.org/tracker/CVE-2026-19445 > [2] https://github.com/python/cpython/pull/158517 > > Signed-off-by: João Marcos Costa (Schneider Electric) > <[email protected]> > --- > .../python/python3/CVE-2026-19445.patch | 265 ++++++++++++++++++ > .../python/python3_3.12.14.bb | 1 + > 2 files changed, 266 insertions(+) > create mode 100644 meta/recipes-devtools/python/python3/CVE-2026- > 19445.patch > > diff --git a/meta/recipes-devtools/python/python3/CVE-2026-19445.patch > b/meta/recipes-devtools/python/python3/CVE-2026-19445.patch > new file mode 100644 > index 0000000000..40fe283ba6 > --- /dev/null > +++ b/meta/recipes-devtools/python/python3/CVE-2026-19445.patch > @@ -0,0 +1,265 @@ > +From 61ba3afbaab1394b5e47402e5122cb5c55d4884f Mon Sep 17 00:00:00 > 2001 > +From: Seth Michael Larson <[email protected]> > +Date: Wed, 30 Sep 2026 10:53:31 -0500 > +Subject: [PATCH] [3.12] gh-156293: Use-after-free for server-side SSLContext > + with sni_callback > +MIME-Version: 1.0 > +Content-Type: text/plain; charset=UTF-8 > +Content-Transfer-Encoding: 8bit > + > +CVE: CVE-2026-19445 > +Upstream-Status: Backport [https://github.com/python/cpython/pull/158517] > + > +Co-authored-by: Gregory P. Smith > <[email protected]> > +Signed-off-by: João Marcos Costa (Schneider Electric) > <[email protected]> > +--- > + Doc/library/ssl.rst | 11 +++ > + Lib/test/test_ssl.py | 80 +++++++++++++++++++ > + ...-08-10-12-00-00.gh-issue-156293.sNIcbk.rst | 7 ++ > + Modules/_ssl.c | 40 +++++++--- > + 4 files changed, 126 insertions(+), 12 deletions(-) > + create mode 100644 Misc/NEWS.d/next/Security/2026-08-10-12-00-00.gh- > issue-156293.sNIcbk.rst > + > +diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst > +index 67360a5079f..71199ceabc6 100644 > +--- a/Doc/library/ssl.rst > ++++ b/Doc/library/ssl.rst > +@@ -1666,6 +1666,12 @@ to speed up repeated connections from the same > clients. > + :class:`SSLContext` representing a certificate chain that matches the > server > + name. > + > ++ If the callback assigns a new context to :attr:`SSLSocket.context`, any > ++ further ClientHello message on the same connection (for example after a > ++ TLS 1.3 HelloRetryRequest) is dispatched to the new context's > ++ *sni_callback*, if it has one; the original callback is not called again > ++ for that connection. > ++ > + Due to the early negotiation phase of the TLS connection, only limited > + methods and attributes are usable like > + :meth:`SSLSocket.selected_alpn_protocol` and :attr:`SSLSocket.context`. > +@@ -1689,6 +1695,11 @@ to speed up repeated connections from the same > clients. > + > + .. versionadded:: 3.7 > + > ++ .. versionchanged:: next > ++ After the callback assigns a new :attr:`SSLSocket.context`, later > ++ ClientHello messages on the connection are dispatched to the new > ++ context's *sni_callback*. > ++ > + .. attribute:: SSLContext.set_servername_callback(server_name_callback) > + > + This is a legacy API retained for backwards compatibility. When possible, > +diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py > +index b13e37d0cd1..accc030d2f3 100644 > +--- a/Lib/test/test_ssl.py > ++++ b/Lib/test/test_ssl.py > +@@ -1818,6 +1818,86 @@ class SSLObjectTests(unittest.TestCase): > + c_in.write(s_out.read()) > + client.unwrap() > + > ++ def test_sni_callback_context_released_and_callback_raises(self): > ++ # Variant of the test below without a HelloRetryRequest: the > callback > ++ # switches the connection to another context, drops the last > ++ # references to the context that carries it, and raises. The C > ++ # callback must not touch that context after the Python callback > ++ # returned. > ++ client_ctx, server_ctx, hostname = testing_context() > ++ leaf_ctx = server_ctx > ++ > ++ def sni_cb(sslobj, server_name, ctx): > ++ sslobj.context = leaf_ctx > ++ del ctx > ++ raise LookupError("no certificate for " + repr(server_name)) > ++ > ++ def make_server(): > ++ dispatch_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) > ++ dispatch_ctx.load_cert_chain(SIGNED_CERTFILE) > ++ dispatch_ctx.sni_callback = sni_cb > ++ s_in, s_out = ssl.MemoryBIO(), ssl.MemoryBIO() > ++ server = dispatch_ctx.wrap_bio(s_in, s_out, server_side=True) > ++ return server, s_in, s_out > ++ > ++ server, s_in, s_out = make_server() > ++ c_in, c_out = ssl.MemoryBIO(), ssl.MemoryBIO() > ++ client = client_ctx.wrap_bio(c_in, c_out, server_hostname=hostname) > ++ with self.assertRaises(ssl.SSLWantReadError): > ++ client.do_handshake() > ++ s_in.write(c_out.read()) > ++ with support.catch_unraisable_exception() as cm: > ++ with self.assertRaises(ssl.SSLError): > ++ server.do_handshake() > ++ self.assertIsInstance(cm.unraisable.exc_value, LookupError) > ++ self.assertIs(server.context, leaf_ctx) > ++ > ++ def test_sni_callback_context_released_before_second_client_hello(self): > ++ # The SSLContext carrying sni_callback may be released by the > ++ # application once the callback has switched the connection over to > ++ # another context. If the server then sends a HelloRetryRequest, > the > ++ # second ClientHello makes OpenSSL consult the original SSL_CTX's > ++ # servername callback again; that must not use the deallocated > ++ # SSLContext object. > ++ client_ctx, leaf_ctx, hostname = testing_context() > ++ calls = [] > ++ > ++ def make_server(): > ++ dispatch_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) > ++ dispatch_ctx.load_cert_chain(SIGNED_CERTFILE) > ++ # Force a HelloRetryRequest: the client offers an X25519 key > ++ # share first, the server only accepts P-384. > ++ dispatch_ctx.set_ecdh_curve("secp384r1") > ++ def sni_cb(sslobj, server_name, ctx): > ++ calls.append(server_name) > ++ sslobj.context = leaf_ctx > ++ dispatch_ctx.sni_callback = sni_cb > ++ s_in, s_out = ssl.MemoryBIO(), ssl.MemoryBIO() > ++ server = dispatch_ctx.wrap_bio(s_in, s_out, server_side=True) > ++ return server, s_in, s_out, weakref.ref(dispatch_ctx) > ++ > ++ # After this only the C-level SSL object references dispatch_ctx. > ++ server, s_in, s_out, dispatch_ref = make_server() > ++ c_in, c_out = ssl.MemoryBIO(), ssl.MemoryBIO() > ++ client = client_ctx.wrap_bio(c_in, c_out, server_hostname=hostname) > ++ for _ in range(10): > ++ for obj, out, peer_in in ((client, c_out, s_in), > ++ (server, s_out, c_in)): > ++ try: > ++ obj.do_handshake() > ++ except ssl.SSLWantReadError: > ++ pass > ++ if out.pending: > ++ peer_in.write(out.read()) > ++ client.do_handshake() > ++ server.do_handshake() > ++ support.gc_collect() > ++ self.assertIsNone(dispatch_ref()) > ++ self.assertGreaterEqual(len(calls), 1) > ++ self.assertEqual(calls[0], hostname) > ++ self.assertIs(server.context, leaf_ctx) > ++ self.assertIsNotNone(client.cipher()) > ++ > + class SimpleBackgroundTests(unittest.TestCase): > + """Tests that connect to a simple server running in the background""" > + > +diff --git a/Misc/NEWS.d/next/Security/2026-08-10-12-00-00.gh-issue- > 156293.sNIcbk.rst b/Misc/NEWS.d/next/Security/2026-08-10-12-00-00.gh-issue- > 156293.sNIcbk.rst > +new file mode 100644 > +index 00000000000..0cc620b3fdc > +--- /dev/null > ++++ b/Misc/NEWS.d/next/Security/2026-08-10-12-00-00.gh-issue- > 156293.sNIcbk.rst > +@@ -0,0 +1,7 @@ > ++Fix a crash in :mod:`ssl` when an :attr:`~ssl.SSLContext.sni_callback` > ++switches a connection to another :class:`~ssl.SSLContext` and the context > ++that carries the callback is no longer referenced by the application. > ++Servers that keep their ``sni_callback`` context alive (the usual case when > ++it wraps the listening socket or is stored on the server object) were not > ++affected. > ++This addresses :cve:`2026-19445`. > +diff --git a/Modules/_ssl.c b/Modules/_ssl.c > +index aae4dc323dd..91ca1362d4e 100644 > +--- a/Modules/_ssl.c > ++++ b/Modules/_ssl.c > +@@ -3184,6 +3184,9 @@ context_dealloc(PySSLContext *self) > + /* bpo-31095: UnTrack is needed before calling any callbacks */ > + PyObject_GC_UnTrack(self); > + context_clear(self); > ++ /* The SSL_CTX may outlive this object as the session_ctx of sockets > that > ++ were switched to another context; leave no Python callback behind. */ > ++ SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); > + SSL_CTX_free(self->ctx); > + PyMem_FREE(self->alpn_protocols); > + Py_TYPE(self)->tp_free(self); > +@@ -4346,27 +4349,37 @@ > _ssl__SSLContext_set_ecdh_curve(PySSLContext *self, PyObject *name) > + } > + > + static int > +-_servername_callback(SSL *s, int *al, void *args) > ++_servername_callback(SSL *s, int *al, void *Py_UNUSED(args)) > + { > + int ret; > +- PySSLContext *sslctx = (PySSLContext *) args; > ++ PySSLContext *sslctx; > + PySSLSocket *ssl; > + PyObject *result; > + /* The high-level ssl.SSLSocket object */ > + PyObject *ssl_socket; > ++ PyObject *sni_cb; > + const char *servername = SSL_get_servername(s, > TLSEXT_NAMETYPE_host_name); > + PyGILState_STATE gstate = PyGILState_Ensure(); > + > +- if (sslctx->set_sni_cb == NULL) { > +- /* remove race condition in this the call back while if removing the > +- * callback is in progress */ > ++ /* Do not use the SSL_CTX's servername arg to find the context: it is a > ++ borrowed pointer to whichever _SSLContext installed the callback, and > ++ that object may already be gone while OpenSSL still reaches this > ++ callback through the connection's session_ctx (e.g. on the second > ++ ClientHello after a HelloRetryRequest, once sni_callback has switched > ++ the socket to another context). The socket's current context is > ++ always alive; hold strong references to it and to the callback while > ++ they are used here. */ > ++ ssl = SSL_get_app_data(s); > ++ assert(ssl != NULL); > ++ sslctx = (PySSLContext *)Py_NewRef(ssl->ctx); > ++ assert(Py_IS_TYPE(ssl, get_state_ctx(sslctx)->PySSLSocket_Type)); > ++ sni_cb = Py_XNewRef(sslctx->set_sni_cb); > ++ if (sni_cb == NULL) { > ++ Py_DECREF(sslctx); > + PyGILState_Release(gstate); > + return SSL_TLSEXT_ERR_OK; > + } > + > +- ssl = SSL_get_app_data(s); > +- assert(Py_IS_TYPE(ssl, get_state_ctx(sslctx)->PySSLSocket_Type)); > +- > + /* The servername callback expects an argument that represents the > current > + * SSL connection and that has a .context attribute that can be changed > to > + * identify the requested hostname. Since the official API is the Python > +@@ -4387,7 +4400,7 @@ _servername_callback(SSL *s, int *al, void *args) > + goto error; > + > + if (servername == NULL) { > +- result = PyObject_CallFunctionObjArgs(sslctx->set_sni_cb, > ssl_socket, > ++ result = PyObject_CallFunctionObjArgs(sni_cb, ssl_socket, > + Py_None, sslctx, NULL); > + } > + else { > +@@ -4410,14 +4423,14 @@ _servername_callback(SSL *s, int *al, void *args) > + } > + Py_DECREF(servername_bytes); > + result = PyObject_CallFunctionObjArgs( > +- sslctx->set_sni_cb, ssl_socket, servername_str, > ++ sni_cb, ssl_socket, servername_str, > + sslctx, NULL); > + Py_DECREF(servername_str); > + } > + Py_DECREF(ssl_socket); > + > + if (result == NULL) { > +- PyErr_WriteUnraisable(sslctx->set_sni_cb); > ++ PyErr_WriteUnraisable(sni_cb); > + *al = SSL_AD_HANDSHAKE_FAILURE; > + ret = SSL_TLSEXT_ERR_ALERT_FATAL; > + } > +@@ -4438,11 +4451,15 @@ _servername_callback(SSL *s, int *al, void *args) > + Py_DECREF(result); > + } > + > ++ Py_DECREF(sni_cb); > ++ Py_DECREF(sslctx); > + PyGILState_Release(gstate); > + return ret; > + > + error: > + Py_DECREF(ssl_socket); > ++ Py_DECREF(sni_cb); > ++ Py_DECREF(sslctx); > + *al = SSL_AD_INTERNAL_ERROR; > + ret = SSL_TLSEXT_ERR_ALERT_FATAL; > + PyGILState_Release(gstate); > +@@ -4480,7 +4497,6 @@ set_sni_callback(PySSLContext *self, PyObject *arg, > void *c) > + } > + self->set_sni_cb = Py_NewRef(arg); > + SSL_CTX_set_tlsext_servername_callback(self->ctx, > _servername_callback); > +- SSL_CTX_set_tlsext_servername_arg(self->ctx, self); > + } > + return 0; > + } > diff --git a/meta/recipes-devtools/python/python3_3.12.14.bb b/meta/recipes- > devtools/python/python3_3.12.14.bb > index 14be125180..bf2c7938b5 100644 > --- a/meta/recipes-devtools/python/python3_3.12.14.bb > +++ b/meta/recipes-devtools/python/python3_3.12.14.bb > @@ -36,6 +36,7 @@ SRC_URI = "http://www.python.org/ftp/python/${PV}/Python- > ${PV}.tar.xz \ > file://0001-test_readline-skip-limited-history-test.patch \ > file://CVE-2026-6019_p1.patch \ > file://CVE-2026-6019_p2.patch \ > + file://CVE-2026-19445.patch \ > " > > SRC_URI:append:class-native = " \ > -- > 2.55.0
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#247119): https://lists.openembedded.org/g/openembedded-core/message/247119 Mute This Topic: https://lists.openembedded.org/mt/121545671/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
