Commit c76d1a9b authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'qmp/queue/qmp' into staging

* qmp/queue/qmp:
  qmp: document strict parsing
  qmp: parse commands in strict mode
  qmp: add and use q type specifier
  qapi: add strict mode to input visitor
  qapi: place outermost object on qiv stack
  qapi: untangle next_list
  qapi: allow freeing partially-allocated objects
  qapi: shortcut visits on errors
  qapi: fix memory leak on error
  qapi: fail hard on stack imbalance
  qapi: add a test case for type errors
  qapi: add struct-errors test case to test-qmp-output-visitor
  qapi: fix double free in qmp_output_visitor_cleanup()
parents 0a5a4e05 1829851c
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -209,13 +209,27 @@ incompatible way are disabled by default and will be advertised by the
capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
that array and enable the capabilities they support.

Additionally, Clients must not assume any particular:

- Size of json-objects or length of json-arrays
The QMP Server performs a type check on the arguments to a command.  It
generates an error if a value does not have the expected type for its
key, or if it does not understand a key that the Client included.  The
strictness of the Server catches wrong assumptions of Clients about
the Server's schema.  Clients can assume that, when such validation
errors occur, they will be reported before the command generated any
side effect.

However, Clients must not assume any particular:

- Length of json-arrays
- Size of json-objects; in particular, future versions of QEMU may add
  new keys and Clients should be able to ignore them.
- Order of json-object members or json-array elements
- Amount of errors generated by a command, that is, new errors can be added
  to any existing command in newer versions of the Server

Of course, the Server does guarantee to send valid JSON.  But apart from
this, a Client should be "conservative in what they send, and liberal in
what they accept".

6. Downstream extension of QMP
------------------------------

+2 −2
Original line number Diff line number Diff line
@@ -194,11 +194,11 @@ Example:

    void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp)
    {
        GenericList *i;
        GenericList *i, **prev = (GenericList **)obj;

        visit_start_list(m, name, errp);

        for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
        for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) {
            UserDefOneList *native_i = (UserDefOneList *)i;
            visit_type_UserDefOne(m, &native_i->value, NULL, errp);
        }
+3 −0
Original line number Diff line number Diff line
@@ -4157,6 +4157,9 @@ static int check_client_args_type(const QDict *client_args,
        case 'O':
            assert(flags & QMP_ACCEPT_UNKNOWNS);
            break;
        case 'q':
            /* Any QObject can be passed.  */
            break;
        case '/':
        case '.':
            /*
+1 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@

# for testing nested structs
{ 'type': 'UserDefOne',
  'data': { 'integer': 'int', 'string': 'str' } }
  'data': { 'integer': 'int', 'string': 'str', '*enum1': 'EnumOne' } }

{ 'type': 'UserDefTwo',
  'data': { 'string': 'str',
+79 −41
Original line number Diff line number Diff line
@@ -22,16 +22,17 @@

typedef struct StackObject
{
    const QObject *obj;
    QObject *obj;
    const QListEntry *entry;
    GHashTable *h;
} StackObject;

struct QmpInputVisitor
{
    Visitor visitor;
    QObject *obj;
    StackObject stack[QIV_STACK_SIZE];
    int nb_stack;
    bool strict;
};

static QmpInputVisitor *to_qiv(Visitor *v)
@@ -39,21 +40,18 @@ static QmpInputVisitor *to_qiv(Visitor *v)
    return container_of(v, QmpInputVisitor, visitor);
}

static const QObject *qmp_input_get_object(QmpInputVisitor *qiv,
static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
                                     const char *name)
{
    const QObject *qobj;

    if (qiv->nb_stack == 0) {
        qobj = qiv->obj;
    } else {
        qobj = qiv->stack[qiv->nb_stack - 1].obj;
    }
    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;

    if (qobj) {
        if (name && qobject_type(qobj) == QTYPE_QDICT) {
            if (qiv->stack[qiv->nb_stack - 1].h) {
                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
            }
            return qdict_get(qobject_to_qdict(qobj), name);
        } else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) {
        } else if (qiv->stack[qiv->nb_stack - 1].entry) {
            return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
        }
    }
@@ -61,34 +59,57 @@ static const QObject *qmp_input_get_object(QmpInputVisitor *qiv,
    return qobj;
}

static void qmp_input_push(QmpInputVisitor *qiv, const QObject *obj, Error **errp)
static void qdict_add_key(const char *key, QObject *obj, void *opaque)
{
    qiv->stack[qiv->nb_stack].obj = obj;
    if (qobject_type(obj) == QTYPE_QLIST) {
        qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj));
    GHashTable *h = opaque;
    g_hash_table_insert(h, (gpointer) key, NULL);
}
    qiv->nb_stack++;

static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
{
    GHashTable *h;

    if (qiv->nb_stack >= QIV_STACK_SIZE) {
        error_set(errp, QERR_BUFFER_OVERRUN);
        return;
    }

    qiv->stack[qiv->nb_stack].obj = obj;
    qiv->stack[qiv->nb_stack].entry = NULL;
    qiv->stack[qiv->nb_stack].h = NULL;

    if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
        h = g_hash_table_new(g_str_hash, g_str_equal);
        qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
        qiv->stack[qiv->nb_stack].h = h;
    }

    qiv->nb_stack++;
}

static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
{
    qiv->nb_stack--;
    if (qiv->nb_stack < 0) {
        error_set(errp, QERR_BUFFER_OVERRUN);
        return;
    GHashTableIter iter;
    gpointer key;

    if (qiv->strict && qiv->stack[qiv->nb_stack - 1].h) {
        g_hash_table_iter_init(&iter, qiv->stack[qiv->nb_stack - 1].h);
        if (g_hash_table_iter_next(&iter, &key, NULL)) {
            error_set(errp, QERR_QMP_EXTRA_MEMBER, (char *) key);
        }
        g_hash_table_unref(qiv->stack[qiv->nb_stack - 1].h);
    }

    assert(qiv->nb_stack > 0);
    qiv->nb_stack--;
}

static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
                                   const char *name, size_t size, Error **errp)
{
    QmpInputVisitor *qiv = to_qiv(v);
    const QObject *qobj = qmp_input_get_object(qiv, name);
    QObject *qobj = qmp_input_get_object(qiv, name);
    Error *err = NULL;

    if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -96,8 +117,9 @@ static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
        return;
    }

    qmp_input_push(qiv, qobj, errp);
    if (error_is_set(errp)) {
    qmp_input_push(qiv, qobj, &err);
    if (err) {
        error_propagate(errp, err);
        return;
    }

@@ -116,7 +138,7 @@ static void qmp_input_end_struct(Visitor *v, Error **errp)
static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
{
    QmpInputVisitor *qiv = to_qiv(v);
    const QObject *qobj = qmp_input_get_object(qiv, name);
    QObject *qobj = qmp_input_get_object(qiv, name);

    if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -133,18 +155,24 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
    QmpInputVisitor *qiv = to_qiv(v);
    GenericList *entry;
    StackObject *so = &qiv->stack[qiv->nb_stack - 1];
    bool first;

    if (so->entry == NULL) {
        return NULL;
        so->entry = qlist_first(qobject_to_qlist(so->obj));
        first = true;
    } else {
        so->entry = qlist_next(so->entry);
        first = false;
    }

    entry = g_malloc0(sizeof(*entry));
    if (*list) {
        so->entry = qlist_next(so->entry);
    if (so->entry == NULL) {
            g_free(entry);
        return NULL;
    }

    entry = g_malloc0(sizeof(*entry));
    if (first) {
        *list = entry;
    } else {
        (*list)->next = entry;
    }

@@ -162,7 +190,7 @@ static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
                               Error **errp)
{
    QmpInputVisitor *qiv = to_qiv(v);
    const QObject *qobj = qmp_input_get_object(qiv, name);
    QObject *qobj = qmp_input_get_object(qiv, name);

    if (!qobj || qobject_type(qobj) != QTYPE_QINT) {
        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -177,7 +205,7 @@ static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
                                Error **errp)
{
    QmpInputVisitor *qiv = to_qiv(v);
    const QObject *qobj = qmp_input_get_object(qiv, name);
    QObject *qobj = qmp_input_get_object(qiv, name);

    if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) {
        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -192,7 +220,7 @@ static void qmp_input_type_str(Visitor *v, char **obj, const char *name,
                               Error **errp)
{
    QmpInputVisitor *qiv = to_qiv(v);
    const QObject *qobj = qmp_input_get_object(qiv, name);
    QObject *qobj = qmp_input_get_object(qiv, name);

    if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) {
        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -207,7 +235,7 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
                                  Error **errp)
{
    QmpInputVisitor *qiv = to_qiv(v);
    const QObject *qobj = qmp_input_get_object(qiv, name);
    QObject *qobj = qmp_input_get_object(qiv, name);

    if (!qobj || qobject_type(qobj) != QTYPE_QFLOAT) {
        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -222,7 +250,7 @@ static void qmp_input_start_optional(Visitor *v, bool *present,
                                     const char *name, Error **errp)
{
    QmpInputVisitor *qiv = to_qiv(v);
    const QObject *qobj = qmp_input_get_object(qiv, name);
    QObject *qobj = qmp_input_get_object(qiv, name);

    if (!qobj) {
        *present = false;
@@ -239,7 +267,7 @@ Visitor *qmp_input_get_visitor(QmpInputVisitor *v)

void qmp_input_visitor_cleanup(QmpInputVisitor *v)
{
    qobject_decref(v->obj);
    qobject_decref(v->stack[0].obj);
    g_free(v);
}

@@ -261,8 +289,18 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
    v->visitor.type_number = qmp_input_type_number;
    v->visitor.start_optional = qmp_input_start_optional;

    v->obj = obj;
    qobject_incref(v->obj);
    qmp_input_push(v, obj, NULL);
    qobject_incref(obj);

    return v;
}

QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj)
{
    QmpInputVisitor *v;

    v = qmp_input_visitor_new(obj);
    v->strict = true;

    return v;
}
Loading