diff options
-rw-r--r-- | MAINTAINERS | 10 | ||||
-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 | ||||
-rw-r--r-- | scripts/tracetool/backend/log.py | 14 | ||||
-rw-r--r-- | util/log.c | 20 |
13 files changed, 177 insertions, 27 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index a462345..f1bd69c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -295,7 +295,7 @@ F: tests/tcg/openrisc/ PowerPC TCG CPUs M: Nicholas Piggin <npiggin@gmail.com> -M: Daniel Henrique Barboza <danielhb413@gmail.com> +R: Chinmay Rath <rathc@linux.ibm.com> L: qemu-ppc@nongnu.org S: Odd Fixes F: target/ppc/ @@ -452,7 +452,7 @@ F: target/mips/system/ PPC KVM CPUs M: Nicholas Piggin <npiggin@gmail.com> -R: Daniel Henrique Barboza <danielhb413@gmail.com> +R: Harsh Prateek Bora <harshpb@linux.ibm.com> S: Odd Fixes F: target/ppc/kvm.c @@ -1550,7 +1550,7 @@ F: tests/functional/test_ppc_40p.py sPAPR (pseries) M: Nicholas Piggin <npiggin@gmail.com> -R: Daniel Henrique Barboza <danielhb413@gmail.com> +M: Harsh Prateek Bora <harshpb@linux.ibm.com> R: Harsh Prateek Bora <harshpb@linux.ibm.com> L: qemu-ppc@nongnu.org S: Odd Fixes @@ -1575,7 +1575,7 @@ F: tests/functional/test_ppc64_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin <npiggin@gmail.com> -R: Frédéric Barrat <fbarrat@linux.ibm.com> +R: Aditya Gupta <adityag@linux.ibm.com> L: qemu-ppc@nongnu.org S: Odd Fixes F: docs/system/ppc/powernv.rst @@ -2776,7 +2776,7 @@ F: tests/qtest/fw_cfg-test.c T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE -R: Frédéric Barrat <fbarrat@linux.ibm.com> +R: Gautam Menghani <gautam@linux.ibm.com> L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/*/*xive* 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 ;; diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 5c9d09d..eb50cee 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -20,7 +20,6 @@ PUBLIC = True def generate_h_begin(events, group): out('#include "qemu/log-for-trace.h"', - '#include "qemu/error-report.h"', '') @@ -32,20 +31,9 @@ def generate_h(event, group): cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', - ' if (message_with_timestamp) {', - ' struct timeval _now;', - ' gettimeofday(&_now, NULL);', '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",', - ' qemu_get_thread_id(),', - ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', - ' %(argnames)s);', + ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' } else {', - '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', - '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', ' }', cond=cond, event_lineno=event.lineno, @@ -145,10 +145,28 @@ void qemu_log_unlock(FILE *logfile) void qemu_log(const char *fmt, ...) { - FILE *f = qemu_log_trylock(); + FILE *f; + g_autofree const char *timestr = NULL; + + /* + * Prepare the timestamp *outside* the logging + * lock so it better reflects when the message + * was emitted if we are delayed acquiring the + * mutex + */ + if (message_with_timestamp) { + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); + timestr = g_date_time_format_iso8601(dt); + } + + f = qemu_log_trylock(); if (f) { va_list ap; + if (timestr) { + fprintf(f, "%s ", timestr); + } + va_start(ap, fmt); vfprintf(f, fmt, ap); va_end(ap); |