From: Markus Armbruster <armbru@redhat.com>
To: Eric Blake <eblake@redhat.com>
Cc: qemu-devel@nongnu.org, mdroth@linux.vnet.ibm.com
Subject: Re: [PATCH 10/13] qapi: Clean up visitor's recovery from input with invalid type
Date: Fri, 24 Apr 2020 09:44:45 +0200 [thread overview]
Message-ID: <877dy5pbte.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <89636246-6a5c-476d-6016-977f39cb653c@redhat.com> (Eric Blake's message of "Thu, 23 Apr 2020 13:18:04 -0500")
Eric Blake <eblake@redhat.com> writes:
> On 4/23/20 1:06 PM, Eric Blake wrote:
>> On 4/23/20 11:00 AM, Markus Armbruster wrote:
>>> An alternate type's visit_type_FOO() fails when it runs into an
>>> invalid ->type. If it's an input visit, we then need to free the the
>>> object we got from visit_start_alternate(). We do that with
>>> qapi_free_FOO(), which uses the dealloc visitor.
>>>
>>> Trouble is that object is in a bad state: its ->type is invalid. So
>>> the dealloc visitor will run into the same error again, and the error
>>> recovery skips deallocating the alternate's (invalid) alternative.
>>> This is a roundabout way to g_free() the alternate.
>>>
>>> Simplify: replace the qapi_free_FOO() by g_free().
>>>
>>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>>> ---
>>> scripts/qapi/visit.py | 2 +-
>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>
>> Required looking at what gets generated into qapi_free_FOO() as well
>> as when visit_start_alternate() can fail, but makes sense.
>>
>> Reviewed-by: Eric Blake <eblake@redhat.com>
>
> Actually, I'm having second thoughts. As an example, look at the generated:
>
>> void visit_type_BlockDirtyBitmapMergeSource(Visitor *v, const char *name, BlockDirtyBitmapMergeSource **obj, Error **errp)
>> {
>> Error *err = NULL;
>>
>> visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj),
>> &err);
>> if (err) {
>> goto out;
>> }
>> if (!*obj) {
>> goto out_obj;
> [1]
>> }
>> switch ((*obj)->type) {
>> case QTYPE_QSTRING:
>> visit_type_str(v, name, &(*obj)->u.local, &err);
> [2]
>> break;
>> case QTYPE_QDICT:
>> visit_start_struct(v, name, NULL, 0, &err);
>> if (err) {
>> break;
> [3]
>> }
>> visit_type_BlockDirtyBitmap_members(v, &(*obj)->u.external, &err);
>> if (!err) {
>> visit_check_struct(v, &err);
> [4]
>> }
>> visit_end_struct(v, NULL);
>> break;
>> case QTYPE_NONE:
>> abort();
>> default:
>> error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>> "BlockDirtyBitmapMergeSource");
> [5]
>> }
>> out_obj:
>> visit_end_alternate(v, (void **)obj);
>> if (err && visit_is_input(v)) {
>> qapi_free_BlockDirtyBitmapMergeSource(*obj);
>
> If we got here, we must have failed at any of the points mentioned above.
>
> If [1], visit_start_alternate() failed, but *obj is NULL and both
> qapi_free_FOO(NULL) and g_free(NULL) are safe.
>
> If [2], visit_type_str() failed, so *obj is allocated but the embedded
> string (here, u.local) was left NULL. qapi_free_FOO() then does
> nothing further than g_free(obj).
>
> If [3], visit_start_struct() failed, the embedded dict (here,
> u.external) was left NULL. qapi_free_FOO() then does nothing further
> than g_free(obj).
>
> If [5], we have the wrong ->type. As pointed out by this commit,
> qapi_free_FOO() does nothing further than g_free(obj).
>
> But what happens in [4]? Here, the embedded dict was allocated, but
> we then failed while parsing its members. That leaves us in a
> partially-allocated state, and g_free(NULL) does NOT recursively visit
> that partial allocation. I think this patch is prone to a memory leak
> unless you _also_ patch things to free any dict branch on failure
> (perhaps during the QTYPE_QDICT case label, rather than here at the
> end).
You're right.
Let's change cleanup only for the default case, like this:
default:
error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"BlockDirtyBitmapMergeSource");
+ g_free(*obj);
+ *obj = NULL;
}
out_obj:
visit_end_alternate(v, (void **)obj);
if (err && visit_is_input(v)) {
qapi_free_BlockDirtyBitmapMergeSource(*obj);
*obj = NULL;
}
out:
error_propagate(errp, err);
}
Thanks!
next prev parent reply other threads:[~2020-04-24 7:45 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-23 16:00 [PATCH 00/13] qapi: Spring cleaning Markus Armbruster
2020-04-23 16:00 ` [PATCH 01/13] qapi: Belatedly update visitor.h's big comment for QAPI modules Markus Armbruster
2020-04-23 17:33 ` Eric Blake
2020-04-23 16:00 ` [PATCH 02/13] qapi: Fix the virtual walk example in visitor.h's big comment Markus Armbruster
2020-04-23 17:33 ` Eric Blake
2020-04-23 16:00 ` [PATCH 03/13] qapi: Fix typo in visit_start_list()'s contract Markus Armbruster
2020-04-23 17:34 ` Eric Blake
2020-04-23 16:00 ` [PATCH 04/13] qapi: Document @errp usage more thoroughly in visitor.h Markus Armbruster
2020-04-23 17:35 ` Eric Blake
2020-04-23 16:00 ` [PATCH 05/13] qapi: Polish prose " Markus Armbruster
2020-04-23 17:51 ` Eric Blake
2020-04-23 16:00 ` [PATCH 06/13] qapi: Assert incomplete object occurs only in dealloc visitor Markus Armbruster
2020-04-23 17:52 ` Eric Blake
2020-04-23 16:00 ` [PATCH 07/13] qapi: Fix Visitor contract for start_alternate() Markus Armbruster
2020-04-23 17:53 ` Eric Blake
2020-04-23 16:00 ` [PATCH 08/13] qapi: Assert output visitors see only valid enum values Markus Armbruster
2020-04-23 17:56 ` Eric Blake
2020-04-24 7:31 ` Markus Armbruster
2020-04-23 16:00 ` [PATCH 09/13] qapi: Assert non-input visitors see only valid narrow integers Markus Armbruster
2020-04-23 17:57 ` Eric Blake
2020-04-23 16:00 ` [PATCH 10/13] qapi: Clean up visitor's recovery from input with invalid type Markus Armbruster
2020-04-23 18:06 ` Eric Blake
2020-04-23 18:18 ` Eric Blake
2020-04-24 7:44 ` Markus Armbruster [this message]
2020-04-23 16:00 ` [PATCH 11/13] qapi: Assert non-input visitors see only valid alternate tags Markus Armbruster
2020-04-23 18:09 ` Eric Blake
2020-04-23 16:00 ` [PATCH 12/13] qapi: Only input visitors can actually fail Markus Armbruster
2020-04-23 18:31 ` Eric Blake
2020-04-23 16:00 ` [PATCH 13/13] qom: Simplify object_property_get_enum() Markus Armbruster
2020-04-23 18:40 ` Eric Blake
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=877dy5pbte.fsf@dusky.pond.sub.org \
--to=armbru@redhat.com \
--cc=eblake@redhat.com \
--cc=mdroth@linux.vnet.ibm.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.