Peter Xu <pet...@redhat.com> writes: > On Wed, Feb 05, 2025 at 05:42:37PM -0300, Fabiano Rosas wrote: >> Fabiano Rosas <faro...@suse.de> writes: >> >> > Daniel P. Berrangé <berra...@redhat.com> writes: >> > >> >> On Tue, Feb 04, 2025 at 10:31:31AM -0500, Peter Xu wrote: >> >>> On Tue, Feb 04, 2025 at 03:39:00PM +0100, Maciej S. Szmigiero wrote: >> >>> > On 3.02.2025 23:56, Peter Xu wrote: >> >>> > > On Mon, Feb 03, 2025 at 10:41:32PM +0100, Maciej S. Szmigiero wrote: >> >>> > > > On 3.02.2025 21:20, Peter Xu wrote: >> >>> > > > > On Mon, Feb 03, 2025 at 07:53:00PM +0100, Maciej S. Szmigiero >> >>> > > > > wrote: >> >>> > > > > > On 3.02.2025 19:20, Peter Xu wrote: >> >>> > > > > > > On Thu, Jan 30, 2025 at 11:08:29AM +0100, Maciej S. >> >>> > > > > > > Szmigiero wrote: >> >>> > > > > > > > From: "Maciej S. Szmigiero" <maciej.szmigi...@oracle.com> >> >>> > > > > > > > >> >>> > > > > > > > Multifd send channels are terminated by calling >> >>> > > > > > > > qio_channel_shutdown(QIO_CHANNEL_SHUTDOWN_BOTH) in >> >>> > > > > > > > multifd_send_terminate_threads(), which in the TLS case >> >>> > > > > > > > essentially >> >>> > > > > > > > calls shutdown(SHUT_RDWR) on the underlying raw socket. >> >>> > > > > > > > >> >>> > > > > > > > Unfortunately, this does not terminate the TLS session >> >>> > > > > > > > properly and >> >>> > > > > > > > the receive side sees this as a >> >>> > > > > > > > GNUTLS_E_PREMATURE_TERMINATION error. >> >>> > > > > > > > >> >>> > > > > > > > The only reason why this wasn't causing migration failures >> >>> > > > > > > > is because >> >>> > > > > > > > the current migration code apparently does not check for >> >>> > > > > > > > migration >> >>> > > > > > > > error being set after the end of the multifd receive >> >>> > > > > > > > process. >> >>> > > > > > > > >> >>> > > > > > > > However, this will change soon so the multifd receive code >> >>> > > > > > > > has to be >> >>> > > > > > > > prepared to not return an error on such premature TLS >> >>> > > > > > > > session EOF. >> >>> > > > > > > > Use the newly introduced QIOChannelTLS method for that. >> >>> > > > > > > > >> >>> > > > > > > > It's worth noting that even if the sender were to be >> >>> > > > > > > > changed to terminate >> >>> > > > > > > > the TLS connection properly the receive side still needs >> >>> > > > > > > > to remain >> >>> > > > > > > > compatible with older QEMU bit stream which does not do >> >>> > > > > > > > this. >> >>> > > > > > > >> >>> > > > > > > If this is an existing bug, we could add a Fixes. >> >>> > > > > > >> >>> > > > > > It is an existing issue but only uncovered by this patch set. >> >>> > > > > > >> >>> > > > > > As far as I can see it was always there, so it would need some >> >>> > > > > > thought where to point that Fixes tag. >> >>> > > > > >> >>> > > > > If there's no way to trigger a real functional bug anyway, it's >> >>> > > > > also ok we >> >>> > > > > omit the Fixes. >> >>> > > > > >> >>> > > > > > > Two pure questions.. >> >>> > > > > > > >> >>> > > > > > > - What is the correct way to terminate the TLS session >> >>> > > > > > > without this flag? >> >>> > > > > > >> >>> > > > > > I guess one would need to call gnutls_bye() like in this >> >>> > > > > > GnuTLS example: >> >>> > > > > > https://gitlab.com/gnutls/gnutls/-/blob/2b8c3e4c71ad380bbbffb32e6003b34ecad596e3/doc/examples/ex-client-anon.c#L102 >> >>> > > > > > >> >>> > > > > > > - Why this is only needed by multifd sessions? >> >>> > > > > > >> >>> > > > > > What uncovered the issue was switching the load threads to >> >>> > > > > > using >> >>> > > > > > migrate_set_error() instead of their own result variable >> >>> > > > > > (load_threads_ret) which you had requested during the previous >> >>> > > > > > patch set version review: >> >>> > > > > > https://lore.kernel.org/qemu-devel/Z1DbH5fwBaxtgrvH@x1n/ >> >>> > > > > > >> >>> > > > > > Turns out that the multifd receive code always returned >> >>> > > > > > error in the TLS case, just nothing was previously checking for >> >>> > > > > > that error presence. >> >>> > > > > >> >>> > > > > What I was curious is whether this issue also exists for the >> >>> > > > > main migration >> >>> > > > > channel when with tls, especially when e.g. multifd not enabled >> >>> > > > > at all. As >> >>> > > > > I don't see anywhere that qemu uses gnutls_bye() for any tls >> >>> > > > > session. >> >>> > > > > >> >>> > > > > I think it's a good to find that we overlooked this before.. and >> >>> > > > > IMHO it's >> >>> > > > > always good we could fix this. >> >>> > > > > >> >>> > > > > Does it mean we need proper gnutls_bye() somewhere? >> >>> > > > > >> >>> > > > > If we need an explicit gnutls_bye(), then I wonder if that >> >>> > > > > should be done >> >>> > > > > on the main channel as well. >> >>> > > > >> >>> > > > That's a good question and looking at the code >> >>> > > > qemu_loadvm_state_main() exits >> >>> > > > on receiving "QEMU_VM_EOF" section (that's different from >> >>> > > > receiving socket EOF) >> >>> > > > and then optionally "QEMU_VM_VMDESCRIPTION" section is read with >> >>> > > > explicit size >> >>> > > > in qemu_loadvm_state() - so still not until channel EOF. >> >>> > > >> >>> > > I had a closer look, I do feel like such pre-mature termination is >> >>> > > caused >> >>> > > by explicit shutdown()s of the iochannels, looks like that can cause >> >>> > > issue >> >>> > > even after everything is sent. Then I noticed indeed multifd sender >> >>> > > iochannels will get explicit shutdown()s since commit 077fbb5942, >> >>> > > while we >> >>> > > don't do that for the main channel. Maybe that is a major >> >>> > > difference. >> >>> > > >> >>> > > Now I wonder whether we should shutdown() the channel at all if >> >>> > > migration >> >>> > > succeeded, because looks like it can cause tls session to interrupt >> >>> > > even if >> >>> > > the shutdown() is done after sent everything, and if so it'll >> >>> > > explain why >> >>> > > you hit the issue with tls. >> >>> > > >> >>> > > > >> >>> > > > Then I can't see anything else reading the channel until it is >> >>> > > > closed in >> >>> > > > migration_incoming_state_destroy(). >> >>> > > > >> >>> > > > So most likely the main migration channel will never read far >> >>> > > > enough to >> >>> > > > reach that GNUTLS_E_PREMATURE_TERMINATION error. >> >>> > > > >> >>> > > > > If we don't need gnutls_bye(), then should we always ignore >> >>> > > > > pre-mature >> >>> > > > > termination of tls no matter if it's multifd or non-multifd >> >>> > > > > channel (or >> >>> > > > > even a tls session that is not migration-related)? >> >>> > > > >> >>> > > > So basically have this patch extended to calling >> >>> > > > qio_channel_tls_set_premature_eof_okay() also on the main >> >>> > > > migration channel? >> >>> > > >> >>> > > If above theory can stand, then eof-okay could be a workaround >> >>> > > papering >> >>> > > over the real problem that we shouldn't always shutdown().. >> >>> > > >> >>> > > Could you have a look at below patch and see whether it can fix the >> >>> > > problem >> >>> > > you hit too, in replace of these two patches (including the previous >> >>> > > iochannel change)? >> >>> > > >> >>> > >> >>> > Unfortunately, the patch below does not fix the problem: >> >>> > > qemu-system-x86_64: Cannot read from TLS channel: The TLS connection >> >>> > > was non-properly terminated. >> >>> > > qemu-system-x86_64: Cannot read from TLS channel: The TLS connection >> >>> > > was non-properly terminated. >> >>> > >> >>> > I think that, even in the absence of shutdown(), if the sender does not >> >>> > call gnutls_bye() the TLS session is considered improperly terminated. >> >>> >> >>> Ah.. >> >>> >> >>> How about one more change on top of above change to disconnect properly >> >>> for >> >>> TLS? Something like gnutls_bye() in qio_channel_tls_close(), would that >> >>> make sense to you? >> >> >> >> Calling gnutls_bye from qio_channel_tls_close is not viable for the >> >> API contract of qio_channel_close. gnutls_bye needs to be able to >> >> perform I/O, which means we need to be able to tell the caller >> >> whether it needs to perform an event loop wait for POLLIN or POLLOUT. >> >> >> >> This is the same API design scenario as the gnutls_handshake method. >> >> As such I tdon't think it is practical to abstract it inside any >> >> existing QIOChannel API call, it'll have to be standalone like >> >> qio_channel_tls_handshake() is. >> >> >> > >> > I implemented the call to gnutls_bye: >> > https://gitlab.com/farosas/qemu/-/commits/migration-tls-bye >> > >> > Then while testing it I realised we actually have a regression from 9.2: >> > >> > 1d457daf86 ("migration/multifd: Further remove the SYNC on complete") >> > >> > It seems that patch somehow affected the ordering between src shutdown >> > vs. recv shutdown and now the recv channels are staying around to see >> > the connection being broken. Or something... I'm still looking into it. >> > >> >> Ok, so the issue is that the recv side would previously be stuck at the >> sync semaphore and multifd_recv_terminate_threads() would kick it only >> after 'exiting' was set, so no further recv() would happen. >> >> After the patch, there's no final sync anymore, so the recv thread loops >> around and waits at the recv() until multifd_send_terminate_threads() >> closes the connection. >> >> Waiting on sem_sync as before would lead to a cleaner termination >> process IMO, but I don't think it's worth the extra complexity of >> introducing a sync to the device state migration. >> >> So I think we'll have to go with one of the approaches suggested on this >> thread (gnutls_bye or premature_ok). I'm fine either way, but let's make >> sure we add a reference to the patch above and some words explaining the >> situation. >> >> (let me know if anyone prefers the gnutls_bye approach I have implemented >> and I can send a proper series) > > Good to know the progress. > > If that doesn't take a lot of time to provide a formal patch, IMO you > should go for it at least with an RFC; RFC is less likely to be completely > forgotten from thread discussions in all cases. >
I'll send it. But let's try to avoid making it a dependency for this series like the last time. That didn't work out so well I think. I'd suggest we prepare a simple fix to go along with the device state code, ideally something that doesn't interact with TLS at all. > Migration is not the only one using tls channels, so even if migration can > avoid depending on it, I wonder if gnutls_bye is a must if we want to make > sure QEMU is free from pre-mature termination attacks on other users. > > Thanks,