[PATCH v2 03/12] qio: Unwatch before notify in QIONetListener

Eric Blake posted 12 patches 1 week ago
Maintainers: Eric Blake <eblake@redhat.com>, Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>, Kevin Wolf <kwolf@redhat.com>, Hanna Reitz <hreitz@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, Peter Xu <peterx@redhat.com>, Fabiano Rosas <farosas@suse.de>
[PATCH v2 03/12] qio: Unwatch before notify in QIONetListener
Posted by Eric Blake 1 week ago
When changing the callback registered with QIONetListener, the code
was calling notify on the old opaque data prior to actually removing
the old GSource objects still pointing to that data.  Similarly,
during finalize, it called notify before tearing down the various
GSource objects tied to the data.

In practice, a grep of the QEMU code base found that every existing
client of QIONetListener passes in a NULL notifier (the opaque data,
if non-NULL, outlives the NetListener and so does not need cleanup
when the NetListener is torn down), so this patch has no impact.  And
even if a caller had passed in a reference-counted object with a
notifier of object_unref but kept its own reference on the data, then
the early notify would merely reduce a refcount from (say) 2 to 1, but
not free the object.  However, it is a latent bug waiting to bite any
future caller that passes in data where the notifier actually frees
the object, because the GSource could then trigger a use-after-free if
it loses the race on a last-minute client connection resulting in the
data being passed to one final use of the async callback.

Better is to delay the notify call until after all GSource that have
been given a copy of the opaque data are torn down.

CC: qemu-stable@nongnu.org
Fixes: 530473924d "io: introduce a network socket listener API", v2.12.0
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v2: new patch, split out from 4/8 to leave that one as just pure
refactoring, and call attention to this being a latent bug fix
---
 io/net-listener.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/io/net-listener.c b/io/net-listener.c
index 007acbd5b18..d71b65270e0 100644
--- a/io/net-listener.c
+++ b/io/net-listener.c
@@ -148,13 +148,6 @@ void qio_net_listener_set_client_func_full(QIONetListener *listener,

     trace_qio_net_listener_unwatch(listener, listener->io_func,
                                    "set_client_func");
-    if (listener->io_notify) {
-        listener->io_notify(listener->io_data);
-    }
-    listener->io_func = func;
-    listener->io_data = data;
-    listener->io_notify = notify;
-
     for (i = 0; i < listener->nsioc; i++) {
         if (listener->io_source[i]) {
             g_source_destroy(listener->io_source[i]);
@@ -163,6 +156,13 @@ void qio_net_listener_set_client_func_full(QIONetListener *listener,
         }
     }

+    if (listener->io_notify) {
+        listener->io_notify(listener->io_data);
+    }
+    listener->io_func = func;
+    listener->io_data = data;
+    listener->io_notify = notify;
+
     trace_qio_net_listener_watch(listener, listener->io_func,
                                  "set_client_func");
     if (listener->io_func != NULL) {
@@ -300,10 +300,10 @@ static void qio_net_listener_finalize(Object *obj)
     QIONetListener *listener = QIO_NET_LISTENER(obj);
     size_t i;

+    qio_net_listener_disconnect(listener);
     if (listener->io_notify) {
         listener->io_notify(listener->io_data);
     }
-    qio_net_listener_disconnect(listener);

     for (i = 0; i < listener->nsioc; i++) {
         object_unref(OBJECT(listener->sioc[i]));
-- 
2.51.1
Re: [PATCH v2 03/12] qio: Unwatch before notify in QIONetListener
Posted by Daniel P. Berrangé 5 days, 8 hours ago
On Sat, Nov 08, 2025 at 04:59:24PM -0600, Eric Blake wrote:
> When changing the callback registered with QIONetListener, the code
> was calling notify on the old opaque data prior to actually removing
> the old GSource objects still pointing to that data.  Similarly,
> during finalize, it called notify before tearing down the various
> GSource objects tied to the data.
> 
> In practice, a grep of the QEMU code base found that every existing
> client of QIONetListener passes in a NULL notifier (the opaque data,
> if non-NULL, outlives the NetListener and so does not need cleanup
> when the NetListener is torn down), so this patch has no impact.  And
> even if a caller had passed in a reference-counted object with a
> notifier of object_unref but kept its own reference on the data, then
> the early notify would merely reduce a refcount from (say) 2 to 1, but
> not free the object.  However, it is a latent bug waiting to bite any
> future caller that passes in data where the notifier actually frees
> the object, because the GSource could then trigger a use-after-free if
> it loses the race on a last-minute client connection resulting in the
> data being passed to one final use of the async callback.
> 
> Better is to delay the notify call until after all GSource that have
> been given a copy of the opaque data are torn down.
> 
> CC: qemu-stable@nongnu.org
> Fixes: 530473924d "io: introduce a network socket listener API", v2.12.0
> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> ---
> v2: new patch, split out from 4/8 to leave that one as just pure
> refactoring, and call attention to this being a latent bug fix
> ---
>  io/net-listener.c | 16 ++++++++--------
>  1 file changed, 8 insertions(+), 8 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|