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
22typedef 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
38typedef union RangeElement {
39 int64_t i64;
40 uint64_t u64;
41} RangeElement;
42
43struct 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
58static StringInputVisitor *to_siv(Visitor *v)
59{
60 return container_of(v, StringInputVisitor, visitor);
61}
62
63static 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
85static 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
105static 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
122static 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
133static 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
181static 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
223static 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
271static 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
313static 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
330static 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
353static 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
362static 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
378static 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
395static void string_input_free(Visitor *v)
396{
397 StringInputVisitor *siv = to_siv(v);
398
399 g_free(siv);
400}
401
402Visitor *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