On 10/04/2022 19:43, Michal Prívozník wrote:
> On 10/4/22 12:24, Peng Liang wrote:
>>
>>
>> On 10/03/2022 15:38, Michal Prívozník wrote:
>>> On 10/1/22 05:35, Peng Liang wrote:
>>>>
>>>>
>>>> On 09/29/2022 21:31, Michal Prívozník wrote:
>>>>> On 9/27/22 17:38, Jiang Jiacheng wrote:
>>>>>> From: jiangjiacheng <jiangjiacheng(a)huawei.com>
>>>>>>
>>>>>> In virNetServerProgramDispatchCall, The arg is passed as a void*
and
>>>>>> used to point
>>>>>> to a certain struct depended on the dispatcher, so I think
it's the
>>>>>> memory of the
>>>>>> struct's member that leaks and this memory shuld be freed by
>>>>>> xdr_free.
>>>>>>
>>>>>> In virNetServerClientNew, client->rx is assigned by invoking
>>>>>> virNetServerClientNew,
>>>>>> but isn't freed if client->privateData's
initialization failed,
>>>>>> which
>>>>>> leads to a
>>>>>> memory leak. Thanks to Liang Peng's suggestion, put
>>>>>> virNetMessageFree(client->rx)
>>>>>> into virNetServerClientDispose() to release the memory.
>>>>>>
>>>>>> Signed-off-by: jiangjiacheng <jiangjiacheng(a)huawei.com>
>>>>>> ---
>>>>>> src/rpc/virnetserverclient.c | 2 ++
>>>>>> src/rpc/virnetserverprogram.c | 12 +++++++++---
>>>>>> 2 files changed, 11 insertions(+), 3 deletions(-)
>>>>>>
>>>>>> diff --git a/src/rpc/virnetserverclient.c
>>>>>> b/src/rpc/virnetserverclient.c
>>>>>> index a7d2dfa795..30f6af7be5 100644
>>>>>> --- a/src/rpc/virnetserverclient.c
>>>>>> +++ b/src/rpc/virnetserverclient.c
>>>>>> @@ -931,6 +931,8 @@ void virNetServerClientDispose(void *obj)
>>>>>> PROBE(RPC_SERVER_CLIENT_DISPOSE,
>>>>>> "client=%p", client);
>>>>>> + if (client->rx)
>>>>>> + virNetMessageFree(client->rx);
>>>>>> if (client->privateData)
>>>>>>
client->privateDataFreeFunc(client->privateData);
>>>>>
>>>>> Yeah, this one is a genuine memleak. IIUC it can be reproduced by:
>>>>>
>>>>> client = virNetServerClientNew(...);
>>>>> virObjectUnref(client);
>>>>>
>>>>>> diff --git a/src/rpc/virnetserverprogram.c
>>>>>> b/src/rpc/virnetserverprogram.c
>>>>>> index 3ddf9f0428..a813e821a3 100644
>>>>>> --- a/src/rpc/virnetserverprogram.c
>>>>>> +++ b/src/rpc/virnetserverprogram.c
>>>>>> @@ -409,11 +409,15 @@
>>>>>> virNetServerProgramDispatchCall(virNetServerProgram *prog,
>>>>>> if (virNetMessageDecodePayload(msg,
dispatcher->arg_filter,
>>>>>> arg) < 0)
>>>>>> goto error;
>>>>>> - if (!(identity =
virNetServerClientGetIdentity(client)))
>>>>>> + if (!(identity = virNetServerClientGetIdentity(client))) {
>>>>>> + xdr_free(dispatcher->arg_filter, arg);
>>>>>
>>>>> But here I believe we also need to free dispatcher->ret_filter:
>>>>>
>>>>> xdr_free(dispatcher->ret_filter, ret);
>>>>
>>>> Hi Michal,
>>>> I'm not sure why we need to free ret here. IIRC, xdr_free is to free
>>>> memory pointed by *ret instead of ret. For example, if ret is pointing
>>>> to a struct which contains a string, like:
>>>> struct Test {
>>>> char *str;
>>>> }
>>>> then I think xdr_free(dispatcher->ret_filter, ret) will free str
>>>> instead
>>>> of struct Test itself. So I think we will only need to call
>>>> xdr_free(dispatcher->ret_filter, ret) after we call
>>>> (dispatcher->func)(server, client, msg, &rerr, arg, ret).
>>>
>>> Oh, you're right. We need to call it only after dispatcher->func()
was
>>> called. That is when call to virIdentitySetCurrent(NULL) fails. Because
>>> at that point dispatcher->func() already populated ret.
>>
>> But there are some `goto error` before virIdentitySetCurrent(NULL) fails
>> originally, e.g.,
>> [1]
>>
https://gitlab.com/libvirt/libvirt/-/blob/master/src/rpc/virnetserverprog...
>> [2]
>>
https://gitlab.com/libvirt/libvirt/-/blob/master/src/rpc/virnetserverprog...
>> [3]
>>
https://gitlab.com/libvirt/libvirt/-/blob/master/src/rpc/virnetserverprog...
>> [4]
>>
https://gitlab.com/libvirt/libvirt/-/blob/master/src/rpc/virnetserverprog...
>> Since dispatcher is allocated already, xdr_free(dispatcher->ret_filter,
>> ret) will be called if we goto error from those scenarios after applying
>> Patch v3
>> (
https://listman.redhat.com/archives/libvir-list/2022-September/234558.html).
Will this cause invalid memory access?
>
> Ah, xdr_free() does not accept NULL. So we need something like this:
>
> if (dispatcher) {
> if (arg)
> xdr_free(dispatcher->arg_filter, arg);
> if (ret)
> xdr_free(dispatcher->ret_filter, ret);
> }
>
If we reach [2],[3],[4], however, then ret is allocated (but is not
populated). I think there is still a invalid memory access via
xdr_free(dispatcher->ret_filter, ret). Maybe the following will work?
free_ret:
xdr_free(dispatcher->arg_filter, ret);
free_arg:
xdr_free(dispatcher->arg_filter, arg);
then change all gotos after (dispatcher->func)(server, client, msg,
&rerr, arg, ret) to goto free_ret, and change [2], [3], [4] to goto
free_arg.
When I try to simulate this problem with the following diff I don't see any invalid
reads. Do you?
diff --git i/src/rpc/virnetserverprogram.c w/src/rpc/virnetserverprogram.c
index 212e0d5ab5..3fa88fafda 100644
--- i/src/rpc/virnetserverprogram.c
+++ w/src/rpc/virnetserverprogram.c
@@ -409,6 +409,8 @@ virNetServerProgramDispatchCall(virNetServerProgram *prog,
if (virNetMessageDecodePayload(msg, dispatcher->arg_filter, arg) < 0)
goto error;
+ if (rand()%10 == 0) goto error;
+
if (!(identity = virNetServerClientGetIdentity(client)))
goto error;
@@ -476,8 +478,10 @@ virNetServerProgramDispatchCall(virNetServerProgram *prog,
error:
if (dispatcher) {
- xdr_free(dispatcher->arg_filter, arg);
- xdr_free(dispatcher->ret_filter, ret);
+ if (arg)
+ xdr_free(dispatcher->arg_filter, arg);
+ if (ret)
+ xdr_free(dispatcher->ret_filter, ret);
}
/* Bad stuff (de-)serializing message, but we have an
Michal