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