xref: /openbmc/qemu/qapi/string-input-visitor.c (revision 64ed6f92)
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 bool 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     return true;
84 }
85 
86 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
87 {
88     StringInputVisitor *siv = to_siv(v);
89 
90     switch (siv->lm) {
91     case LM_END:
92         return NULL;
93     case LM_INT64_RANGE:
94     case LM_UINT64_RANGE:
95     case LM_UNPARSED:
96         /* we have an unparsed string or something left in a range */
97         break;
98     default:
99         abort();
100     }
101 
102     tail->next = g_malloc0(size);
103     return tail->next;
104 }
105 
106 static bool check_list(Visitor *v, Error **errp)
107 {
108     const StringInputVisitor *siv = to_siv(v);
109 
110     switch (siv->lm) {
111     case LM_INT64_RANGE:
112     case LM_UINT64_RANGE:
113     case LM_UNPARSED:
114         error_setg(errp, "Fewer list elements expected");
115         return false;
116     case LM_END:
117         return true;
118     default:
119         abort();
120     }
121 }
122 
123 static void end_list(Visitor *v, void **obj)
124 {
125     StringInputVisitor *siv = to_siv(v);
126 
127     assert(siv->lm != LM_NONE);
128     assert(siv->list == obj);
129     siv->list = NULL;
130     siv->unparsed_string = NULL;
131     siv->lm = LM_NONE;
132 }
133 
134 static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
135 {
136     const char *endptr;
137     int64_t start, end;
138 
139     /* parse a simple int64 or range */
140     if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
141         return -EINVAL;
142     }
143     end = start;
144 
145     switch (endptr[0]) {
146     case '\0':
147         siv->unparsed_string = endptr;
148         break;
149     case ',':
150         siv->unparsed_string = endptr + 1;
151         break;
152     case '-':
153         /* parse the end of the range */
154         if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
155             return -EINVAL;
156         }
157         if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
158             return -EINVAL;
159         }
160         switch (endptr[0]) {
161         case '\0':
162             siv->unparsed_string = endptr;
163             break;
164         case ',':
165             siv->unparsed_string = endptr + 1;
166             break;
167         default:
168             return -EINVAL;
169         }
170         break;
171     default:
172         return -EINVAL;
173     }
174 
175     /* we have a proper range (with maybe only one element) */
176     siv->lm = LM_INT64_RANGE;
177     siv->rangeNext.i64 = start;
178     siv->rangeEnd.i64 = end;
179     return 0;
180 }
181 
182 static bool parse_type_int64(Visitor *v, const char *name, int64_t *obj,
183                              Error **errp)
184 {
185     StringInputVisitor *siv = to_siv(v);
186     int64_t val;
187 
188     switch (siv->lm) {
189     case LM_NONE:
190         /* just parse a simple int64, bail out if not completely consumed */
191         if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
192             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
193                        name ? name : "null", "int64");
194             return false;
195         }
196         *obj = val;
197         return true;
198     case LM_UNPARSED:
199         if (try_parse_int64_list_entry(siv, obj)) {
200             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
201                        "list of int64 values or ranges");
202             return false;
203         }
204         assert(siv->lm == LM_INT64_RANGE);
205         /* fall through */
206     case LM_INT64_RANGE:
207         /* return the next element in the range */
208         assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
209         *obj = siv->rangeNext.i64++;
210 
211         if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
212             /* end of range, check if there is more to parse */
213             siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
214         }
215         return true;
216     case LM_END:
217         error_setg(errp, "Fewer list elements expected");
218         return false;
219     default:
220         abort();
221     }
222 }
223 
224 static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
225 {
226     const char *endptr;
227     uint64_t start, end;
228 
229     /* parse a simple uint64 or range */
230     if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
231         return -EINVAL;
232     }
233     end = start;
234 
235     switch (endptr[0]) {
236     case '\0':
237         siv->unparsed_string = endptr;
238         break;
239     case ',':
240         siv->unparsed_string = endptr + 1;
241         break;
242     case '-':
243         /* parse the end of the range */
244         if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
245             return -EINVAL;
246         }
247         if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
248             return -EINVAL;
249         }
250         switch (endptr[0]) {
251         case '\0':
252             siv->unparsed_string = endptr;
253             break;
254         case ',':
255             siv->unparsed_string = endptr + 1;
256             break;
257         default:
258             return -EINVAL;
259         }
260         break;
261     default:
262         return -EINVAL;
263     }
264 
265     /* we have a proper range (with maybe only one element) */
266     siv->lm = LM_UINT64_RANGE;
267     siv->rangeNext.u64 = start;
268     siv->rangeEnd.u64 = end;
269     return 0;
270 }
271 
272 static bool parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
273                               Error **errp)
274 {
275     StringInputVisitor *siv = to_siv(v);
276     uint64_t val;
277 
278     switch (siv->lm) {
279     case LM_NONE:
280         /* just parse a simple uint64, bail out if not completely consumed */
281         if (qemu_strtou64(siv->string, NULL, 0, &val)) {
282             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
283                        "uint64");
284             return false;
285         }
286         *obj = val;
287         return true;
288     case LM_UNPARSED:
289         if (try_parse_uint64_list_entry(siv, obj)) {
290             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
291                        "list of uint64 values or ranges");
292             return false;
293         }
294         assert(siv->lm == LM_UINT64_RANGE);
295         /* fall through */
296     case LM_UINT64_RANGE:
297         /* return the next element in the range */
298         assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
299         *obj = siv->rangeNext.u64++;
300 
301         if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
302             /* end of range, check if there is more to parse */
303             siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
304         }
305         return true;
306     case LM_END:
307         error_setg(errp, "Fewer list elements expected");
308         return false;
309     default:
310         abort();
311     }
312 }
313 
314 static bool parse_type_size(Visitor *v, const char *name, uint64_t *obj,
315                             Error **errp)
316 {
317     StringInputVisitor *siv = to_siv(v);
318     uint64_t val;
319 
320     assert(siv->lm == LM_NONE);
321     if (!parse_option_size(name, siv->string, &val, errp)) {
322         return false;
323     }
324 
325     *obj = val;
326     return true;
327 }
328 
329 static bool parse_type_bool(Visitor *v, const char *name, bool *obj,
330                             Error **errp)
331 {
332     StringInputVisitor *siv = to_siv(v);
333 
334     assert(siv->lm == LM_NONE);
335     if (!strcasecmp(siv->string, "on") ||
336         !strcasecmp(siv->string, "yes") ||
337         !strcasecmp(siv->string, "true")) {
338         *obj = true;
339         return true;
340     }
341     if (!strcasecmp(siv->string, "off") ||
342         !strcasecmp(siv->string, "no") ||
343         !strcasecmp(siv->string, "false")) {
344         *obj = false;
345         return true;
346     }
347 
348     error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
349                "boolean");
350     return false;
351 }
352 
353 static bool 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     return true;
361 }
362 
363 static bool parse_type_number(Visitor *v, const char *name, double *obj,
364                               Error **errp)
365 {
366     StringInputVisitor *siv = to_siv(v);
367     double val;
368 
369     assert(siv->lm == LM_NONE);
370     if (qemu_strtod_finite(siv->string, NULL, &val)) {
371         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
372                    "number");
373         return false;
374     }
375 
376     *obj = val;
377     return true;
378 }
379 
380 static bool parse_type_null(Visitor *v, const char *name, QNull **obj,
381                             Error **errp)
382 {
383     StringInputVisitor *siv = to_siv(v);
384 
385     assert(siv->lm == LM_NONE);
386     *obj = NULL;
387 
388     if (siv->string[0]) {
389         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
390                    "null");
391         return false;
392     }
393 
394     *obj = qnull();
395     return true;
396 }
397 
398 static void string_input_free(Visitor *v)
399 {
400     StringInputVisitor *siv = to_siv(v);
401 
402     g_free(siv);
403 }
404 
405 Visitor *string_input_visitor_new(const char *str)
406 {
407     StringInputVisitor *v;
408 
409     assert(str);
410     v = g_malloc0(sizeof(*v));
411 
412     v->visitor.type = VISITOR_INPUT;
413     v->visitor.type_int64 = parse_type_int64;
414     v->visitor.type_uint64 = parse_type_uint64;
415     v->visitor.type_size = parse_type_size;
416     v->visitor.type_bool = parse_type_bool;
417     v->visitor.type_str = parse_type_str;
418     v->visitor.type_number = parse_type_number;
419     v->visitor.type_null = parse_type_null;
420     v->visitor.start_list = start_list;
421     v->visitor.next_list = next_list;
422     v->visitor.check_list = check_list;
423     v->visitor.end_list = end_list;
424     v->visitor.free = string_input_free;
425 
426     v->string = str;
427     v->lm = LM_NONE;
428     return &v->visitor;
429 }
430