]> git.proxmox.com Git - mirror_qemu.git/blame - qapi/qmp-input-visitor.c
qapi: Fix crash when 'any' or 'null' parameter is missing
[mirror_qemu.git] / qapi / qmp-input-visitor.c
CommitLineData
c40cc0a0
MR
1/*
2 * Input Visitor
3 *
08f9541d 4 * Copyright (C) 2012-2016 Red Hat, Inc.
c40cc0a0
MR
5 * Copyright IBM, Corp. 2011
6 *
7 * Authors:
8 * Anthony Liguori <aliguori@us.ibm.com>
9 *
10 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
11 * See the COPYING.LIB file in the top-level directory.
12 *
13 */
14
cbf21151 15#include "qemu/osdep.h"
da34e65c 16#include "qapi/error.h"
7b1b5d19
PB
17#include "qapi/qmp-input-visitor.h"
18#include "qapi/visitor-impl.h"
1de7afc9 19#include "qemu/queue.h"
c40cc0a0 20#include "qemu-common.h"
7b1b5d19
PB
21#include "qapi/qmp/types.h"
22#include "qapi/qmp/qerror.h"
c40cc0a0
MR
23
24#define QIV_STACK_SIZE 1024
25
26typedef struct StackObject
27{
b471d012 28 QObject *obj; /* Object being visited */
1158bb2a 29 void *qapi; /* sanity check that caller uses same pointer */
b471d012
EB
30
31 GHashTable *h; /* If obj is dict: unvisited keys */
32 const QListEntry *entry; /* If obj is list: unvisited tail */
3d344c2a
PB
33
34 QSLIST_ENTRY(StackObject) node;
c40cc0a0
MR
35} StackObject;
36
37struct QmpInputVisitor
38{
39 Visitor visitor;
b471d012 40
ce140b17
EB
41 /* Root of visit at visitor creation. */
42 QObject *root;
43
44 /* Stack of objects being visited (all entries will be either
45 * QDict or QList). */
3d344c2a 46 QSLIST_HEAD(, StackObject) stack;
b471d012
EB
47
48 /* True to reject parse in visit_end_struct() if unvisited keys remain. */
e38ac962 49 bool strict;
c40cc0a0
MR
50};
51
52static QmpInputVisitor *to_qiv(Visitor *v)
53{
54 return container_of(v, QmpInputVisitor, visitor);
55}
56
4faaec6a 57static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
e8316d7e
KW
58 const char *name,
59 bool consume)
c40cc0a0 60{
ce140b17
EB
61 StackObject *tos;
62 QObject *qobj;
e5826a2f 63 QObject *ret;
b471d012 64
3d344c2a 65 if (QSLIST_EMPTY(&qiv->stack)) {
ce140b17
EB
66 /* Starting at root, name is ignored. */
67 return qiv->root;
68 }
69
70 /* We are in a container; find the next element. */
3d344c2a 71 tos = QSLIST_FIRST(&qiv->stack);
ce140b17 72 qobj = tos->obj;
b471d012
EB
73 assert(qobj);
74
ce140b17
EB
75 if (qobject_type(qobj) == QTYPE_QDICT) {
76 assert(name);
e5826a2f
EB
77 ret = qdict_get(qobject_to_qdict(qobj), name);
78 if (tos->h && consume && ret) {
79 bool removed = g_hash_table_remove(tos->h, name);
80 assert(removed);
47c6d3ec 81 }
ce140b17 82 } else {
b471d012 83 assert(qobject_type(qobj) == QTYPE_QLIST);
ce140b17
EB
84 assert(!name);
85 ret = qlist_entry_obj(tos->entry);
fcf3cb21
EB
86 if (consume) {
87 tos->entry = qlist_next(tos->entry);
88 }
c40cc0a0
MR
89 }
90
ce140b17 91 return ret;
c40cc0a0
MR
92}
93
e38ac962
PB
94static void qdict_add_key(const char *key, QObject *obj, void *opaque)
95{
96 GHashTable *h = opaque;
97 g_hash_table_insert(h, (gpointer) key, NULL);
98}
99
d9f62dde 100static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
1158bb2a 101 void *qapi, Error **errp)
c40cc0a0 102{
e38ac962 103 GHashTable *h;
3d344c2a 104 StackObject *tos = g_new0(StackObject, 1);
c40cc0a0 105
b471d012 106 assert(obj);
b471d012 107 tos->obj = obj;
1158bb2a 108 tos->qapi = qapi;
e38ac962
PB
109
110 if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
111 h = g_hash_table_new(g_str_hash, g_str_equal);
112 qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
b471d012 113 tos->h = h;
fcf3cb21
EB
114 } else if (qobject_type(obj) == QTYPE_QLIST) {
115 tos->entry = qlist_first(qobject_to_qlist(obj));
e38ac962
PB
116 }
117
3d344c2a 118 QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
d9f62dde 119 return tos->entry;
c40cc0a0
MR
120}
121
57a33d89 122
15c2f669 123static void qmp_input_check_struct(Visitor *v, Error **errp)
c40cc0a0 124{
15c2f669 125 QmpInputVisitor *qiv = to_qiv(v);
3d344c2a 126 StackObject *tos = QSLIST_FIRST(&qiv->stack);
e38ac962 127
3d344c2a 128 assert(tos && !tos->entry);
57a33d89 129 if (qiv->strict) {
fcf3cb21 130 GHashTable *const top_ht = tos->h;
57a33d89 131 if (top_ht) {
f96493b1
EB
132 GHashTableIter iter;
133 const char *key;
134
135 g_hash_table_iter_init(&iter, top_ht);
136 if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
c6bd8c70 137 error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
57a33d89 138 }
15c2f669
EB
139 }
140 }
141}
142
3d344c2a 143static void qmp_input_stack_object_free(StackObject *tos)
15c2f669 144{
3d344c2a
PB
145 if (tos->h) {
146 g_hash_table_unref(tos->h);
147 }
15c2f669 148
3d344c2a
PB
149 g_free(tos);
150}
15c2f669 151
3d344c2a
PB
152static void qmp_input_pop(Visitor *v, void **obj)
153{
154 QmpInputVisitor *qiv = to_qiv(v);
155 StackObject *tos = QSLIST_FIRST(&qiv->stack);
e38ac962 156
3d344c2a
PB
157 assert(tos && tos->qapi == obj);
158 QSLIST_REMOVE_HEAD(&qiv->stack, node);
159 qmp_input_stack_object_free(tos);
c40cc0a0
MR
160}
161
0b2a0d6b 162static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
337283df 163 size_t size, Error **errp)
c40cc0a0
MR
164{
165 QmpInputVisitor *qiv = to_qiv(v);
e8316d7e 166 QObject *qobj = qmp_input_get_object(qiv, name, true);
8b714d37 167 Error *err = NULL;
c40cc0a0 168
e58d695e
EB
169 if (obj) {
170 *obj = NULL;
171 }
c40cc0a0 172 if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
c6bd8c70
MA
173 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
174 "QDict");
c40cc0a0
MR
175 return;
176 }
177
1158bb2a 178 qmp_input_push(qiv, qobj, obj, &err);
8b714d37
PB
179 if (err) {
180 error_propagate(errp, err);
c40cc0a0
MR
181 return;
182 }
183
184 if (obj) {
7267c094 185 *obj = g_malloc0(size);
c40cc0a0
MR
186 }
187}
188
c40cc0a0 189
d9f62dde
EB
190static void qmp_input_start_list(Visitor *v, const char *name,
191 GenericList **list, size_t size, Error **errp)
c40cc0a0
MR
192{
193 QmpInputVisitor *qiv = to_qiv(v);
e8316d7e 194 QObject *qobj = qmp_input_get_object(qiv, name, true);
d9f62dde 195 const QListEntry *entry;
c40cc0a0
MR
196
197 if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
d9f62dde
EB
198 if (list) {
199 *list = NULL;
200 }
c6bd8c70
MA
201 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
202 "list");
c40cc0a0
MR
203 return;
204 }
205
1158bb2a 206 entry = qmp_input_push(qiv, qobj, list, errp);
d9f62dde
EB
207 if (list) {
208 if (entry) {
209 *list = g_malloc0(size);
210 } else {
211 *list = NULL;
212 }
213 }
c40cc0a0
MR
214}
215
d9f62dde 216static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail,
e65d89bf 217 size_t size)
c40cc0a0
MR
218{
219 QmpInputVisitor *qiv = to_qiv(v);
3d344c2a 220 StackObject *so = QSLIST_FIRST(&qiv->stack);
c40cc0a0 221
fcf3cb21 222 if (!so->entry) {
c40cc0a0
MR
223 return NULL;
224 }
d9f62dde
EB
225 tail->next = g_malloc0(size);
226 return tail->next;
c40cc0a0
MR
227}
228
c40cc0a0 229
dbf11922
EB
230static void qmp_input_start_alternate(Visitor *v, const char *name,
231 GenericAlternate **obj, size_t size,
232 bool promote_int, Error **errp)
69dd62df
KW
233{
234 QmpInputVisitor *qiv = to_qiv(v);
235 QObject *qobj = qmp_input_get_object(qiv, name, false);
236
237 if (!qobj) {
dbf11922 238 *obj = NULL;
c6bd8c70 239 error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
69dd62df
KW
240 return;
241 }
dbf11922
EB
242 *obj = g_malloc0(size);
243 (*obj)->type = qobject_type(qobj);
244 if (promote_int && (*obj)->type == QTYPE_QINT) {
245 (*obj)->type = QTYPE_QFLOAT;
d00341af 246 }
69dd62df
KW
247}
248
0b2a0d6b 249static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj,
4c40314a 250 Error **errp)
c40cc0a0
MR
251{
252 QmpInputVisitor *qiv = to_qiv(v);
fcf73f66 253 QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
c40cc0a0 254
fcf73f66 255 if (!qint) {
c6bd8c70
MA
256 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
257 "integer");
c40cc0a0
MR
258 return;
259 }
260
fcf73f66 261 *obj = qint_get_int(qint);
c40cc0a0
MR
262}
263
0b2a0d6b 264static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
f755dea7
EB
265 Error **errp)
266{
267 /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
268 QmpInputVisitor *qiv = to_qiv(v);
269 QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
270
271 if (!qint) {
272 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
273 "integer");
274 return;
275 }
276
277 *obj = qint_get_int(qint);
278}
279
0b2a0d6b 280static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj,
c40cc0a0
MR
281 Error **errp)
282{
283 QmpInputVisitor *qiv = to_qiv(v);
14b61600 284 QBool *qbool = qobject_to_qbool(qmp_input_get_object(qiv, name, true));
c40cc0a0 285
14b61600 286 if (!qbool) {
c6bd8c70
MA
287 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
288 "boolean");
c40cc0a0
MR
289 return;
290 }
291
14b61600 292 *obj = qbool_get_bool(qbool);
c40cc0a0
MR
293}
294
0b2a0d6b 295static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
c40cc0a0
MR
296 Error **errp)
297{
298 QmpInputVisitor *qiv = to_qiv(v);
7f027843 299 QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
c40cc0a0 300
7f027843 301 if (!qstr) {
e58d695e 302 *obj = NULL;
c6bd8c70
MA
303 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
304 "string");
c40cc0a0
MR
305 return;
306 }
307
7f027843 308 *obj = g_strdup(qstring_get_str(qstr));
c40cc0a0
MR
309}
310
0b2a0d6b 311static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
c40cc0a0
MR
312 Error **errp)
313{
314 QmpInputVisitor *qiv = to_qiv(v);
e8316d7e 315 QObject *qobj = qmp_input_get_object(qiv, name, true);
fcf73f66
MA
316 QInt *qint;
317 QFloat *qfloat;
c40cc0a0 318
fcf73f66
MA
319 qint = qobject_to_qint(qobj);
320 if (qint) {
321 *obj = qint_get_int(qobject_to_qint(qobj));
c40cc0a0
MR
322 return;
323 }
324
fcf73f66
MA
325 qfloat = qobject_to_qfloat(qobj);
326 if (qfloat) {
1ee51876 327 *obj = qfloat_get_double(qobject_to_qfloat(qobj));
fcf73f66 328 return;
1ee51876 329 }
fcf73f66
MA
330
331 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
332 "number");
c40cc0a0
MR
333}
334
0b2a0d6b 335static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
28770e05
MA
336 Error **errp)
337{
338 QmpInputVisitor *qiv = to_qiv(v);
339 QObject *qobj = qmp_input_get_object(qiv, name, true);
340
b34c7bd4
MAL
341 if (!qobj) {
342 error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
343 *obj = NULL;
344 return;
345 }
346
28770e05
MA
347 qobject_incref(qobj);
348 *obj = qobj;
349}
350
3bc97fd5
EB
351static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
352{
3df016f1
EB
353 QmpInputVisitor *qiv = to_qiv(v);
354 QObject *qobj = qmp_input_get_object(qiv, name, true);
355
b34c7bd4
MAL
356 if (!qobj) {
357 error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
358 return;
359 }
360
3df016f1
EB
361 if (qobject_type(qobj) != QTYPE_QNULL) {
362 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
363 "null");
364 }
3bc97fd5
EB
365}
366
0b2a0d6b 367static void qmp_input_optional(Visitor *v, const char *name, bool *present)
c40cc0a0
MR
368{
369 QmpInputVisitor *qiv = to_qiv(v);
e5826a2f 370 QObject *qobj = qmp_input_get_object(qiv, name, false);
c40cc0a0
MR
371
372 if (!qobj) {
373 *present = false;
374 return;
375 }
376
377 *present = true;
378}
379
2c0ef9f4
EB
380static void qmp_input_free(Visitor *v)
381{
382 QmpInputVisitor *qiv = to_qiv(v);
3d344c2a
PB
383 while (!QSLIST_EMPTY(&qiv->stack)) {
384 StackObject *tos = QSLIST_FIRST(&qiv->stack);
385
386 QSLIST_REMOVE_HEAD(&qiv->stack, node);
387 qmp_input_stack_object_free(tos);
388 }
2c0ef9f4 389
b70ce101
EB
390 qobject_decref(qiv->root);
391 g_free(qiv);
2c0ef9f4
EB
392}
393
b70ce101 394Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
c40cc0a0
MR
395{
396 QmpInputVisitor *v;
397
7267c094 398 v = g_malloc0(sizeof(*v));
c40cc0a0 399
983f52d4 400 v->visitor.type = VISITOR_INPUT;
c40cc0a0 401 v->visitor.start_struct = qmp_input_start_struct;
15c2f669
EB
402 v->visitor.check_struct = qmp_input_check_struct;
403 v->visitor.end_struct = qmp_input_pop;
c40cc0a0
MR
404 v->visitor.start_list = qmp_input_start_list;
405 v->visitor.next_list = qmp_input_next_list;
15c2f669 406 v->visitor.end_list = qmp_input_pop;
dbf11922 407 v->visitor.start_alternate = qmp_input_start_alternate;
4c40314a 408 v->visitor.type_int64 = qmp_input_type_int64;
f755dea7 409 v->visitor.type_uint64 = qmp_input_type_uint64;
c40cc0a0
MR
410 v->visitor.type_bool = qmp_input_type_bool;
411 v->visitor.type_str = qmp_input_type_str;
412 v->visitor.type_number = qmp_input_type_number;
28770e05 413 v->visitor.type_any = qmp_input_type_any;
3bc97fd5 414 v->visitor.type_null = qmp_input_type_null;
e2cd0f4f 415 v->visitor.optional = qmp_input_optional;
2c0ef9f4 416 v->visitor.free = qmp_input_free;
fc471c18 417 v->strict = strict;
c40cc0a0 418
ce140b17 419 v->root = obj;
4faaec6a 420 qobject_incref(obj);
c40cc0a0 421
b70ce101 422 return &v->visitor;
c40cc0a0 423}