xref: /qemu/qapi/string-input-visitor.c (revision ab9056ff)
1 /*
2  * String parsing visitor
3  *
4  * Copyright Red Hat, Inc. 2012-2016
5  *
6  * Author: Paolo Bonzini <pbonzini@redhat.com>
7  *         David Hildenbrand <david@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qapi/error.h"
15 #include "qapi/string-input-visitor.h"
16 #include "qapi/visitor-impl.h"
17 #include "qapi/qmp/qerror.h"
18 #include "qapi/qmp/qnull.h"
19 #include "qemu/option.h"
20 #include "qemu/cutils.h"
21 
22 typedef enum ListMode {
23     /* no list parsing active / no list expected */
24     LM_NONE,
25     /* we have an unparsed string remaining */
26     LM_UNPARSED,
27     /* we have an unfinished int64 range */
28     LM_INT64_RANGE,
29     /* we have an unfinished uint64 range */
30     LM_UINT64_RANGE,
31     /* we have parsed the string completely and no range is remaining */
32     LM_END,
33 } ListMode;
34 
35 /* protect against DOS attacks, limit the amount of elements per range */
36 #define RANGE_MAX_ELEMENTS 65536
37 
38 typedef union RangeElement {
39     int64_t i64;
40     uint64_t u64;
41 } RangeElement;
42 
43 struct StringInputVisitor
44 {
45     Visitor visitor;
46 
47     /* List parsing state */
48     ListMode lm;
49     RangeElement rangeNext;
50     RangeElement rangeEnd;
51     const char *unparsed_string;
52     void *list;
53 
54     /* The original string to parse */
55     const char *string;
56 };
57 
58 static StringInputVisitor *to_siv(Visitor *v)
59 {
60     return container_of(v, StringInputVisitor, visitor);
61 }
62 
63 static void start_list(Visitor *v, const char *name, GenericList **list,
64                        size_t size, Error **errp)
65 {
66     StringInputVisitor *siv = to_siv(v);
67 
68     assert(siv->lm == LM_NONE);
69     siv->list = list;
70     siv->unparsed_string = siv->string;
71 
72     if (!siv->string[0]) {
73         if (list) {
74             *list = NULL;
75         }
76         siv->lm = LM_END;
77     } else {
78         if (list) {
79             *list = g_malloc0(size);
80         }
81         siv->lm = LM_UNPARSED;
82     }
83 }
84 
85 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
86 {
87     StringInputVisitor *siv = to_siv(v);
88 
89     switch (siv->lm) {
90     case LM_END:
91         return NULL;
92     case LM_INT64_RANGE:
93     case LM_UINT64_RANGE:
94     case LM_UNPARSED:
95         /* we have an unparsed string or something left in a range */
96         break;
97     default:
98         abort();
99     }
100 
101     tail->next = g_malloc0(size);
102     return tail->next;
103 }
104 
105 static void check_list(Visitor *v, Error **errp)
106 {
107     const StringInputVisitor *siv = to_siv(v);
108 
109     switch (siv->lm) {
110     case LM_INT64_RANGE:
111     case LM_UINT64_RANGE:
112     case LM_UNPARSED:
113         error_setg(errp, "Fewer list elements expected");
114         return;
115     case LM_END:
116         return;
117     default:
118         abort();
119     }
120 }
121 
122 static void end_list(Visitor *v, void **obj)
123 {
124     StringInputVisitor *siv = to_siv(v);
125 
126     assert(siv->lm != LM_NONE);
127     assert(siv->list == obj);
128     siv->list = NULL;
129     siv->unparsed_string = NULL;
130     siv->lm = LM_NONE;
131 }
132 
133 static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
134 {
135     const char *endptr;
136     int64_t start, end;
137 
138     /* parse a simple int64 or range */
139     if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
140         return -EINVAL;
141     }
142     end = start;
143 
144     switch (endptr[0]) {
145     case '\0':
146         siv->unparsed_string = endptr;
147         break;
148     case ',':
149         siv->unparsed_string = endptr + 1;
150         break;
151     case '-':
152         /* parse the end of the range */
153         if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
154             return -EINVAL;
155         }
156         if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
157             return -EINVAL;
158         }
159         switch (endptr[0]) {
160         case '\0':
161             siv->unparsed_string = endptr;
162             break;
163         case ',':
164             siv->unparsed_string = endptr + 1;
165             break;
166         default:
167             return -EINVAL;
168         }
169         break;
170     default:
171         return -EINVAL;
172     }
173 
174     /* we have a proper range (with maybe only one element) */
175     siv->lm = LM_INT64_RANGE;
176     siv->rangeNext.i64 = start;
177     siv->rangeEnd.i64 = end;
178     return 0;
179 }
180 
181 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
182                              Error **errp)
183 {
184     StringInputVisitor *siv = to_siv(v);
185     int64_t val;
186 
187     switch (siv->lm) {
188     case LM_NONE:
189         /* just parse a simple int64, bail out if not completely consumed */
190         if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
191                 error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
192                            name ? name : "null", "int64");
193             return;
194         }
195         *obj = val;
196         return;
197     case LM_UNPARSED:
198         if (try_parse_int64_list_entry(siv, obj)) {
199             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
200                        "list of int64 values or ranges");
201             return;
202         }
203         assert(siv->lm == LM_INT64_RANGE);
204         /* fall through */
205     case LM_INT64_RANGE:
206         /* return the next element in the range */
207         assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
208         *obj = siv->rangeNext.i64++;
209 
210         if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
211             /* end of range, check if there is more to parse */
212             siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
213         }
214         return;
215     case LM_END:
216         error_setg(errp, "Fewer list elements expected");
217         return;
218     default:
219         abort();
220     }
221 }
222 
223 static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
224 {
225     const char *endptr;
226     uint64_t start, end;
227 
228     /* parse a simple uint64 or range */
229     if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
230         return -EINVAL;
231     }
232     end = start;
233 
234     switch (endptr[0]) {
235     case '\0':
236         siv->unparsed_string = endptr;
237         break;
238     case ',':
239         siv->unparsed_string = endptr + 1;
240         break;
241     case '-':
242         /* parse the end of the range */
243         if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
244             return -EINVAL;
245         }
246         if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
247             return -EINVAL;
248         }
249         switch (endptr[0]) {
250         case '\0':
251             siv->unparsed_string = endptr;
252             break;
253         case ',':
254             siv->unparsed_string = endptr + 1;
255             break;
256         default:
257             return -EINVAL;
258         }
259         break;
260     default:
261         return -EINVAL;
262     }
263 
264     /* we have a proper range (with maybe only one element) */
265     siv->lm = LM_UINT64_RANGE;
266     siv->rangeNext.u64 = start;
267     siv->rangeEnd.u64 = end;
268     return 0;
269 }
270 
271 static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
272                               Error **errp)
273 {
274     StringInputVisitor *siv = to_siv(v);
275     uint64_t val;
276 
277     switch (siv->lm) {
278     case LM_NONE:
279         /* just parse a simple uint64, bail out if not completely consumed */
280         if (qemu_strtou64(siv->string, NULL, 0, &val)) {
281             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
282                        "uint64");
283             return;
284         }
285         *obj = val;
286         return;
287     case LM_UNPARSED:
288         if (try_parse_uint64_list_entry(siv, obj)) {
289             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
290                        "list of uint64 values or ranges");
291             return;
292         }
293         assert(siv->lm == LM_UINT64_RANGE);
294         /* fall through */
295     case LM_UINT64_RANGE:
296         /* return the next element in the range */
297         assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
298         *obj = siv->rangeNext.u64++;
299 
300         if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
301             /* end of range, check if there is more to parse */
302             siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
303         }
304         return;
305     case LM_END:
306         error_setg(errp, "Fewer list elements expected");
307         return;
308     default:
309         abort();
310     }
311 }
312 
313 static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
314                             Error **errp)
315 {
316     StringInputVisitor *siv = to_siv(v);
317     Error *err = NULL;
318     uint64_t val;
319 
320     assert(siv->lm == LM_NONE);
321     parse_option_size(name, siv->string, &val, &err);
322     if (err) {
323         error_propagate(errp, err);
324         return;
325     }
326 
327     *obj = val;
328 }
329 
330 static void parse_type_bool(Visitor *v, const char *name, bool *obj,
331                             Error **errp)
332 {
333     StringInputVisitor *siv = to_siv(v);
334 
335     assert(siv->lm == LM_NONE);
336     if (!strcasecmp(siv->string, "on") ||
337         !strcasecmp(siv->string, "yes") ||
338         !strcasecmp(siv->string, "true")) {
339         *obj = true;
340         return;
341     }
342     if (!strcasecmp(siv->string, "off") ||
343         !strcasecmp(siv->string, "no") ||
344         !strcasecmp(siv->string, "false")) {
345         *obj = false;
346         return;
347     }
348 
349     error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
350                "boolean");
351 }
352 
353 static void parse_type_str(Visitor *v, const char *name, char **obj,
354                            Error **errp)
355 {
356     StringInputVisitor *siv = to_siv(v);
357 
358     assert(siv->lm == LM_NONE);
359     *obj = g_strdup(siv->string);
360 }
361 
362 static void parse_type_number(Visitor *v, const char *name, double *obj,
363                               Error **errp)
364 {
365     StringInputVisitor *siv = to_siv(v);
366     double val;
367 
368     assert(siv->lm == LM_NONE);
369     if (qemu_strtod_finite(siv->string, NULL, &val)) {
370         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
371                    "number");
372         return;
373     }
374 
375     *obj = val;
376 }
377 
378 static void parse_type_null(Visitor *v, const char *name, QNull **obj,
379                             Error **errp)
380 {
381     StringInputVisitor *siv = to_siv(v);
382 
383     assert(siv->lm == LM_NONE);
384     *obj = NULL;
385 
386     if (siv->string[0]) {
387         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
388                    "null");
389         return;
390     }
391 
392     *obj = qnull();
393 }
394 
395 static void string_input_free(Visitor *v)
396 {
397     StringInputVisitor *siv = to_siv(v);
398 
399     g_free(siv);
400 }
401 
402 Visitor *string_input_visitor_new(const char *str)
403 {
404     StringInputVisitor *v;
405 
406     assert(str);
407     v = g_malloc0(sizeof(*v));
408 
409     v->visitor.type = VISITOR_INPUT;
410     v->visitor.type_int64 = parse_type_int64;
411     v->visitor.type_uint64 = parse_type_uint64;
412     v->visitor.type_size = parse_type_size;
413     v->visitor.type_bool = parse_type_bool;
414     v->visitor.type_str = parse_type_str;
415     v->visitor.type_number = parse_type_number;
416     v->visitor.type_null = parse_type_null;
417     v->visitor.start_list = start_list;
418     v->visitor.next_list = next_list;
419     v->visitor.check_list = check_list;
420     v->visitor.end_list = end_list;
421     v->visitor.free = string_input_free;
422 
423     v->string = str;
424     v->lm = LM_NONE;
425     return &v->visitor;
426 }
427