Commit 563890c7 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

libqtest: escape strings in QMP commands, fix leak



libqtest is using g_strdup_printf to format QMP commands, but
this does not work if the argument strings need to be escaped.
Instead, use the fancy %-formatting functionality of QObject.
The only change required in tests is that strings have to be
formatted as %s, not '%s' or \"%s\".  Luckily this usage of
parameterized QMP commands is not that frequent.

The leak is in socket_sendf.  Since we are extracting the send
loop to a new function, fix it now.

Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 8ffad850
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -291,7 +291,7 @@ static void test_media_insert(void)
    /* Insert media in drive. DSKCHK should not be reset until a step pulse
     * is sent. */
    qmp_discard_response("{'execute':'change', 'arguments':{"
                         " 'device':'floppy0', 'target': '%s' }}",
                         " 'device':'floppy0', 'target': %s }}",
                         test_image);
    qmp_discard_response(""); /* ignore event
                                 (FIXME open -> open transition?!) */
+37 −10
Original line number Diff line number Diff line
@@ -30,8 +30,9 @@

#include "qemu/compiler.h"
#include "qemu/osdep.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/qjson.h"

#define MAX_IRQ 256
#define SOCKET_TIMEOUT 5
@@ -220,19 +221,15 @@ void qtest_quit(QTestState *s)
    g_free(s);
}

static void socket_sendf(int fd, const char *fmt, va_list ap)
static void socket_send(int fd, const char *buf, size_t size)
{
    gchar *str;
    size_t size, offset;

    str = g_strdup_vprintf(fmt, ap);
    size = strlen(str);
    size_t offset;

    offset = 0;
    while (offset < size) {
        ssize_t len;

        len = write(fd, str + offset, size - offset);
        len = write(fd, buf + offset, size - offset);
        if (len == -1 && errno == EINTR) {
            continue;
        }
@@ -244,6 +241,15 @@ static void socket_sendf(int fd, const char *fmt, va_list ap)
    }
}

static void socket_sendf(int fd, const char *fmt, va_list ap)
{
    gchar *str = g_strdup_vprintf(fmt, ap);
    size_t size = strlen(str);

    socket_send(fd, str, size);
    g_free(str);
}

static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
{
    va_list ap;
@@ -378,8 +384,29 @@ QDict *qtest_qmp_receive(QTestState *s)

QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
{
    va_list ap_copy;
    QObject *qobj;

    /* Going through qobject ensures we escape strings properly.
     * This seemingly unnecessary copy is required in case va_list
     * is an array type.
     */
    va_copy(ap_copy, ap);
    qobj = qobject_from_jsonv(fmt, &ap_copy);
    va_end(ap_copy);

    /* No need to send anything for an empty QObject.  */
    if (qobj) {
        QString *qstr = qobject_to_json(qobj);
        const char *str = qstring_get_str(qstr);
        size_t size = qstring_get_length(qstr);

        /* Send QMP request */
    socket_sendf(s->qmp_fd, fmt, ap);
        socket_send(s->qmp_fd, str, size);

        QDECREF(qstr);
        qobject_decref(qobj);
    }

    /* Receive reply */
    return qtest_qmp_receive(s);
+3 −3
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ static void test_properties(const char *path, bool recurse)

    g_test_message("Obtaining properties of %s", path);
    response = qmp("{ 'execute': 'qom-list',"
                   "  'arguments': { 'path': '%s' } }", path);
                   "  'arguments': { 'path': %s } }", path);
    g_assert(response);

    if (!recurse) {
@@ -76,8 +76,8 @@ static void test_properties(const char *path, bool recurse)
            const char *prop = qdict_get_str(tuple, "name");
            g_test_message("Testing property %s.%s", path, prop);
            response = qmp("{ 'execute': 'qom-get',"
                           "  'arguments': { 'path': '%s',"
                           "                 'property': '%s' } }",
                           "  'arguments': { 'path': %s,"
                           "                 'property': %s } }",
                           path, prop);
            /* qom-get may fail but should not, e.g., segfault. */
            g_assert(response);
+2 −2
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ static int qmp_tmp105_get_temperature(const char *id)
    QDict *response;
    int ret;

    response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': '%s', "
    response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, "
                   "'property': 'temperature' } }", id);
    g_assert(qdict_haskey(response, "return"));
    ret = qdict_get_int(response, "return");
@@ -81,7 +81,7 @@ static void qmp_tmp105_set_temperature(const char *id, int value)
{
    QDict *response;

    response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': '%s', "
    response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, "
                   "'property': 'temperature', 'value': %d } }", id, value);
    g_assert(qdict_haskey(response, "return"));
    QDECREF(response);