Skip to content

Commit

Permalink
qmp: Don't let malformed in-band commands jump the queue
Browse files Browse the repository at this point in the history
handle_qmp_command() reports certain errors right away.  This is wrong
when OOB is enabled, because the errors can "jump the queue" then, as
the previous commit demonstrates.

To fix, we need to delay errors until dispatch.  Do that for semantic
errors, mostly by reverting ill-advised parts of commit cf869d5
"qmp: support out-of-band (oob) execution".  Bonus: doesn't run
qmp_dispatch_check_obj() twice, once in handle_qmp_command(), and
again in do_qmp_dispatch().  That's also due to commit cf869d5.

The next commit will fix queue jumping for syntax errors.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-18-armbru@redhat.com>
  • Loading branch information
Markus Armbruster committed Jul 3, 2018
1 parent e8f4a22 commit 69240fe
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 67 deletions.
2 changes: 0 additions & 2 deletions include/qapi/qmp/dispatch.h
Expand Up @@ -48,8 +48,6 @@ bool qmp_command_is_enabled(const QmpCommand *cmd);
const char *qmp_command_name(const QmpCommand *cmd);
bool qmp_has_success_response(const QmpCommand *cmd);
QObject *qmp_build_error_object(Error *err);
QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
Error **errp);
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request,
bool allow_oob);
bool qmp_is_oob(QDict *dict);
Expand Down
79 changes: 18 additions & 61 deletions monitor.c
Expand Up @@ -1290,48 +1290,6 @@ static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list)
}
}

/*
* Return true if check successful, or false otherwise. When false is
* returned, detailed error will be in errp if provided.
*/
static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp)
{
const char *command;
QmpCommand *cmd;

command = qdict_get_try_str(req, "execute");
if (!command) {
command = qdict_get_try_str(req, "exec-oob");
}
if (!command) {
error_setg(errp, "Command field 'execute' missing");
return false;
}

cmd = qmp_find_command(mon->qmp.commands, command);
if (!cmd) {
if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Expecting capabilities negotiation "
"with 'qmp_capabilities'");
} else {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"The command %s has not been found", command);
}
return false;
}

if (qmp_is_oob(req)) {
if (!(cmd->options & QCO_ALLOW_OOB)) {
error_setg(errp, "The command %s does not support OOB",
command);
return false;
}
}

return true;
}

void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
Error **errp)
{
Expand Down Expand Up @@ -4171,6 +4129,7 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id)
{
Monitor *old_mon;
QObject *rsp;
QDict *error;

old_mon = cur_mon;
cur_mon = mon;
Expand All @@ -4179,6 +4138,19 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id)

cur_mon = old_mon;

if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
error = qdict_get_qdict(qobject_to(QDict, rsp), "error");
if (error
&& !g_strcmp0(qdict_get_try_str(error, "class"),
QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
/* Provide a more useful error message */
qdict_del(error, "desc");
qdict_put_str(error, "desc", "Expecting capabilities negotiation"
" with 'qmp_capabilities'");
}
}

/* Respond if necessary */
monitor_qmp_respond(mon, rsp, NULL, qobject_ref(id));
}

Expand Down Expand Up @@ -4256,7 +4228,9 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
error_setg(&err, QERR_JSON_PARSING);
}
if (err) {
goto err;
assert(!req);
monitor_qmp_respond(mon, NULL, err, NULL);
return;
}

qdict = qobject_to(QDict, req);
Expand All @@ -4271,18 +4245,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
qobject_unref(req_json);
}

/* Check against the request in general layout */
qdict = qmp_dispatch_check_obj(req, qmp_oob_enabled(mon), &err);
if (!qdict) {
goto err;
}

/* Check against OOB specific */
if (!qmp_cmd_oob_check(mon, qdict, &err)) {
goto err;
}

if (qmp_is_oob(qdict)) {
if (qdict && qmp_is_oob(qdict)) {
/* Out-of-band (OOB) requests are executed directly in parser. */
trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id)
?: "");
Expand Down Expand Up @@ -4336,12 +4299,6 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)

/* Kick the dispatcher routine */
qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
return;

err:
/* FIXME overtakes queued in-band commands, wrong when !qmp_is_oob() */
monitor_qmp_respond(mon, NULL, err, NULL);
qobject_unref(req);
}

static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
Expand Down
12 changes: 10 additions & 2 deletions qapi/qmp-dispatch.c
Expand Up @@ -20,8 +20,8 @@
#include "qapi/qmp/qbool.h"
#include "sysemu/sysemu.h"

QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
Error **errp)
static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
Error **errp)
{
const char *exec_key = NULL;
const QDictEntry *ent;
Expand Down Expand Up @@ -78,6 +78,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
bool allow_oob, Error **errp)
{
Error *local_err = NULL;
bool oob;
const char *command;
QDict *args, *dict;
QmpCommand *cmd;
Expand All @@ -89,9 +90,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
}

command = qdict_get_try_str(dict, "execute");
oob = false;
if (!command) {
assert(allow_oob);
command = qdict_get_str(dict, "exec-oob");
oob = true;
}
cmd = qmp_find_command(cmds, command);
if (cmd == NULL) {
Expand All @@ -104,6 +107,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
command);
return NULL;
}
if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
error_setg(errp, "The command %s does not support OOB",
command);
return false;
}

if (runstate_check(RUN_STATE_PRECONFIG) &&
!(cmd->options & QCO_ALLOW_PRECONFIG)) {
Expand Down
4 changes: 2 additions & 2 deletions tests/qmp-test.c
Expand Up @@ -242,12 +242,12 @@ static void test_qmp_oob(void)
recv_cmd_id(qts, "ib-blocks-1");
recv_cmd_id(qts, "ib-quick-1");

/* FIXME certain in-band errors overtake slow in-band command */
/* Even malformed in-band command fails in-band */
send_cmd_that_blocks(qts, "blocks-2");
qtest_async_qmp(qts, "{ 'id': 'err-2' }");
recv_cmd_id(qts, NULL);
unblock_blocked_cmd();
recv_cmd_id(qts, "blocks-2");
recv_cmd_id(qts, "err-2");
cleanup_blocking_cmd();

qtest_quit(qts);
Expand Down

0 comments on commit 69240fe

Please sign in to comment.