aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorEric Blake <eblake@redhat.com>2016-06-15 11:37:51 -0600
committerMarkus Armbruster <armbru@redhat.com>2016-06-30 15:24:36 +0200
commit9b4e38fe6a35890bb1d995316d7be08de0b30ee5 (patch)
tree5c990a7fa28d667f3feb2fcf1f4ca48ca931cab4 /tests
parent01fb8e192d2cb139622df22983360836e39a64ff (diff)
qapi: Fix crash on missing alternate member of QAPI struct
If a QAPI struct has a mandatory alternate member which is not present on input, the input visitor reports an error for the missing alternate without setting the discriminator, but the cleanup code for the struct still tries to use the dealloc visitor to clean up the alternate. Commit dbf11922 changed visit_start_alternate to set *obj to NULL when an error occurs, where it was previously left untouched. Thus, before the patch, the dealloc visitor is blindly trying to cleanup whatever branch corresponds to (*obj)->type == 0 (that is, QTYPE_NONE, because *obj still pointed to zeroed memory), which selects the default branch of the switch and sets an error, but this second error is ignored by the way the dealloc visitor is used; but after the patch, the attempt to switch dereferences NULL. When cleaning up after a partial object parse, we specifically check for !*obj after visit_start_struct() (see gen_visit_object()); doing the same for alternates fixes the crash. Enhance the testsuite to give coverage for both missing struct and missing alternate members. Also add an abort - we expect visit_start_alternate() to either set an error or to set (*obj)->type to a valid QType that corresponds to actual user input, and QTYPE_NONE should never be reachable from valid input. Had the abort() been in place earlier, we might have noticed the dealloc visitor dereferencing bogus zeroed memory prior to when commit dbf11922 forced our hand by setting *obj to NULL and causing a fault. Test case: {'execute':'blockdev-add', 'arguments':{'options':{'driver':'raw'}}} The choice of 'driver':'raw' selects a BlockdevOptionsGenericFormat struct, which has a mandatory 'file':'BlockdevRef' in QAPI. Since 'file' is missing as a sibling of 'driver', this should report a graceful error rather than fault. After this patch, we are back to: {"error": {"class": "GenericError", "desc": "Parameter 'file' is missing"}} Generated code in qapi-visit.c changes as: |@@ -2444,6 +2444,9 @@ void visit_type_BlockdevRef(Visitor *v, | if (err) { | goto out; | } |+ if (!*obj) { |+ goto out_obj; |+ } | switch ((*obj)->type) { | case QTYPE_QDICT: | visit_start_struct(v, name, NULL, 0, &err); |@@ -2459,10 +2462,13 @@ void visit_type_BlockdevRef(Visitor *v, | case QTYPE_QSTRING: | visit_type_str(v, name, &(*obj)->u.reference, &err); | break; |+ case QTYPE_NONE: |+ abort(); | default: | error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | "BlockdevRef"); | } |+out_obj: | visit_end_alternate(v); Reported by Kashyap Chamarthy <kchamart@redhat.com> CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1466012271-5204-1-git-send-email-eblake@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Tested-by: Kashyap Chamarthy <kchamart@redhat.com> [Commit message tweaked] Signed-off-by: Markus Armbruster <armbru@redhat.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/test-qmp-input-visitor.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 3b6b39e297..1a4585c553 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -766,6 +766,8 @@ static void test_visitor_in_errors(TestInputVisitorData *data,
Error *err = NULL;
Visitor *v;
strList *q = NULL;
+ UserDefTwo *r = NULL;
+ WrapAlternate *s = NULL;
v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', "
"'string': -42 }");
@@ -778,6 +780,16 @@ static void test_visitor_in_errors(TestInputVisitorData *data,
visit_type_strList(v, NULL, &q, &err);
error_free_or_abort(&err);
assert(!q);
+
+ v = visitor_input_test_init(data, "{ 'str':'hi' }");
+ visit_type_UserDefTwo(v, NULL, &r, &err);
+ error_free_or_abort(&err);
+ assert(!r);
+
+ v = visitor_input_test_init(data, "{ }");
+ visit_type_WrapAlternate(v, NULL, &s, &err);
+ error_free_or_abort(&err);
+ assert(!s);
}
static void test_visitor_in_wrong_type(TestInputVisitorData *data,