[Spice-devel] [PATCH spice-server 2/2] Fix crash attempting to connect duplicate channels

Frediano Ziglio fziglio at redhat.com
Wed Aug 30 08:53:31 UTC 2017


> 
> > On 30 Aug 2017, at 10:19, Frediano Ziglio <fziglio at redhat.com> 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);
> 
> So what you are doing with this change is create multiple channels
> with the same id. Is that valid? Why would you want to do that
> legitimately?
> 

Because I'm the bad guy! :-)

If you expect that everybody is good with you... well...
then there are no security issue, right?
In this case an authenticated user (maybe a normal one) can
crash (so shutdown) the guest (which require admin privileges).

But beside that a robust code should handle unexpected
conditions, at the end anything can come from the network.

> If it’s not valid, shouldn’t we rather assert at creation time?
> Would that also fix the issue you were seeing?
> 

assert -> crash ?? why do I have to crash the VM ?

> I’m trying to understand if the condition your patch is addressing
> is legitimate today, or if there is a reason to make it legitimate later,
> or if it’s forever a “don’t go there” scenario :-)
> 
> > 
> >     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
> > 
> > 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.
> 
> For my education, could you explain why it’s harmless to move from a
> per-stream watch to a per-channel client watch?
> 
> 

Is just that RedsStream assume that the stream is attached to
the main SpiceCoreInterface which is different from the RCC one.
This difference basically causes the code to call a function
(provided by spice server user) passing an invalid pointer.
So storing the watch in RCC fix this assumption.

As RedsStream is attached to a single RCC both watches would
be in theory per-channel.

Another option would be to store the right SpiceCoreInterface
inside RedsStream.

> 
> > 
> > Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> > ---
> > server/red-channel-client.c | 39 ++++++++++++++++++++++++++++-----------
> > 1 file changed, 28 insertions(+), 11 deletions(-)
> > 
> > diff --git a/server/red-channel-client.c b/server/red-channel-client.c
> > index 145ba43f..65392ed1 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;
> > +    }
> > +
> >     reds_stream_free(self->priv->stream);
> >     self->priv->stream = NULL;
> > 
> > @@ -922,7 +937,7 @@ static gboolean
> > red_channel_client_initable_init(GInitable *initable,
> > 
> >     core = red_channel_get_core_interface(self->priv->channel);
> >     if (self->priv->stream)
> > -        self->priv->stream->watch =
> > +        self->priv->watch =
> >             core->watch_add(core, self->priv->stream->socket,
> >                             SPICE_WATCH_EVENT_READ,
> >                             red_channel_client_event,
> > @@ -1003,9 +1018,11 @@ void red_channel_client_destroy(RedChannelClient
> > *rcc)
> > void red_channel_client_shutdown(RedChannelClient *rcc)
> > {
> >     if (rcc->priv->stream && !rcc->priv->stream->shutdown) {
> > -        SpiceCoreInterfaceInternal *core =
> > red_channel_get_core_interface(rcc->priv->channel);
> > -        core->watch_remove(core, rcc->priv->stream->watch);
> > -        rcc->priv->stream->watch = NULL;
> > +        if (rcc->priv->watch) {
> > +            SpiceCoreInterfaceInternal *core =
> > red_channel_get_core_interface(rcc->priv->channel);
> > +            core->watch_remove(core, rcc->priv->watch);
> > +            rcc->priv->watch = NULL;
> > +        }
> >         shutdown(rcc->priv->stream->socket, SHUT_RDWR);
> >         rcc->priv->stream->shutdown = TRUE;
> >     }
> > @@ -1294,10 +1311,10 @@ void red_channel_client_push(RedChannelClient *rcc)
> >         red_channel_client_send_item(rcc, pipe_item);
> >     }
> >     if (red_channel_client_no_item_being_sent(rcc) &&
> >     g_queue_is_empty(&rcc->priv->pipe)
> > -        && rcc->priv->stream->watch) {
> > +        && rcc->priv->watch) {
> >         SpiceCoreInterfaceInternal *core;
> >         core = red_channel_get_core_interface(rcc->priv->channel);
> > -        core->watch_update_mask(core, rcc->priv->stream->watch,
> > +        core->watch_update_mask(core, rcc->priv->watch,
> >                                 SPICE_WATCH_EVENT_READ);
> >     }
> >     rcc->priv->during_send = FALSE;
> > @@ -1511,10 +1528,10 @@ static inline gboolean
> > prepare_pipe_add(RedChannelClient *rcc, RedPipeItem *item
> >         red_pipe_item_unref(item);
> >         return FALSE;
> >     }
> > -    if (g_queue_is_empty(&rcc->priv->pipe) && rcc->priv->stream->watch) {
> > +    if (g_queue_is_empty(&rcc->priv->pipe) && rcc->priv->watch) {
> >         SpiceCoreInterfaceInternal *core;
> >         core = red_channel_get_core_interface(rcc->priv->channel);
> > -        core->watch_update_mask(core, rcc->priv->stream->watch,
> > +        core->watch_update_mask(core, rcc->priv->watch,
> >                                 SPICE_WATCH_EVENT_READ |
> >                                 SPICE_WATCH_EVENT_WRITE);
> >     }
> >     return TRUE;
> > @@ -1678,9 +1695,9 @@ void red_channel_client_disconnect(RedChannelClient
> > *rcc)
> >     spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel,
> >                    type, id);
> >     red_channel_client_pipe_clear(rcc);
> > -    if (rcc->priv->stream->watch) {
> > -        core->watch_remove(core, rcc->priv->stream->watch);
> > -        rcc->priv->stream->watch = NULL;
> > +    if (rcc->priv->watch) {
> > +        core->watch_remove(core, rcc->priv->watch);
> > +        rcc->priv->watch = NULL;
> >     }
> >     if (rcc->priv->latency_monitor.timer) {
> >         core->timer_remove(core, rcc->priv->latency_monitor.timer);

Frediano


More information about the Spice-devel mailing list