qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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 
     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 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 
     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 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 
    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 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 
    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 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 
    314 static 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 
    329 static 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 
    338 static 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 
    348 static 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 
    365 static 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 
    383 static void string_input_free(Visitor *v)
    384 {
    385     StringInputVisitor *siv = to_siv(v);
    386 
    387     g_free(siv);
    388 }
    389 
    390 Visitor *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 }