[Spice-devel] [PATCH spice-server 1/5] Fix crash attempting to connect duplicate channels
Christophe Fergeau
cfergeau at redhat.com
Fri Aug 25 15:19:20 UTC 2017
On Fri, Aug 25, 2017 at 11:04:35AM -0400, Frediano Ziglio wrote:
> >
> > On Thu, Aug 24, 2017 at 03:37:16PM +0100, Frediano Ziglio wrote:
> > > You could easily trigger this issue using multiple monitors and
> > > a modified client with this patch:
> > >
> > > --- a/src/channel-main.c
> > > +++ b/src/channel-main.c
> > > @@ -1699,6 +1699,7 @@ static gboolean _channel_new(channel_new_t *c)
> > > {
> > > g_return_val_if_fail(c != NULL, FALSE);
> > >
> > > + if (c->type == SPICE_CHANNEL_DISPLAY) c->id = 0;
> > > spice_channel_new(c->session, c->type, c->id);
> > >
> > > g_object_unref(c->session);
> > >
> > >
> > > which cause a crash like
> > >
> > >
> > > (process:28742): Spice-WARNING **: Failed to create channel client: Client
> > > 0x40246f5d0: duplicate channel type 2 id 0
> > > 2017-08-24 09:36:57.451+0000: shutting down, reason=crashed
> > >
> > > One issue is that g_initable_new in this case returns NULL (dcc.c).
> >
> > I would split this commit in 2 parts, one simple NULL dereference fix,
> > and a second one with your watch changes.
> >
>
> Make sense.
> Actually I discovered the watch part trying to reproduce the NULL
> pointer dereference.
>
> > >
> > > The other is that the watch in RedsStream is supposed to be bound
> > > to the Qemu provided SpiceCoreInterface while RedChannelClient bound
> > > it to Glib one causing the crash when the watch is deleted from
> > > RedsStream. To avoid this a new watch is saved in
> > > RedChannelClientPrivate.
> > >
> > > Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> > > ---
> > > server/dcc.c | 4 +++-
> > > server/red-channel-client.c | 39 ++++++++++++++++++++++++++++-----------
> > > 2 files changed, 31 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/server/dcc.c b/server/dcc.c
> > > index 4490507b..2e1acd23 100644
> > > --- a/server/dcc.c
> > > +++ b/server/dcc.c
> > > @@ -516,7 +516,9 @@ DisplayChannelClient *dcc_new(DisplayChannel *display,
> > > NULL);
> > > spice_debug("New display (client %p) dcc %p stream %p", client, dcc,
> > > stream);
> > > common_graphics_channel_set_during_target_migrate(COMMON_GRAPHICS_CHANNEL(display),
> > > mig_target);
> > > - dcc->priv->id =
> > > common_graphics_channel_get_qxl(COMMON_GRAPHICS_CHANNEL(display))->id;
> > > + if (dcc) {
> > > + dcc->priv->id =
> > > common_graphics_channel_get_qxl(COMMON_GRAPHICS_CHANNEL(display))->id;
> > > + }
> > >
> > > return dcc;
> > > }
> > > diff --git a/server/red-channel-client.c b/server/red-channel-client.c
> > > index f1042dd4..1162ddca 100644
> > > --- a/server/red-channel-client.c
> > > +++ b/server/red-channel-client.c
> > > @@ -120,6 +120,7 @@ struct RedChannelClientPrivate
> > > RedChannel *channel;
> > > RedClient *client;
> > > RedsStream *stream;
> > > + SpiceWatch *watch;
> > > gboolean monitor_latency;
> > >
> > > struct {
> > > @@ -339,6 +340,20 @@ red_channel_client_finalize(GObject *object)
> > > {
> > > RedChannelClient *self = RED_CHANNEL_CLIENT(object);
> > >
> > > + SpiceCoreInterfaceInternal *core =
> > > red_channel_get_core_interface(self->priv->channel);
> > > + if (self->priv->watch) {
> > > + core->watch_remove(core, self->priv->watch);
> > > + self->priv->watch = NULL;
> > > + }
> > > + if (self->priv->latency_monitor.timer) {
> > > + core->timer_remove(core, self->priv->latency_monitor.timer);
> > > + self->priv->latency_monitor.timer = NULL;
> > > + }
> > > + if (self->priv->connectivity_monitor.timer) {
> > > + core->timer_remove(core, self->priv->connectivity_monitor.timer);
> > > + self->priv->connectivity_monitor.timer = NULL;
> > > + }
> > > +
> >
> > These latency_monitor/connectivity_monitor hunks seem unrelated to this
> > patch?
>
> When a double channel is registered the second get build, register
> watches/timers then fail to be inserted into RedClient and got freed.
> As the timers/watches points to the object if you don't unregister
> them they will use a freed object.
>
> Maybe all these unregistering should be on a single function?
This still sounds like a separate commit
"Stop latency/connectivity monitors when SpiceChannel is destroyed". And
yes this could be factored with the code in _disconnect() if this is
what you are asking.
>
> > Apart from this, looks good to me, though I don't really like this whole
> > RedsStream API :-/
> >
> > Christophe
> >
>
> Fully agree I think the RedsStream API should be a low core API not
> related to RedChannel. Actually I think should not even know what is
> RedsState/SpiceServer.
>
> On the other side an authenticated client can cause the crash of
> the VM with this method. Still authenticated but the shutdown
> may require admin privileges.
>
> One option would be to pass also (and allows to change) the
> SpiceCoreInterface to RedsStream so it can release the watch
> more safely with the right interface used to build the watch.
>
> Not sure how easy is to remove watch from RedsStream (if make
> sense).
>
> Any suggestion are welcome on the API.
Solution might be to gut all of that and to use GSocket or GIOChannel ;)
Not sure it's worth spending a lot of time on RedsStream right now,
the way you address things should be good enough for now.
Christophe
More information about the Spice-devel
mailing list