From 563890c7c7e977842e2a35afe7a24d06d2103242 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 13 Jun 2014 09:35:18 +0200 Subject: [PATCH] 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: Paolo Bonzini --- tests/fdc-test.c | 2 +- tests/libqtest.c | 47 +++++++++++++++++++++++++++++++++++---------- tests/qom-test.c | 6 +++--- tests/tmp105-test.c | 4 ++-- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/tests/fdc-test.c b/tests/fdc-test.c index 37096dcc13..c8e1e7bd18 100644 --- a/tests/fdc-test.c +++ b/tests/fdc-test.c @@ -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?!) */ diff --git a/tests/libqtest.c b/tests/libqtest.c index 71468ac9c7..98e8f4b648 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -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) { - /* Send QMP request */ - socket_sendf(s->qmp_fd, fmt, 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_send(s->qmp_fd, str, size); + + QDECREF(qstr); + qobject_decref(qobj); + } /* Receive reply */ return qtest_qmp_receive(s); diff --git a/tests/qom-test.c b/tests/qom-test.c index d8d1d8d9ff..4246382d38 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -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); diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c index 15ddaf38d4..99db538191 100644 --- a/tests/tmp105-test.c +++ b/tests/tmp105-test.c @@ -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);