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 if (!parse_option_size(name, siv->string, &val, &err)) { 322 error_propagate(errp, err); 323 return; 324 } 325 326 *obj = val; 327 } 328 329 static void 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; 340 } 341 if (!strcasecmp(siv->string, "off") || 342 !strcasecmp(siv->string, "no") || 343 !strcasecmp(siv->string, "false")) { 344 *obj = false; 345 return; 346 } 347 348 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 349 "boolean"); 350 } 351 352 static void parse_type_str(Visitor *v, const char *name, char **obj, 353 Error **errp) 354 { 355 StringInputVisitor *siv = to_siv(v); 356 357 assert(siv->lm == LM_NONE); 358 *obj = g_strdup(siv->string); 359 } 360 361 static void parse_type_number(Visitor *v, const char *name, double *obj, 362 Error **errp) 363 { 364 StringInputVisitor *siv = to_siv(v); 365 double val; 366 367 assert(siv->lm == LM_NONE); 368 if (qemu_strtod_finite(siv->string, NULL, &val)) { 369 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 370 "number"); 371 return; 372 } 373 374 *obj = val; 375 } 376 377 static void parse_type_null(Visitor *v, const char *name, QNull **obj, 378 Error **errp) 379 { 380 StringInputVisitor *siv = to_siv(v); 381 382 assert(siv->lm == LM_NONE); 383 *obj = NULL; 384 385 if (siv->string[0]) { 386 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 387 "null"); 388 return; 389 } 390 391 *obj = qnull(); 392 } 393 394 static void string_input_free(Visitor *v) 395 { 396 StringInputVisitor *siv = to_siv(v); 397 398 g_free(siv); 399 } 400 401 Visitor *string_input_visitor_new(const char *str) 402 { 403 StringInputVisitor *v; 404 405 assert(str); 406 v = g_malloc0(sizeof(*v)); 407 408 v->visitor.type = VISITOR_INPUT; 409 v->visitor.type_int64 = parse_type_int64; 410 v->visitor.type_uint64 = parse_type_uint64; 411 v->visitor.type_size = parse_type_size; 412 v->visitor.type_bool = parse_type_bool; 413 v->visitor.type_str = parse_type_str; 414 v->visitor.type_number = parse_type_number; 415 v->visitor.type_null = parse_type_null; 416 v->visitor.start_list = start_list; 417 v->visitor.next_list = next_list; 418 v->visitor.check_list = check_list; 419 v->visitor.end_list = end_list; 420 v->visitor.free = string_input_free; 421 422 v->string = str; 423 v->lm = LM_NONE; 424 return &v->visitor; 425 } 426