diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-07-25 08:24:29 -0400 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-07-25 08:24:29 -0400 |
commit | 919c486c406a34823c6cef5438f1a13e2c79a7d5 (patch) | |
tree | 0741b07544b3b0cdac5e5443840654204e8318e4 | |
parent | 9e601684dc24a521bb1d23215a63e5c6e79ea0bb (diff) | |
parent | 0db6f798024ea6f57ecf2020209b761b50a01d71 (diff) | |
download | qemu-919c486c406a34823c6cef5438f1a13e2c79a7d5.zip qemu-919c486c406a34823c6cef5438f1a13e2c79a7d5.tar.gz qemu-919c486c406a34823c6cef5438f1a13e2c79a7d5.tar.bz2 |
Merge tag 'migration-20250722-pull-request' of https://gitlab.com/farosas/qemu into staging
Migration pull request
- Fixes to postcopy blocktime latency display code
- Fix to QMP error message (not)shown when postcopy fails
- Workaround to a GNUTLS bug that crashes QEMU
# -----BEGIN PGP SIGNATURE-----
#
# iQJEBAABCAAuFiEEqhtIsKIjJqWkw2TPx5jcdBvsMZ0FAmiAG1wQHGZhcm9zYXNA
# c3VzZS5kZQAKCRDHmNx0G+wxnR0xEACZMIqnVIFUu57V5gJ8v/4IJv70n6jrjtzJ
# 5/TzdAAY9bKJE5y84axovZy4iHijbZnGz+kVKr5Wai9KKb41tW0liWAe5RART2TE
# VuRBgxXODCmg3US6w0niy9cR3NH7WXbEQ5gyexC7D3/1R1ahpqOragZQxzvtA+3e
# aKe2pqRyQODHU9D1tnKexeFNJM6dGBVd9FVsYAHDfhx0Bk1vcpVXVrAJcfaSY2Y5
# +4/g7CXOJCUFBrFbVxYFU9muU8JrMvWv8lU4nG2ztDhmSH7Uy/DVCfEUa9/jEjDa
# 1BwZbOIIFMJy0P/G3toK6Z9lJEVfiUXaboNtqgSK5ZM8ZL1L1yHKQi631Qny/Wuf
# pzJWR1nOSL2f/bsueWj2OmZKl3FpXcaDWisZuDeS3wXWrtPRuJEXi6f//6JcYd2i
# Zm0kVRNf3CbXGnJxwDrsbh0hr5sN+bonaI+N4hHGxDCqUHhND4p0JMaPMte+PF4u
# pOooaRKq2a6KRZFyDPjyBgESXfDJ0Tdw5IeOKbFPskOEIpBVxyc3mpwu8Kz45qoV
# 8b2GYCKBjWLpqfTPwUcJd5MNVDO1ZUyqOPuarHNADth6pJglnWyFI/TIBoARzAKB
# EzS4dQ+DKM/Jz5cM++0dMPL75/1i2q2x7BBhCBBm9yeZDqDIKeT07yl8JGL/OCq9
# 7gNGfyze5w==
# =DGn2
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 22 Jul 2025 19:14:36 EDT
# gpg: using RSA key AA1B48B0A22326A5A4C364CFC798DC741BEC319D
# gpg: issuer "farosas@suse.de"
# gpg: Good signature from "Fabiano Rosas <farosas@suse.de>" [unknown]
# gpg: aka "Fabiano Almeida Rosas <fabiano.rosas@suse.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: AA1B 48B0 A223 26A5 A4C3 64CF C798 DC74 1BEC 319D
* tag 'migration-20250722-pull-request' of https://gitlab.com/farosas/qemu:
crypto: add tracing & warning about GNUTLS countermeasures
migration: activate TLS thread safety workaround
io: add support for activating TLS thread safety workaround
crypto: implement workaround for GNUTLS thread safety problems
migration: show error message when postcopy fails
migration: HMP: Fix postcopy latency distribution label
migration: HMP: Fix possible out-of-bounds access
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r-- | crypto/tlssession.c | 103 | ||||
-rw-r--r-- | crypto/trace-events | 2 | ||||
-rw-r--r-- | include/crypto/tlssession.h | 14 | ||||
-rw-r--r-- | include/io/channel.h | 1 | ||||
-rw-r--r-- | io/channel-tls.c | 5 | ||||
-rw-r--r-- | meson.build | 9 | ||||
-rw-r--r-- | meson_options.txt | 2 | ||||
-rw-r--r-- | migration/migration-hmp-cmds.c | 10 | ||||
-rw-r--r-- | migration/tls.c | 9 | ||||
-rw-r--r-- | scripts/meson-buildoptions.sh | 5 |
10 files changed, 152 insertions, 8 deletions
diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 6d8f8df..86d407a 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -19,6 +19,8 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/thread.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" #include "crypto/tlscredspsk.h" @@ -51,6 +53,14 @@ struct QCryptoTLSSession { */ Error *rerr; Error *werr; + + /* + * Used to protect against broken GNUTLS thread safety + * https://gitlab.com/gnutls/gnutls/-/issues/1717 + */ + bool requireThreadSafety; + bool lockEnabled; + QemuMutex lock; }; @@ -69,6 +79,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) g_free(session->peername); g_free(session->authzid); object_unref(OBJECT(session->creds)); + qemu_mutex_destroy(&session->lock); g_free(session); } @@ -84,10 +95,19 @@ qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) return -1; }; + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + error_free(session->werr); session->werr = NULL; ret = session->writeFunc(buf, len, session->opaque, &session->werr); + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { errno = EAGAIN; return -1; @@ -114,7 +134,16 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) error_free(session->rerr); session->rerr = NULL; + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + ret = session->readFunc(buf, len, session->opaque, &session->rerr); + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { errno = EAGAIN; return -1; @@ -153,6 +182,8 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, session->creds = creds; object_ref(OBJECT(creds)); + qemu_mutex_init(&session->lock); + if (creds->endpoint != endpoint) { error_setg(errp, "Credentials endpoint doesn't match session"); goto error; @@ -289,6 +320,11 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, return NULL; } +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess) +{ + sess->requireThreadSafety = true; +} + static int qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, Error **errp) @@ -480,7 +516,17 @@ qcrypto_tls_session_write(QCryptoTLSSession *session, size_t len, Error **errp) { - ssize_t ret = gnutls_record_send(session->handle, buf, len); + ssize_t ret; + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + + ret = gnutls_record_send(session->handle, buf, len); + + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { @@ -509,7 +555,17 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, bool gracefulTermination, Error **errp) { - ssize_t ret = gnutls_record_recv(session->handle, buf, len); + ssize_t ret; + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + + ret = gnutls_record_recv(session->handle, buf, len); + + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { @@ -545,8 +601,39 @@ int qcrypto_tls_session_handshake(QCryptoTLSSession *session, Error **errp) { - int ret = gnutls_handshake(session->handle); + int ret; + ret = gnutls_handshake(session->handle); + if (!ret) { +#ifdef CONFIG_GNUTLS_BUG1717_WORKAROUND + gnutls_cipher_algorithm_t cipher = + gnutls_cipher_get(session->handle); + + /* + * Any use of rekeying in TLS 1.3 is unsafe for + * a gnutls with bug 1717, however, we know that + * QEMU won't initiate manual rekeying. Thus we + * only have to protect against automatic rekeying + * which doesn't trigger with CHACHA20 + */ + trace_qcrypto_tls_session_parameters( + session, + session->requireThreadSafety, + gnutls_protocol_get_version(session->handle), + cipher); + + if (session->requireThreadSafety && + gnutls_protocol_get_version(session->handle) == + GNUTLS_TLS1_3 && + cipher != GNUTLS_CIPHER_CHACHA20_POLY1305) { + warn_report("WARNING: activating thread safety countermeasures " + "for potentially broken GNUTLS with TLS1.3 cipher=%d", + cipher); + trace_qcrypto_tls_session_bug1717_workaround(session); + session->lockEnabled = true; + } +#endif + session->handshakeComplete = true; return QCRYPTO_TLS_HANDSHAKE_COMPLETE; } @@ -584,8 +671,15 @@ qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) return 0; } + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR); + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + if (!ret) { return QCRYPTO_TLS_BYE_COMPLETE; } @@ -651,6 +745,9 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, return NULL; } +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess) +{ +} void qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) diff --git a/crypto/trace-events b/crypto/trace-events index bccd0bbf..d0e3342 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -21,6 +21,8 @@ qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds # tlssession.c qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d" qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s" +qcrypto_tls_session_parameters(void *session, int threadSafety, int protocol, int cipher) "TLS session parameters session=%p threadSafety=%d protocol=%d cipher=%d" +qcrypto_tls_session_bug1717_workaround(void *session) "TLS session bug1717 workaround session=%p" # tls-cipher-suites.c qcrypto_tls_cipher_suite_priority(const char *name) "priority: %s" diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index d77ae0d..2f62ce2 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -166,6 +166,20 @@ void qcrypto_tls_session_free(QCryptoTLSSession *sess); G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) /** + * qcrypto_tls_session_require_thread_safety: + * @sess: the TLS session object + * + * Mark that this TLS session will require thread safety + * for concurrent I/O in both directions. This must be + * called before the handshake is performed. + * + * This will activate a workaround for GNUTLS thread + * safety issues, where appropriate for the negotiated + * TLS session parameters. + */ +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess); + +/** * qcrypto_tls_session_check_credentials: * @sess: the TLS session object * @errp: pointer to a NULL-initialized error object diff --git a/include/io/channel.h b/include/io/channel.h index 62b6571..234e5db 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -46,6 +46,7 @@ enum QIOChannelFeature { QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY, QIO_CHANNEL_FEATURE_READ_MSG_PEEK, QIO_CHANNEL_FEATURE_SEEKABLE, + QIO_CHANNEL_FEATURE_CONCURRENT_IO, }; diff --git a/io/channel-tls.c b/io/channel-tls.c index db2ac1d..a8248a9 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -241,6 +241,11 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, { QIOTask *task; + if (qio_channel_has_feature(QIO_CHANNEL(ioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO)) { + qcrypto_tls_session_require_thread_safety(ioc->session); + } + task = qio_task_new(OBJECT(ioc), func, opaque, destroy); diff --git a/meson.build b/meson.build index c2bc3ee..e53cd5b 100644 --- a/meson.build +++ b/meson.build @@ -1809,6 +1809,7 @@ endif gnutls = not_found gnutls_crypto = not_found +gnutls_bug1717_workaround = false if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system) # For general TLS support our min gnutls matches # that implied by our platform support matrix @@ -1834,6 +1835,12 @@ if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_syste method: 'pkg-config', required: get_option('gnutls')) endif + + if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() + # XXX: when bug 1717 is resolved, add logic to probe for + # the GNUTLS fixed version number to handle the 'auto' case + gnutls_bug1717_workaround = true + endif endif # We prefer use of gnutls for crypto, unless the options @@ -2585,6 +2592,7 @@ config_host_data.set('CONFIG_KEYUTILS', keyutils.found()) config_host_data.set('CONFIG_GETTID', has_gettid) config_host_data.set('CONFIG_GNUTLS', gnutls.found()) config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found()) +config_host_data.set('CONFIG_GNUTLS_BUG1717_WORKAROUND', gnutls_bug1717_workaround) config_host_data.set('CONFIG_TASN1', tasn1.found()) config_host_data.set('CONFIG_GCRYPT', gcrypt.found()) config_host_data.set('CONFIG_NETTLE', nettle.found()) @@ -4869,6 +4877,7 @@ summary_info += {'TLS priority': get_option('tls_priority')} summary_info += {'GNUTLS support': gnutls} if gnutls.found() summary_info += {' GNUTLS crypto': gnutls_crypto.found()} + summary_info += {' GNUTLS bug 1717 workaround': gnutls_bug1717_workaround } endif summary_info += {'libgcrypt': gcrypt} summary_info += {'nettle': nettle} diff --git a/meson_options.txt b/meson_options.txt index fff1521..dd33530 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -174,6 +174,8 @@ option('libcbor', type : 'feature', value : 'auto', description: 'libcbor support') option('gnutls', type : 'feature', value : 'auto', description: 'GNUTLS cryptography support') +option('gnutls-bug1717-workaround', type: 'feature', value : 'auto', + description: 'GNUTLS workaround for https://gitlab.com/gnutls/gnutls/-/issues/1717') option('nettle', type : 'feature', value : 'auto', description: 'nettle cryptography support') option('gcrypt', type : 'feature', value : 'auto', diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index cef5608..0fc21f0 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -57,11 +57,9 @@ static const gchar *format_time_str(uint64_t us) const char *units[] = {"us", "ms", "sec"}; int index = 0; - while (us > 1000) { + while (us >= 1000 && index + 1 < ARRAY_SIZE(units)) { us /= 1000; - if (++index >= (sizeof(units) - 1)) { - break; - } + index++; } return g_strdup_printf("%"PRIu64" %s", us, units[index]); @@ -153,7 +151,9 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) if (info->has_status) { monitor_printf(mon, "Status: \t\t%s", MigrationStatus_str(info->status)); - if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { + if ((info->status == MIGRATION_STATUS_FAILED || + info->status == MIGRATION_STATUS_POSTCOPY_PAUSED) && + info->error_desc) { monitor_printf(mon, " (%s)\n", info->error_desc); } else { monitor_printf(mon, "\n"); diff --git a/migration/tls.c b/migration/tls.c index 5cbf952..284a619 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -90,6 +90,10 @@ void migration_tls_channel_process_incoming(MigrationState *s, trace_migration_tls_incoming_handshake_start(); qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-incoming"); + if (migrate_postcopy_ram() || migrate_return_path()) { + qio_channel_set_feature(QIO_CHANNEL(tioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO); + } qio_channel_tls_handshake(tioc, migration_tls_incoming_handshake, NULL, @@ -149,6 +153,11 @@ void migration_tls_channel_connect(MigrationState *s, s->hostname = g_strdup(hostname); trace_migration_tls_outgoing_handshake_start(hostname); qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-outgoing"); + + if (migrate_postcopy_ram() || migrate_return_path()) { + qio_channel_set_feature(QIO_CHANNEL(tioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO); + } qio_channel_tls_handshake(tioc, migration_tls_outgoing_handshake, s, diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 0ebe6bc..d559e26 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -123,6 +123,9 @@ meson_options_help() { printf "%s\n" ' gio use libgio for D-Bus support' printf "%s\n" ' glusterfs Glusterfs block device driver' printf "%s\n" ' gnutls GNUTLS cryptography support' + printf "%s\n" ' gnutls-bug1717-workaround' + printf "%s\n" ' GNUTLS workaround for' + printf "%s\n" ' https://gitlab.com/gnutls/gnutls/-/issues/1717' printf "%s\n" ' gtk GTK+ user interface' printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' printf "%s\n" ' guest-agent Build QEMU Guest Agent' @@ -331,6 +334,8 @@ _meson_option_parse() { --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; --disable-gnutls) printf "%s" -Dgnutls=disabled ;; + --enable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=enabled ;; + --disable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=disabled ;; --enable-gtk) printf "%s" -Dgtk=enabled ;; --disable-gtk) printf "%s" -Dgtk=disabled ;; --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;; |