Hi On Mon, Mar 28, 2022 at 12:22 PM Hogan Wang via <qemu-devel@nongnu.org> wrote:
> IOWatchPoll object did not hold the @ioc and @src objects reference, > then io_watch_poll_prepare execute in IO thread, if IOWatchPoll > removed by mian thread, io_watch_poll_prepare may execute at last > chance and access the freed @ioc or @src object. > > In IO thread monitor scene, the IO thread used to accept client, > receive qmp request and handle hung-up event. Main thread used to > handle qmp request and send response, it will remove IOWatchPoll > and free @ioc when send response fail, then cause use-after-free > like this: > > (gdb) bt > 0 0x00007f4d121c8edf in g_source_remove_child_source > (source=0x7f4c58003560, child_source=0x7f4c58009b10) > 1 0x00007f4d11e0705c in io_watch_poll_prepare (source=0x7f4c58003560, > timeout=timeout@entry=0x7f4c7fffed94 > 2 0x00007f4d121ca419 in g_main_context_prepare > (context=context@entry=0x55a1463f8260, > priority=priority@entry=0x7f4c7fffee20) > 3 0x00007f4d121cadeb in g_main_context_iterate (context=0x55a1463f8260, > block=block@entry=1, dispatch=dispatch@entry=1, self=self@entry > =0x7f4c94002260) > 4 0x00007f4d121cb21d in g_main_loop_run (loop=0x55a146c90920) > 5 0x00007f4d11de3ea1 in iothread_run (opaque=0x55a146411820) > 6 0x00007f4d11d77470 in qemu_thread_start (args=0x55a146b1f3c0) > 7 0x00007f4d11f2ef3b in ?? () from /usr/lib64/libpthread.so.0 > 8 0x00007f4d120ba550 in clone () from /usr/lib64/libc.so.6 > (gdb) p iwp > $1 = (IOWatchPoll *) 0x7f4c58003560 > (gdb) p *iwp > $2 = {parent = {callback_data = 0x0, > callback_funcs = 0x0, > source_funcs = 0x7f4d11f10760 <io_watch_poll_funcs>, > ref_count = 1, > context = 0x55a1463f8260, > priority = 0, > flags = 0, > source_id = 544, > poll_fds = 0x0, > prev = 0x55a147a47a30, > next = 0x55a14712fb80, > name = 0x7f4c580036d0 "chardev-iowatch-charmonitor", > priv = 0x7f4c58003060}, > ioc = 0x7f4c580033f0, > src = 0x7f4c58009b10, > fd_can_read = 0x7f4d11e0a5d0 <tcp_chr_read_poll>, > fd_read = 0x7f4d11e0a380 <tcp_chr_read>, > opaque = 0x55a1463aeea0 } > (gdb) p iwp->ioc > $3 = (QIOChannel *) 0x7f4c580033f0 > (gdb) p *iwp->ioc > $4 = {parent = {class = 0x55a14707f400, > free = 0x55a146313010, > properties = 0x55a147b37b60, > ref = 0, > parent = 0x0}, > features = 3, > name = 0x7f4c580085f0 "\240F", > ctx = 0x0, > read_coroutine = 0x0, > write_coroutine = 0x0} > > Solution: IOWatchPoll object hold the @ioc and @src objects reference > and release the reference in GSource finalize callback function. > > Signed-off-by: Hogan Wang <hogan.w...@huawei.com> > --- > chardev/char-io.c | 17 ++++++++++++++++- > 1 file changed, 16 insertions(+), 1 deletion(-) > > diff --git a/chardev/char-io.c b/chardev/char-io.c > index 8ced184160..3af388eeaa 100644 > --- a/chardev/char-io.c > +++ b/chardev/char-io.c > @@ -55,9 +55,9 @@ static gboolean io_watch_poll_prepare(GSource *source, > iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); > g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL); > g_source_add_child_source(source, iwp->src); > - g_source_unref(iwp->src); > } else { > g_source_remove_child_source(source, iwp->src); > + g_source_unref(iwp->src); > This change looks unnecessary. According to g_source_add_child_source() documentation: *"**source* will hold a reference on *child_source* while *child_source* is attached to it." > iwp->src = NULL; > } > return FALSE; > @@ -69,9 +69,23 @@ static gboolean io_watch_poll_dispatch(GSource *source, > GSourceFunc callback, > return G_SOURCE_CONTINUE; > } > > +static void io_watch_poll_finalize(GSource *source) > +{ > + IOWatchPoll *iwp = io_watch_poll_from_source(source); > + if (iwp->src) { > + g_source_unref(iwp->src); > (see above) > + iwp->src = NULL; > + } > + if (iwp->ioc) { > + object_unref(OBJECT(iwp->ioc)); > + iwp->ioc = NULL; > + } > replace with g_clear_pointer(&iwp->ioc, object_unref); > +} > + > static GSourceFuncs io_watch_poll_funcs = { > .prepare = io_watch_poll_prepare, > .dispatch = io_watch_poll_dispatch, > + .finalize = io_watch_poll_finalize, > }; > > GSource *io_add_watch_poll(Chardev *chr, > @@ -88,6 +102,7 @@ GSource *io_add_watch_poll(Chardev *chr, > sizeof(IOWatchPoll)); > iwp->fd_can_read = fd_can_read; > iwp->opaque = user_data; > + object_ref(OBJECT(ioc)); > iwp->ioc = ioc; > please use "iwp->ioc = object_ref(ioc)" > iwp->fd_read = (GSourceFunc) fd_read; > iwp->src = NULL; > -- > 2.33.0 > > > Daniel, please review thanks -- Marc-André Lureau