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 "qemu-common.h" 16 #include "qapi/string-input-visitor.h" 17 #include "qapi/visitor-impl.h" 18 #include "qapi/qmp/qerror.h" 19 #include "qapi/qmp/qnull.h" 20 #include "qemu/option.h" 21 #include "qemu/cutils.h" 22 23 typedef enum ListMode { 24 /* no list parsing active / no list expected */ 25 LM_NONE, 26 /* we have an unparsed string remaining */ 27 LM_UNPARSED, 28 /* we have an unfinished int64 range */ 29 LM_INT64_RANGE, 30 /* we have an unfinished uint64 range */ 31 LM_UINT64_RANGE, 32 /* we have parsed the string completely and no range is remaining */ 33 LM_END, 34 } ListMode; 35 36 /* protect against DOS attacks, limit the amount of elements per range */ 37 #define RANGE_MAX_ELEMENTS 65536 38 39 typedef union RangeElement { 40 int64_t i64; 41 uint64_t u64; 42 } RangeElement; 43 44 struct StringInputVisitor 45 { 46 Visitor visitor; 47 48 /* List parsing state */ 49 ListMode lm; 50 RangeElement rangeNext; 51 RangeElement rangeEnd; 52 const char *unparsed_string; 53 void *list; 54 55 /* The original string to parse */ 56 const char *string; 57 }; 58 59 static StringInputVisitor *to_siv(Visitor *v) 60 { 61 return container_of(v, StringInputVisitor, visitor); 62 } 63 64 static void start_list(Visitor *v, const char *name, GenericList **list, 65 size_t size, Error **errp) 66 { 67 StringInputVisitor *siv = to_siv(v); 68 69 assert(siv->lm == LM_NONE); 70 siv->list = list; 71 siv->unparsed_string = siv->string; 72 73 if (!siv->string[0]) { 74 if (list) { 75 *list = NULL; 76 } 77 siv->lm = LM_END; 78 } else { 79 if (list) { 80 *list = g_malloc0(size); 81 } 82 siv->lm = LM_UNPARSED; 83 } 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 void 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; 116 case LM_END: 117 return; 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 void 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; 195 } 196 *obj = val; 197 return; 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; 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; 216 case LM_END: 217 error_setg(errp, "Fewer list elements expected"); 218 return; 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 void 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; 285 } 286 *obj = val; 287 return; 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; 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; 306 case LM_END: 307 error_setg(errp, "Fewer list elements expected"); 308 return; 309 default: 310 abort(); 311 } 312 } 313 314 static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, 315 Error **errp) 316 { 317 StringInputVisitor *siv = to_siv(v); 318 Error *err = NULL; 319 uint64_t val; 320 321 assert(siv->lm == LM_NONE); 322 parse_option_size(name, siv->string, &val, &err); 323 if (err) { 324 error_propagate(errp, err); 325 return; 326 } 327 328 *obj = val; 329 } 330 331 static void parse_type_bool(Visitor *v, const char *name, bool *obj, 332 Error **errp) 333 { 334 StringInputVisitor *siv = to_siv(v); 335 336 assert(siv->lm == LM_NONE); 337 if (!strcasecmp(siv->string, "on") || 338 !strcasecmp(siv->string, "yes") || 339 !strcasecmp(siv->string, "true")) { 340 *obj = true; 341 return; 342 } 343 if (!strcasecmp(siv->string, "off") || 344 !strcasecmp(siv->string, "no") || 345 !strcasecmp(siv->string, "false")) { 346 *obj = false; 347 return; 348 } 349 350 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 351 "boolean"); 352 } 353 354 static void parse_type_str(Visitor *v, const char *name, char **obj, 355 Error **errp) 356 { 357 StringInputVisitor *siv = to_siv(v); 358 359 assert(siv->lm == LM_NONE); 360 *obj = g_strdup(siv->string); 361 } 362 363 static void 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; 374 } 375 376 *obj = val; 377 } 378 379 static void parse_type_null(Visitor *v, const char *name, QNull **obj, 380 Error **errp) 381 { 382 StringInputVisitor *siv = to_siv(v); 383 384 assert(siv->lm == LM_NONE); 385 *obj = NULL; 386 387 if (siv->string[0]) { 388 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 389 "null"); 390 return; 391 } 392 393 *obj = qnull(); 394 } 395 396 static void string_input_free(Visitor *v) 397 { 398 StringInputVisitor *siv = to_siv(v); 399 400 g_free(siv); 401 } 402 403 Visitor *string_input_visitor_new(const char *str) 404 { 405 StringInputVisitor *v; 406 407 assert(str); 408 v = g_malloc0(sizeof(*v)); 409 410 v->visitor.type = VISITOR_INPUT; 411 v->visitor.type_int64 = parse_type_int64; 412 v->visitor.type_uint64 = parse_type_uint64; 413 v->visitor.type_size = parse_type_size; 414 v->visitor.type_bool = parse_type_bool; 415 v->visitor.type_str = parse_type_str; 416 v->visitor.type_number = parse_type_number; 417 v->visitor.type_null = parse_type_null; 418 v->visitor.start_list = start_list; 419 v->visitor.next_list = next_list; 420 v->visitor.check_list = check_list; 421 v->visitor.end_list = end_list; 422 v->visitor.free = string_input_free; 423 424 v->string = str; 425 v->lm = LM_NONE; 426 return &v->visitor; 427 } 428