[libvirt] [PATCH] remote_daemon_stream: Hold an extra reference to stream in daemonStreamFilter

Lance Liu liu.lance.89 at gmail.com
Thu Nov 28 03:09:02 UTC 2019


Hi, Michal:
    I think  the daemonFreeClientStream(NULL, stream);  in your patch
should line above   virMutexUnlock(&stream->priv->lock), or else there is
issue WAW. That is two threads may sub stream->

Lance Liu <liu.lance.89 at gmail.com> 于2019年11月26日周二 下午1:54写道:

> Yeah, your patch is perfectly fine for stream freed problem.
> But I have reviewed code in daemonStreamEvent() and daemonStreamFilter()
> again, and I think there still two issue need to be reconsidered:
> 1. stream->ref only ++ in daemonStreamFilter, except for error
> occurred call daemonRemoveClientStream() lead to ref--, like code as
> follow. Though code won't be executed because of no VIR_STREAM_EVENT_HANGUP
> event taken place,
> but it is not good code:
> /* If we got HANGUP, we need to only send an empty
>      * packet so the client sees an EOF and cleans up
>      */
>     if (!stream->closed && !stream->recvEOF &&
>         (events & VIR_STREAM_EVENT_HANGUP)) {
>         virNetMessagePtr msg;
>         events &= ~(VIR_STREAM_EVENT_HANGUP);
>         stream->tx = false;
>         stream->recvEOF = true;
>         if (!(msg = virNetMessageNew(false))) {
>             daemonRemoveClientStream(client, stream);
>             virNetServerClientClose(client);
>             goto cleanup;
>         }
>         msg->cb = daemonStreamMessageFinished;
>         msg->opaque = stream;
>         stream->refs++;
>         if (virNetServerProgramSendStreamData(stream->prog,
>                                               client,
>                                               msg,
>                                               stream->procedure,
>                                               stream->serial,
>                                               "", 0) < 0) {
>             virNetMessageFree(msg);
>             daemonRemoveClientStream(client, stream);
>             virNetServerClientClose(client);
>             goto cleanup;
>         }
>     }
> 2. call virNetServerClientClose() is still inappropriate in  because the
> it may free the client resource which other threads need ref, may be
> replace it with virNetServerClientImmediateClose() is better,
> the daemon can process client resource release unified
> 3. Segmentfault may still exists when another thread
> call remoteClientCloseFunc in virNetServerClientClose()(for example, when
> new force console session break down existed console session, and existed
> console session
> 's client close the session simutaneously, but remoteClientCloseFunc not
> lock(priv->lock), so the resource maybe released when daemonStreamEvent ref)
>
> How do you see it?
>
>
> Best Regards!
>
>
>
>
> Michal Privoznik <mprivozn at redhat.com> 于2019年11月26日周二 上午12:49写道:
>
>> In v5.9.0-273-g8ecab214de I've tried to fix a lock ordering
>> problem, but introduced a crasher. Problem is that because the
>> client lock is unlocked (in order to honour lock ordering) the
>> stream we are currently checking in daemonStreamFilter() might be
>> freed and thus stream->priv might not even exist when the control
>> get to virMutexLock() call.
>>
>> To resolve this, grab an extra reference to the stream and handle
>> its cleanup should the refcounter reach zero after the deref.
>> If that's the case and we are the only ones holding a reference
>> to the stream, we MUST return a positive value to make
>> virNetServerClientDispatchRead() break its loop where it iterates
>> over filters. The problem is, if we did not do so, then
>> "filter = filter->next" line will read from a memory that was
>> just freed (freeing a stream also unregisters its filter).
>>
>> Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
>> ---
>>
>> Reproducing this issue is very easy:
>>
>> 1) put sleep(5) right after virObjectUnlock(client) in the fist hunk,
>> 2) virsh console --force $dom   and type something so that the stream
>>    has some data to process
>> 3) while 2) is still running, run the same command from another terminal
>> 4) observe libvirtd crash
>>
>>  src/remote/remote_daemon_stream.c | 19 +++++++++++++++++++
>>  1 file changed, 19 insertions(+)
>>
>> diff --git a/src/remote/remote_daemon_stream.c
>> b/src/remote/remote_daemon_stream.c
>> index 82cadb67ac..73e4d7befb 100644
>> --- a/src/remote/remote_daemon_stream.c
>> +++ b/src/remote/remote_daemon_stream.c
>> @@ -293,10 +293,25 @@ daemonStreamFilter(virNetServerClientPtr client,
>>      daemonClientStream *stream = opaque;
>>      int ret = 0;
>>
>> +    /* We must honour lock ordering here. Client private data lock must
>> +     * be acquired before client lock. Bu we are already called with
>> +     * client locked. To avoid stream disappearing while we unlock
>> +     * everything, let's increase its refcounter. This has some
>> +     * implications though. */
>> +    stream->refs++;
>>      virObjectUnlock(client);
>>      virMutexLock(&stream->priv->lock);
>>      virObjectLock(client);
>>
>> +    if (stream->refs == 1) {
>> +        /* So we are the only ones holding the reference to the stream.
>> +         * Return 1 to signal to the caller that we've processed the
>> +         * message. And to "process" means free. */
>> +        virNetMessageFree(msg);
>> +        ret = 1;
>> +        goto cleanup;
>> +    }
>> +
>>      if (msg->header.type != VIR_NET_STREAM &&
>>          msg->header.type != VIR_NET_STREAM_HOLE)
>>          goto cleanup;
>> @@ -318,6 +333,10 @@ daemonStreamFilter(virNetServerClientPtr client,
>>
>>   cleanup:
>>      virMutexUnlock(&stream->priv->lock);
>> +    /* Don't pass client here, because client is locked here and this
>> +     * function might try to lock it again which would result in a
>> +     * deadlock. */
>> +    daemonFreeClientStream(NULL, stream);
>>      return ret;
>>  }
>>
>> --
>> 2.23.0
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20191128/3c771bc1/attachment-0001.htm>


More information about the libvir-list mailing list