string-input-visitor.c (10740B)
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 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 86static 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 106static 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 123static 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 134static 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 182static 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 224static 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 272static 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 314static 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 329static 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 return qapi_bool_parse(name ? name : "null", siv->string, obj, errp); 336} 337 338static bool parse_type_str(Visitor *v, const char *name, char **obj, 339 Error **errp) 340{ 341 StringInputVisitor *siv = to_siv(v); 342 343 assert(siv->lm == LM_NONE); 344 *obj = g_strdup(siv->string); 345 return true; 346} 347 348static bool parse_type_number(Visitor *v, const char *name, double *obj, 349 Error **errp) 350{ 351 StringInputVisitor *siv = to_siv(v); 352 double val; 353 354 assert(siv->lm == LM_NONE); 355 if (qemu_strtod_finite(siv->string, NULL, &val)) { 356 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 357 "number"); 358 return false; 359 } 360 361 *obj = val; 362 return true; 363} 364 365static bool parse_type_null(Visitor *v, const char *name, QNull **obj, 366 Error **errp) 367{ 368 StringInputVisitor *siv = to_siv(v); 369 370 assert(siv->lm == LM_NONE); 371 *obj = NULL; 372 373 if (siv->string[0]) { 374 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 375 "null"); 376 return false; 377 } 378 379 *obj = qnull(); 380 return true; 381} 382 383static void string_input_free(Visitor *v) 384{ 385 StringInputVisitor *siv = to_siv(v); 386 387 g_free(siv); 388} 389 390Visitor *string_input_visitor_new(const char *str) 391{ 392 StringInputVisitor *v; 393 394 assert(str); 395 v = g_malloc0(sizeof(*v)); 396 397 v->visitor.type = VISITOR_INPUT; 398 v->visitor.type_int64 = parse_type_int64; 399 v->visitor.type_uint64 = parse_type_uint64; 400 v->visitor.type_size = parse_type_size; 401 v->visitor.type_bool = parse_type_bool; 402 v->visitor.type_str = parse_type_str; 403 v->visitor.type_number = parse_type_number; 404 v->visitor.type_null = parse_type_null; 405 v->visitor.start_list = start_list; 406 v->visitor.next_list = next_list; 407 v->visitor.check_list = check_list; 408 v->visitor.end_list = end_list; 409 v->visitor.free = string_input_free; 410 411 v->string = str; 412 v->lm = LM_NONE; 413 return &v->visitor; 414}