From nobody Fri Dec 19 17:34:39 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 150181054902659.87374310336418; Thu, 3 Aug 2017 18:35:49 -0700 (PDT) Received: from localhost ([::1]:43481 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ddRWi-0006um-2Y for importer@patchew.org; Thu, 03 Aug 2017 21:35:44 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59790) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ddRNQ-0008IO-Tx for qemu-devel@nongnu.org; Thu, 03 Aug 2017 21:26:10 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ddRNN-0003qs-Fn for qemu-devel@nongnu.org; Thu, 03 Aug 2017 21:26:08 -0400 Received: from mx1.redhat.com ([209.132.183.28]:51566) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ddRNN-0003pf-64 for qemu-devel@nongnu.org; Thu, 03 Aug 2017 21:26:05 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3D8D683F42; Fri, 4 Aug 2017 01:26:04 +0000 (UTC) Received: from red.redhat.com (ovpn-121-23.rdu2.redhat.com [10.10.121.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 980D161F36; Fri, 4 Aug 2017 01:26:03 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 3D8D683F42 Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=eblake@redhat.com From: Eric Blake To: qemu-devel@nongnu.org Date: Thu, 3 Aug 2017 20:25:34 -0500 Message-Id: <20170804012551.2714-6-eblake@redhat.com> In-Reply-To: <20170804012551.2714-1-eblake@redhat.com> References: <20170804012551.2714-1-eblake@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Fri, 04 Aug 2017 01:26:04 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 05/22] qobject: Simplify qobject_from_jsonv() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: armbru@redhat.com, Michael Roth Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" qobject_from_jsonv() was unusual in that it took a va_list*, instead of the more typical va_list; this was so that callers could pass NULL to avoid % interpolation. While this works under the hood, it is awkward for callers, so move the magic into qjson.c rather than in the public interface, and finally improve the documentation of qobject_from_jsonf(). test-qobject-input-visitor.c's visitor_input_test_init_internal() was the only caller passing NULL, fix it to instead use a QObject created by the various callers, who now use the appropriate form of qobject_from_json*() according to whether % interpolation is desired. Once that is done, all remaining callers to qobject_from_jsonv() are passing &error_abort; drop this parameter to match the counterpart qobject_from_jsonf() which assert()s success instead. Besides, all current callers that need interpolation live in the testsuite, where enforcing well-defined input by asserts can help catch typos, and where we should not be operating on dynamic untrusted arbitrary input in the first place. Asserting success has another nice benefit: if we pass more than one %p, but could return failure, we would have to worry about whether all arguments in the va_list had consistent refcount treatment (it should be an all-or-none decision on whether each QObject in the va_list had its reference count altered - but whichever way we prefer, it's a lot of overhead to ensure we do it for everything in the va_list even if we failed halfway through). But now that we promise success, we can now easily promise that all %p arguments will now be cleaned up when freeing the returned object. Signed-off-by: Eric Blake --- include/qapi/qmp/qjson.h | 2 +- tests/libqtest.c | 10 ++------ qobject/qjson.c | 49 ++++++++++++++++++++++++++++++++++= +--- tests/test-qobject-input-visitor.c | 18 ++++++++------ 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h index 6e84082d5f..9aacb1ccf6 100644 --- a/include/qapi/qmp/qjson.h +++ b/include/qapi/qmp/qjson.h @@ -19,7 +19,7 @@ QObject *qobject_from_json(const char *string, Error **errp); QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); -QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp) +QObject *qobject_from_jsonv(const char *string, va_list ap) GCC_FMT_ATTR(1, 0); QString *qobject_to_json(const QObject *obj); diff --git a/tests/libqtest.c b/tests/libqtest.c index 99a07c246f..cde737ec5a 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -448,7 +448,6 @@ QDict *qtest_qmp_receive(QTestState *s) */ void qmp_fd_sendv(int fd, const char *fmt, va_list ap) { - va_list ap_copy; QObject *qobj; int log =3D getenv("QTEST_LOG") !=3D NULL; QString *qstr; @@ -463,13 +462,8 @@ void qmp_fd_sendv(int fd, const char *fmt, va_list ap) } assert(*fmt); - /* 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 =3D qobject_from_jsonv(fmt, &ap_copy, &error_abort); - va_end(ap_copy); + /* Going through qobject ensures we escape strings properly. */ + qobj =3D qobject_from_jsonv(fmt, ap); qstr =3D qobject_to_json(qobj); /* diff --git a/qobject/qjson.c b/qobject/qjson.c index 2e0930884e..210c290aa9 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -35,7 +35,8 @@ static void parse_json(JSONMessageParser *parser, GQueue = *tokens) s->result =3D json_parser_parse_err(tokens, s->ap, &s->err); } -QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp) +static QObject *qobject_from_json_internal(const char *string, va_list *ap, + Error **errp) { JSONParsingState state =3D {}; @@ -50,12 +51,31 @@ QObject *qobject_from_jsonv(const char *string, va_list= *ap, Error **errp) return state.result; } +/* + * Parses JSON input without interpolation. + * + * Returns a QObject matching the input on success, or NULL with + * an error set if the input is not valid JSON. + */ QObject *qobject_from_json(const char *string, Error **errp) { - return qobject_from_jsonv(string, NULL, errp); + return qobject_from_json_internal(string, NULL, errp); } /* + * Parses JSON input with interpolation of % sequences. + * + * The set of sequences recognized is compatible with gcc's -Wformat + * warnings, although not all printf sequences are valid. All use of + * % must occur outside JSON strings. + * + * %i - treat corresponding integer value as JSON bool + * %[l[l]]d, %PRId64 - treat sized integer value as signed JSON number + * %[l[l]]u, %PRIu64 - treat sized integer value as unsigned JSON number + * %f - treat double as JSON number (undefined for inf, NaN) + * %s - convert char * into JSON string (adds escapes, outer quotes) + * %p - embed QObject, transferring the reference to the returned object + * * IMPORTANT: This function aborts on error, thus it must not * be used with untrusted arguments. */ @@ -65,13 +85,36 @@ QObject *qobject_from_jsonf(const char *string, ...) va_list ap; va_start(ap, string); - obj =3D qobject_from_jsonv(string, &ap, &error_abort); + obj =3D qobject_from_json_internal(string, &ap, &error_abort); va_end(ap); assert(obj !=3D NULL); return obj; } +/* + * va_list form of qobject_from_jsonf(). + * + * IMPORTANT: This function aborts on error, thus it must not + * be used with untrusted arguments. + */ +QObject *qobject_from_jsonv(const char *string, va_list ap) +{ + QObject *obj; + va_list ap_copy; + + /* + * This seemingly unnecessary copy is required in case va_list + * is an array type. + */ + va_copy(ap_copy, ap); + obj =3D qobject_from_json_internal(string, &ap_copy, &error_abort); + va_end(ap_copy); + + assert(obj !=3D NULL); + return obj; +} + typedef struct ToJsonIterState { int indent; diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-= visitor.c index bcf02617dc..a9addd9f98 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -45,13 +45,11 @@ static void visitor_input_teardown(TestInputVisitorData= *data, function so that the JSON string used by the tests are kept in the test functions (and not in main()). */ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *dat= a, - bool keyval, - const char *json_string, - va_list *ap) + bool keyval, QObject *obj) { visitor_input_teardown(data, NULL); - data->obj =3D qobject_from_jsonv(json_string, ap, &error_abort); + data->obj =3D obj; g_assert(data->obj); if (keyval) { @@ -69,10 +67,12 @@ Visitor *visitor_input_test_init_full(TestInputVisitorD= ata *data, const char *json_string, ...) { Visitor *v; + QObject *obj; va_list ap; va_start(ap, json_string); - v =3D visitor_input_test_init_internal(data, keyval, json_string, &ap); + obj =3D qobject_from_jsonv(json_string, ap); + v =3D visitor_input_test_init_internal(data, keyval, obj); va_end(ap); return v; } @@ -82,10 +82,12 @@ Visitor *visitor_input_test_init(TestInputVisitorData *= data, const char *json_string, ...) { Visitor *v; + QObject *obj; va_list ap; va_start(ap, json_string); - v =3D visitor_input_test_init_internal(data, false, json_string, &ap); + obj =3D qobject_from_jsonv(json_string, ap); + v =3D visitor_input_test_init_internal(data, false, obj); va_end(ap); return v; } @@ -100,7 +102,9 @@ Visitor *visitor_input_test_init(TestInputVisitorData *= data, static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data, const char *json_string) { - return visitor_input_test_init_internal(data, false, json_string, NULL= ); + QObject *obj =3D qobject_from_json(json_string, &error_abort); + + return visitor_input_test_init_internal(data, false, obj); } static void test_visitor_in_int(TestInputVisitorData *data, --=20 2.13.3