2009-11-11 17:39:47 +01:00
|
|
|
/*
|
|
|
|
* Copyright IBM, Corp. 2009
|
json-parser: Accept 'null' in QMP
We document that in QMP, the client may send any json-value
for the optional "id" key, and then return that same value
on reply (both success and failures, insofar as the failure
happened after parsing the id). [Note that the output may
not be identical to the input, as whitespace may change and
since we may reorder keys within a json-object, but that this
still constitutes the same json-value]. However, we were not
handling the JSON literal null, which counts as a json-value
per RFC 7159.
Also, down the road, given the QAPI schema of {'*foo':'str'} or
{'*foo':'ComplexType'}, we could decide to allow the QMP client
to pass { "foo":null } instead of the current representation of
{ } where omitting the key is the only way to get at the default
NULL value. Such a change might be useful for argument
introspection (if a type in older qemu lacks 'foo' altogether,
then an explicit "foo":null probe will force an easily
distinguished error message for whether the optional "foo" key
is even understood in newer qemu). And if we add default values
to optional arguments, allowing an explicit null would be
required for getting a NULL value associated with an optional
string that has a non-null default. But all that can come at a
later day.
The 'check-unit' testsuite is enhanced to test that parsing
produces the same object as explicitly requesting a reference
to the special qnull object. In addition, I tested with:
$ ./x86_64-softmmu/qemu-system-x86_64 -qmp stdio -nodefaults
{"QMP": {"version": {"qemu": {"micro": 91, "minor": 2, "major": 2}, "package": ""}, "capabilities": []}}
{"execute":"qmp_capabilities","id":null}
{"return": {}, "id": null}
{"id":{"a":null,"b":[1,null]},"execute":"quit"}
{"return": {}, "id": {"a": null, "b": [1, null]}}
{"timestamp": {"seconds": 1427742379, "microseconds": 423128}, "event": "SHUTDOWN"}
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-04-29 23:35:06 +02:00
|
|
|
* Copyright (c) 2013, 2015 Red Hat Inc.
|
2009-11-11 17:39:47 +01:00
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
2013-04-11 18:07:19 +02:00
|
|
|
* Markus Armbruster <armbru@redhat.com>
|
2009-11-11 17:39:47 +01:00
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
|
|
|
* See the COPYING.LIB file in the top-level directory.
|
|
|
|
*
|
|
|
|
*/
|
2017-02-28 22:27:00 +01:00
|
|
|
|
2016-02-08 19:08:51 +01:00
|
|
|
#include "qemu/osdep.h"
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
#include "qapi/error.h"
|
2018-02-01 12:18:35 +01:00
|
|
|
#include "qapi/qmp/qbool.h"
|
2012-12-17 18:19:43 +01:00
|
|
|
#include "qapi/qmp/qjson.h"
|
2017-08-25 12:59:01 +02:00
|
|
|
#include "qapi/qmp/qlit.h"
|
2018-02-01 12:18:36 +01:00
|
|
|
#include "qapi/qmp/qnull.h"
|
|
|
|
#include "qapi/qmp/qnum.h"
|
2018-02-01 12:18:40 +01:00
|
|
|
#include "qapi/qmp/qstring.h"
|
2018-08-23 18:39:41 +02:00
|
|
|
#include "qemu/unicode.h"
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2018-08-23 18:39:35 +02:00
|
|
|
static QString *from_json_str(const char *jstr, bool single, Error **errp)
|
|
|
|
{
|
|
|
|
char quote = single ? '\'' : '"';
|
|
|
|
char *qjstr = g_strdup_printf("%c%s%c", quote, jstr, quote);
|
|
|
|
QString *ret = qobject_to(QString, qobject_from_json(qjstr, errp));
|
|
|
|
|
|
|
|
g_free(qjstr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *to_json_str(QString *str)
|
|
|
|
{
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *json = qobject_to_json(QOBJECT(str));
|
2018-08-23 18:39:35 +02:00
|
|
|
|
|
|
|
if (!json) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* peel off double quotes */
|
2020-12-11 18:11:37 +01:00
|
|
|
g_string_truncate(json, json->len - 1);
|
|
|
|
g_string_erase(json, 0, 1);
|
|
|
|
return g_string_free(json, false);
|
2018-08-23 18:39:35 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void escaped_string(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
struct {
|
2018-08-23 18:39:35 +02:00
|
|
|
/* Content of JSON string to parse with qobject_from_json() */
|
|
|
|
const char *json_in;
|
|
|
|
/* Expected parse output; to unparse with qobject_to_json() */
|
|
|
|
const char *utf8_out;
|
2009-11-11 20:16:03 +01:00
|
|
|
int skip;
|
2009-11-11 17:39:47 +01:00
|
|
|
} test_cases[] = {
|
2018-08-23 18:39:36 +02:00
|
|
|
{ "\\b\\f\\n\\r\\t\\\\\\\"", "\b\f\n\r\t\\\"" },
|
2018-08-23 18:39:37 +02:00
|
|
|
{ "\\/\\'", "/'", .skip = 1 },
|
2018-08-23 18:39:35 +02:00
|
|
|
{ "single byte utf-8 \\u0020", "single byte utf-8 ", .skip = 1 },
|
|
|
|
{ "double byte utf-8 \\u00A2", "double byte utf-8 \xc2\xa2" },
|
|
|
|
{ "triple byte utf-8 \\u20AC", "triple byte utf-8 \xe2\x82\xac" },
|
2018-08-23 18:39:37 +02:00
|
|
|
{ "quadruple byte utf-8 \\uD834\\uDD1E", /* U+1D11E */
|
2018-08-23 18:39:56 +02:00
|
|
|
"quadruple byte utf-8 \xF0\x9D\x84\x9E" },
|
2018-08-23 18:39:37 +02:00
|
|
|
{ "\\", NULL },
|
|
|
|
{ "\\z", NULL },
|
|
|
|
{ "\\ux", NULL },
|
|
|
|
{ "\\u1x", NULL },
|
|
|
|
{ "\\u12x", NULL },
|
|
|
|
{ "\\u123x", NULL },
|
|
|
|
{ "\\u12345", "\341\210\2645" },
|
2018-08-23 18:39:55 +02:00
|
|
|
{ "\\u0000x", "\xC0\x80x" },
|
|
|
|
{ "unpaired leading surrogate \\uD800", NULL },
|
|
|
|
{ "unpaired leading surrogate \\uD800\\uCAFE", NULL },
|
|
|
|
{ "unpaired leading surrogate \\uD800\\uD801\\uDC02", NULL },
|
|
|
|
{ "unpaired trailing surrogate \\uDC00", NULL },
|
|
|
|
{ "backward surrogate pair \\uDC00\\uD800", NULL },
|
|
|
|
{ "noncharacter U+FDD0 \\uFDD0", NULL },
|
|
|
|
{ "noncharacter U+FDEF \\uFDEF", NULL },
|
|
|
|
{ "noncharacter U+1FFFE \\uD87F\\uDFFE", NULL },
|
|
|
|
{ "noncharacter U+10FFFF \\uDC3F\\uDFFF", NULL },
|
2009-11-11 17:39:47 +01:00
|
|
|
{}
|
|
|
|
};
|
2018-08-23 18:39:35 +02:00
|
|
|
int i, j;
|
|
|
|
QString *cstr;
|
|
|
|
char *jstr;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2018-08-23 18:39:35 +02:00
|
|
|
for (i = 0; test_cases[i].json_in; i++) {
|
|
|
|
for (j = 0; j < 2; j++) {
|
2018-08-23 18:39:37 +02:00
|
|
|
if (test_cases[i].utf8_out) {
|
|
|
|
cstr = from_json_str(test_cases[i].json_in, j, &error_abort);
|
2020-12-11 18:11:45 +01:00
|
|
|
g_assert_cmpstr(qstring_get_str(cstr),
|
2018-08-23 18:39:37 +02:00
|
|
|
==, test_cases[i].utf8_out);
|
|
|
|
if (!test_cases[i].skip) {
|
|
|
|
jstr = to_json_str(cstr);
|
|
|
|
g_assert_cmpstr(jstr, ==, test_cases[i].json_in);
|
|
|
|
g_free(jstr);
|
|
|
|
}
|
|
|
|
qobject_unref(cstr);
|
|
|
|
} else {
|
|
|
|
cstr = from_json_str(test_cases[i].json_in, j, NULL);
|
|
|
|
g_assert(!cstr);
|
2018-08-23 18:39:35 +02:00
|
|
|
}
|
2009-11-11 20:16:03 +01:00
|
|
|
}
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 18:39:38 +02:00
|
|
|
static void string_with_quotes(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
2018-08-23 18:39:38 +02:00
|
|
|
const char *test_cases[] = {
|
|
|
|
"\"the bee's knees\"",
|
|
|
|
"'double quote \"'",
|
|
|
|
NULL
|
2009-11-11 17:39:47 +01:00
|
|
|
};
|
|
|
|
int i;
|
2018-08-23 18:39:38 +02:00
|
|
|
QString *str;
|
|
|
|
char *cstr;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2018-08-23 18:39:38 +02:00
|
|
|
for (i = 0; test_cases[i]; i++) {
|
|
|
|
str = qobject_to(QString,
|
|
|
|
qobject_from_json(test_cases[i], &error_abort));
|
2017-02-17 21:38:20 +01:00
|
|
|
g_assert(str);
|
2018-08-23 18:39:38 +02:00
|
|
|
cstr = g_strndup(test_cases[i] + 1, strlen(test_cases[i]) - 2);
|
|
|
|
g_assert_cmpstr(qstring_get_str(str), ==, cstr);
|
|
|
|
g_free(cstr);
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(str);
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-05 09:30:55 +01:00
|
|
|
static void utf8_string(void)
|
|
|
|
{
|
|
|
|
/*
|
2013-04-11 18:07:20 +02:00
|
|
|
* Most test cases are scraped from Markus Kuhn's UTF-8 decoder
|
2013-02-05 09:30:55 +01:00
|
|
|
* capability and stress test at
|
|
|
|
* http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
|
|
|
*/
|
|
|
|
static const struct {
|
2018-08-23 18:39:39 +02:00
|
|
|
/* Content of JSON string to parse with qobject_from_json() */
|
2013-02-05 09:30:55 +01:00
|
|
|
const char *json_in;
|
2018-08-23 18:39:39 +02:00
|
|
|
/* Expected parse output */
|
2013-02-05 09:30:55 +01:00
|
|
|
const char *utf8_out;
|
2018-08-23 18:39:39 +02:00
|
|
|
/* Expected unparse output, defaults to @json_in */
|
|
|
|
const char *json_out;
|
2013-02-05 09:30:55 +01:00
|
|
|
} test_cases[] = {
|
2018-08-23 18:39:42 +02:00
|
|
|
/* 0 Control characters */
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Note: \x00 is impossible, other representations of
|
|
|
|
* U+0000 are covered under 4.3
|
|
|
|
*/
|
|
|
|
"\x01\x02\x03\x04\x05\x06\x07"
|
|
|
|
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
|
|
|
|
"\x10\x11\x12\x13\x14\x15\x16\x17"
|
|
|
|
"\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
|
2018-08-23 18:39:45 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:42 +02:00
|
|
|
"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007"
|
|
|
|
"\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F"
|
|
|
|
"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017"
|
|
|
|
"\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F",
|
|
|
|
},
|
2013-02-05 09:30:55 +01:00
|
|
|
/* 1 Some correct UTF-8 text */
|
|
|
|
{
|
|
|
|
/* a bit of German */
|
|
|
|
"Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt"
|
|
|
|
" jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.",
|
2018-08-23 18:39:39 +02:00
|
|
|
"Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt"
|
|
|
|
" jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.",
|
|
|
|
"Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt"
|
|
|
|
" jeden gr\\u00F6\\u00DFeren Zwerg.",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* a bit of Greek */
|
|
|
|
"\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5",
|
|
|
|
"\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5",
|
json: Fix % handling when not interpolating
Commit 8bca4613 added support for %% in json strings when interpolating,
but in doing so broke handling of % when not interpolating.
When parse_string() is fed a string token containing '%', it skips the
'%' regardless of ctxt->ap, i.e. even it's not interpolating. If the
'%' is the string's last character, it fails an assertion. Else, it
"merely" swallows the '%'.
Fix parse_string() to handle '%' specially only when interpolating.
To gauge the bug's impact, let's review non-interpolating users of this
parser, i.e. code passing NULL context to json_message_parser_init():
* tests/check-qjson.c, tests/test-qobject-input-visitor.c,
tests/test-visitor-serialization.c
Plenty of tests, but we still failed to cover the buggy case.
* monitor.c: QMP input
* qga/main.c: QGA input
* qobject_from_json():
- qobject-input-visitor.c: JSON command line option arguments of
-display and -blockdev
Reproducer: -blockdev '{"%"}'
- block.c: JSON pseudo-filenames starting with "json:"
Reproducer: https://bugzilla.redhat.com/show_bug.cgi?id=1668244#c3
- block/rbd.c: JSON key pairs
Pseudo-filenames starting with "rbd:".
Command line, QMP and QGA input are trusted.
Filenames are trusted when they come from command line, QMP or HMP.
They are untrusted when they come from from image file headers.
Example: QCOW2 backing file name. Note that this is *not* the security
boundary between host and guest. It's the boundary between host and an
image file from an untrusted source.
Neither failing an assertion nor skipping a character in a filename of
your choice looks exploitable. Note that we don't support compiling
with NDEBUG.
Fixes: 8bca4613e6cddd948895b8db3def05950463495b
Cc: qemu-stable@nongnu.org
Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
Message-Id: <20190102140535.11512-1-cfergeau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Tested-by: Richard W.M. Jones <rjones@redhat.com>
[Commit message extended to discuss impact]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2019-01-02 15:05:35 +01:00
|
|
|
},
|
|
|
|
/* '%' character when not interpolating */
|
|
|
|
{
|
|
|
|
"100%",
|
|
|
|
"100%",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2 Boundary condition test cases */
|
|
|
|
/* 2.1 First possible sequence of a certain length */
|
2018-08-23 18:39:40 +02:00
|
|
|
/*
|
2018-08-23 18:39:42 +02:00
|
|
|
* 2.1.1 1 byte U+0020
|
|
|
|
* Control characters are already covered by their own test
|
|
|
|
* case under 0. Test the first 1 byte non-control character
|
|
|
|
* here.
|
2018-08-23 18:39:40 +02:00
|
|
|
*/
|
2013-02-05 09:30:55 +01:00
|
|
|
{
|
2018-08-23 18:39:42 +02:00
|
|
|
" ",
|
|
|
|
" ",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.1.2 2 bytes U+0080 */
|
|
|
|
{
|
|
|
|
"\xC2\x80",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xC2\x80",
|
|
|
|
"\\u0080",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.1.3 3 bytes U+0800 */
|
|
|
|
{
|
|
|
|
"\xE0\xA0\x80",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xE0\xA0\x80",
|
|
|
|
"\\u0800",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.1.4 4 bytes U+10000 */
|
|
|
|
{
|
|
|
|
"\xF0\x90\x80\x80",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF0\x90\x80\x80",
|
|
|
|
"\\uD800\\uDC00",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.1.5 5 bytes U+200000 */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF8\x88\x80\x80\x80",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.1.6 6 bytes U+4000000 */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFC\x84\x80\x80\x80\x80",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.2 Last possible sequence of a certain length */
|
|
|
|
/* 2.2.1 1 byte U+007F */
|
|
|
|
{
|
|
|
|
"\x7F",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x7F",
|
|
|
|
"\\u007F",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.2.2 2 bytes U+07FF */
|
|
|
|
{
|
|
|
|
"\xDF\xBF",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xDF\xBF",
|
|
|
|
"\\u07FF",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
2013-04-11 18:07:20 +02:00
|
|
|
/*
|
|
|
|
* 2.2.3 3 bytes U+FFFC
|
|
|
|
* The last possible sequence is actually U+FFFF. But that's
|
|
|
|
* a noncharacter, and already covered by its own test case
|
|
|
|
* under 5.3. Same for U+FFFE. U+FFFD is the last character
|
|
|
|
* in the BMP, and covered under 2.3. Because of U+FFFD's
|
|
|
|
* special role as replacement character, it's worth testing
|
|
|
|
* U+FFFC here.
|
|
|
|
*/
|
2013-02-05 09:30:55 +01:00
|
|
|
{
|
2013-04-11 18:07:20 +02:00
|
|
|
"\xEF\xBF\xBC",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEF\xBF\xBC",
|
|
|
|
"\\uFFFC",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.2.4 4 bytes U+1FFFFF */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF7\xBF\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.2.5 5 bytes U+3FFFFFF */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFB\xBF\xBF\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.2.6 6 bytes U+7FFFFFFF */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFD\xBF\xBF\xBF\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 2.3 Other boundary conditions */
|
|
|
|
{
|
2013-04-11 18:07:19 +02:00
|
|
|
/* last one before surrogate range: U+D7FF */
|
2013-02-05 09:30:55 +01:00
|
|
|
"\xED\x9F\xBF",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\x9F\xBF",
|
|
|
|
"\\uD7FF",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2013-04-11 18:07:19 +02:00
|
|
|
/* first one after surrogate range: U+E000 */
|
2013-02-05 09:30:55 +01:00
|
|
|
"\xEE\x80\x80",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEE\x80\x80",
|
|
|
|
"\\uE000",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2013-04-11 18:07:19 +02:00
|
|
|
/* last one in BMP: U+FFFD */
|
2013-02-05 09:30:55 +01:00
|
|
|
"\xEF\xBF\xBD",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEF\xBF\xBD",
|
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2013-04-11 18:07:20 +02:00
|
|
|
/* last one in last plane: U+10FFFD */
|
|
|
|
"\xF4\x8F\xBF\xBD",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF4\x8F\xBF\xBD",
|
|
|
|
"\\uDBFF\\uDFFD"
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2013-04-11 18:07:19 +02:00
|
|
|
/* first one beyond Unicode range: U+110000 */
|
2013-02-05 09:30:55 +01:00
|
|
|
"\xF4\x90\x80\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3 Malformed sequences */
|
|
|
|
/* 3.1 Unexpected continuation bytes */
|
|
|
|
/* 3.1.1 First continuation byte */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.2 Last continuation byte */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.3 2 continuation bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.4 3 continuation bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80\xBF\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.5 4 continuation bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80\xBF\x80\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.6 5 continuation bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80\xBF\x80\xBF\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.7 6 continuation bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80\xBF\x80\xBF\x80\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.8 7 continuation bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80\xBF\x80\xBF\x80\xBF\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.1.9 Sequence of all 64 possible continuation bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\x80\x81\x82\x83\x84\x85\x86\x87"
|
2013-02-05 09:30:55 +01:00
|
|
|
"\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F"
|
|
|
|
"\x90\x91\x92\x93\x94\x95\x96\x97"
|
|
|
|
"\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F"
|
|
|
|
"\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7"
|
|
|
|
"\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF"
|
|
|
|
"\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7"
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2013-04-11 18:07:21 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
2018-08-23 18:39:49 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.2 Lonely start characters */
|
|
|
|
/* 3.2.1 All 32 first bytes of 2-byte sequences, followed by space */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 "
|
2013-02-05 09:30:55 +01:00
|
|
|
"\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF "
|
|
|
|
"\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 "
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF ",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2013-04-11 18:07:21 +02:00
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD "
|
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD "
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD "
|
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.2.2 All 16 first bytes of 3-byte sequences, followed by space */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 "
|
|
|
|
"\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF ",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD "
|
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.2.3 All 8 first bytes of 4-byte sequences, followed by space */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 ",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.2.4 All 4 first bytes of 5-byte sequences, followed by space */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF8 \xF9 \xFA \xFB ",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD \\uFFFD \\uFFFD \\uFFFD ",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.2.5 All 2 first bytes of 6-byte sequences, followed by space */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFC \xFD ",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD \\uFFFD ",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3 Sequences with last continuation byte missing */
|
|
|
|
/* 3.3.1 2-byte sequence with last byte missing (U+0000) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xC0",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.2 3-byte sequence with last byte missing (U+0000) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xE0\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.3 4-byte sequence with last byte missing (U+0000) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF0\x80\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.4 5-byte sequence with last byte missing (U+0000) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF8\x80\x80\x80",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.5 6-byte sequence with last byte missing (U+0000) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFC\x80\x80\x80\x80",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.6 2-byte sequence with last byte missing (U+07FF) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xDF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.7 3-byte sequence with last byte missing (U+FFFF) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.8 4-byte sequence with last byte missing (U+1FFFFF) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF7\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.9 5-byte sequence with last byte missing (U+3FFFFFF) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFB\xBF\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.3.10 6-byte sequence with last byte missing (U+7FFFFFFF) */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFD\xBF\xBF\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.4 Concatenation of incomplete sequences */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80"
|
|
|
|
"\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 3.5 Impossible bytes */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFE",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFE\xFE\xFF\xFF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 4 Overlong sequences */
|
|
|
|
/* 4.1 Overlong '/' */
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xC0\xAF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xE0\x80\xAF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF0\x80\x80\xAF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF8\x80\x80\x80\xAF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFC\x80\x80\x80\x80\xAF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
2013-04-11 18:07:19 +02:00
|
|
|
/*
|
|
|
|
* 4.2 Maximum overlong sequences
|
|
|
|
* Highest Unicode value that is still resulting in an
|
|
|
|
* overlong sequence if represented with the given number of
|
|
|
|
* bytes. This is a boundary test for safe UTF-8 decoders.
|
|
|
|
*/
|
2013-02-05 09:30:55 +01:00
|
|
|
{
|
|
|
|
/* \U+007F */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xC1\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+07FF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xE0\x9F\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
2013-04-11 18:07:20 +02:00
|
|
|
/*
|
|
|
|
* \U+FFFC
|
|
|
|
* The actual maximum would be U+FFFF, but that's a
|
|
|
|
* noncharacter. Testing U+FFFC seems more useful. See
|
|
|
|
* also 2.2.3
|
|
|
|
*/
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF0\x8F\xBF\xBC",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+1FFFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF8\x87\xBF\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+3FFFFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFC\x83\xBF\xBF\xBF\xBF",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 4.3 Overlong representation of the NUL character */
|
|
|
|
{
|
|
|
|
/* \U+0000 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xC0\x80",
|
2018-08-23 18:39:52 +02:00
|
|
|
"\xC0\x80",
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\u0000",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+0000 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xE0\x80\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+0000 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF0\x80\x80\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+0000 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF8\x80\x80\x80\x80",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+0000 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xFC\x80\x80\x80\x80\x80",
|
2018-08-23 18:39:48 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 5 Illegal code positions */
|
|
|
|
/* 5.1 Single UTF-16 surrogates */
|
|
|
|
{
|
|
|
|
/* \U+D800 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xA0\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DB7F */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAD\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DB80 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAE\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DBFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DC00 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xB0\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DF80 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xBE\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 5.2 Paired UTF-16 surrogates */
|
|
|
|
{
|
|
|
|
/* \U+D800\U+DC00 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xA0\x80\xED\xB0\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+D800\U+DFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xA0\x80\xED\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DB7F\U+DC00 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAD\xBF\xED\xB0\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DB7F\U+DFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAD\xBF\xED\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DB80\U+DC00 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAE\x80\xED\xB0\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DB80\U+DFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAE\x80\xED\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DBFF\U+DC00 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAF\xBF\xED\xB0\x80",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+DBFF\U+DFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xED\xAF\xBF\xED\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
/* 5.3 Other illegal code positions */
|
2013-04-11 18:07:20 +02:00
|
|
|
/* BMP noncharacters */
|
2013-02-05 09:30:55 +01:00
|
|
|
{
|
|
|
|
/* \U+FFFE */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEF\xBF\xBE",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* \U+FFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEF\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-02-05 09:30:55 +01:00
|
|
|
},
|
2013-04-11 18:07:20 +02:00
|
|
|
{
|
|
|
|
/* U+FDD0 */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEF\xB7\x90",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-04-11 18:07:20 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
/* U+FDEF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xEF\xB7\xAF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD",
|
2013-04-11 18:07:20 +02:00
|
|
|
},
|
|
|
|
/* Plane 1 .. 16 noncharacters */
|
|
|
|
{
|
|
|
|
/* U+1FFFE U+1FFFF U+2FFFE U+2FFFF ... U+10FFFE U+10FFFF */
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF0\x9F\xBF\xBE\xF0\x9F\xBF\xBF"
|
2013-04-11 18:07:20 +02:00
|
|
|
"\xF0\xAF\xBF\xBE\xF0\xAF\xBF\xBF"
|
|
|
|
"\xF0\xBF\xBF\xBE\xF0\xBF\xBF\xBF"
|
|
|
|
"\xF1\x8F\xBF\xBE\xF1\x8F\xBF\xBF"
|
|
|
|
"\xF1\x9F\xBF\xBE\xF1\x9F\xBF\xBF"
|
|
|
|
"\xF1\xAF\xBF\xBE\xF1\xAF\xBF\xBF"
|
|
|
|
"\xF1\xBF\xBF\xBE\xF1\xBF\xBF\xBF"
|
|
|
|
"\xF2\x8F\xBF\xBE\xF2\x8F\xBF\xBF"
|
|
|
|
"\xF2\x9F\xBF\xBE\xF2\x9F\xBF\xBF"
|
|
|
|
"\xF2\xAF\xBF\xBE\xF2\xAF\xBF\xBF"
|
|
|
|
"\xF2\xBF\xBF\xBE\xF2\xBF\xBF\xBF"
|
|
|
|
"\xF3\x8F\xBF\xBE\xF3\x8F\xBF\xBF"
|
|
|
|
"\xF3\x9F\xBF\xBE\xF3\x9F\xBF\xBF"
|
|
|
|
"\xF3\xAF\xBF\xBE\xF3\xAF\xBF\xBF"
|
|
|
|
"\xF3\xBF\xBF\xBE\xF3\xBF\xBF\xBF"
|
2018-08-23 18:39:39 +02:00
|
|
|
"\xF4\x8F\xBF\xBE\xF4\x8F\xBF\xBF",
|
2018-08-23 18:39:49 +02:00
|
|
|
NULL,
|
2013-04-11 18:07:21 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
2018-08-23 18:39:39 +02:00
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD"
|
|
|
|
"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD",
|
2013-04-11 18:07:20 +02:00
|
|
|
},
|
2013-02-05 09:30:55 +01:00
|
|
|
{}
|
|
|
|
};
|
2018-08-23 18:39:39 +02:00
|
|
|
int i, j;
|
2013-02-05 09:30:55 +01:00
|
|
|
QString *str;
|
2018-08-23 18:39:41 +02:00
|
|
|
const char *json_in, *utf8_out, *utf8_in, *json_out, *tail;
|
|
|
|
char *end, *in, *jstr;
|
2013-02-05 09:30:55 +01:00
|
|
|
|
|
|
|
for (i = 0; test_cases[i].json_in; i++) {
|
2018-08-23 18:39:39 +02:00
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
json_in = test_cases[i].json_in;
|
|
|
|
utf8_out = test_cases[i].utf8_out;
|
2018-08-23 18:39:40 +02:00
|
|
|
utf8_in = test_cases[i].utf8_out ?: test_cases[i].json_in;
|
2018-08-23 18:39:39 +02:00
|
|
|
json_out = test_cases[i].json_out ?: test_cases[i].json_in;
|
2013-02-05 09:30:55 +01:00
|
|
|
|
2018-08-23 18:39:39 +02:00
|
|
|
/* Parse @json_in, expect @utf8_out */
|
|
|
|
if (utf8_out) {
|
|
|
|
str = from_json_str(json_in, j, &error_abort);
|
2020-12-11 18:11:45 +01:00
|
|
|
g_assert_cmpstr(qstring_get_str(str), ==, utf8_out);
|
2018-08-23 18:39:39 +02:00
|
|
|
qobject_unref(str);
|
|
|
|
} else {
|
|
|
|
str = from_json_str(json_in, j, NULL);
|
|
|
|
g_assert(!str);
|
2018-08-23 18:39:41 +02:00
|
|
|
/*
|
|
|
|
* Failure may be due to any sequence, but *all* sequences
|
|
|
|
* are expected to fail. Test each one in isolation.
|
|
|
|
*/
|
|
|
|
for (tail = json_in; *tail; tail = end) {
|
|
|
|
mod_utf8_codepoint(tail, 6, &end);
|
|
|
|
if (*end == ' ') {
|
|
|
|
end++;
|
|
|
|
}
|
2019-06-08 07:25:52 +02:00
|
|
|
in = g_strndup(tail, end - tail);
|
2018-08-23 18:39:41 +02:00
|
|
|
str = from_json_str(in, j, NULL);
|
|
|
|
g_assert(!str);
|
|
|
|
g_free(in);
|
|
|
|
}
|
2018-08-23 18:39:39 +02:00
|
|
|
}
|
2013-02-05 09:30:55 +01:00
|
|
|
|
2018-08-23 18:39:39 +02:00
|
|
|
/* Unparse @utf8_in, expect @json_out */
|
|
|
|
str = qstring_from_str(utf8_in);
|
|
|
|
jstr = to_json_str(str);
|
|
|
|
g_assert_cmpstr(jstr, ==, json_out);
|
|
|
|
qobject_unref(str);
|
|
|
|
g_free(jstr);
|
2013-02-05 09:30:55 +01:00
|
|
|
|
2018-08-23 18:39:57 +02:00
|
|
|
/* Parse @json_out right back, unless it has replacements */
|
|
|
|
if (!strstr(json_out, "\\uFFFD")) {
|
2018-08-23 18:39:39 +02:00
|
|
|
str = from_json_str(json_out, j, &error_abort);
|
2020-12-11 18:11:45 +01:00
|
|
|
g_assert_cmpstr(qstring_get_str(str), ==, utf8_in);
|
2018-09-01 23:19:17 +02:00
|
|
|
qobject_unref(str);
|
2018-08-23 18:39:39 +02:00
|
|
|
}
|
2013-02-05 09:30:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 17:14:45 +01:00
|
|
|
static void int_number(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
struct {
|
|
|
|
const char *encoded;
|
|
|
|
int64_t decoded;
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
const char *reencoded;
|
2009-11-11 17:39:47 +01:00
|
|
|
} test_cases[] = {
|
|
|
|
{ "0", 0 },
|
|
|
|
{ "1234", 1234 },
|
|
|
|
{ "1", 1 },
|
|
|
|
{ "-32", -32 },
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
{ "-0", 0, "0" },
|
|
|
|
{},
|
2009-11-11 17:39:47 +01:00
|
|
|
};
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
int i;
|
|
|
|
QNum *qnum;
|
2020-12-10 17:14:44 +01:00
|
|
|
int64_t ival;
|
|
|
|
uint64_t uval;
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *str;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
|
|
|
for (i = 0; test_cases[i].encoded; i++) {
|
2018-02-24 16:40:29 +01:00
|
|
|
qnum = qobject_to(QNum,
|
|
|
|
qobject_from_json(test_cases[i].encoded,
|
|
|
|
&error_abort));
|
2017-06-07 18:35:58 +02:00
|
|
|
g_assert(qnum);
|
2020-12-10 17:14:44 +01:00
|
|
|
g_assert(qnum_get_try_int(qnum, &ival));
|
|
|
|
g_assert_cmpint(ival, ==, test_cases[i].decoded);
|
|
|
|
if (test_cases[i].decoded >= 0) {
|
|
|
|
g_assert(qnum_get_try_uint(qnum, &uval));
|
|
|
|
g_assert_cmpuint(uval, ==, (uint64_t)test_cases[i].decoded);
|
|
|
|
} else {
|
|
|
|
g_assert(!qnum_get_try_uint(qnum, &uval));
|
|
|
|
}
|
|
|
|
g_assert_cmpfloat(qnum_get_double(qnum), ==,
|
|
|
|
(double)test_cases[i].decoded);
|
2009-11-11 20:16:03 +01:00
|
|
|
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
str = qobject_to_json(QOBJECT(qnum));
|
2020-12-11 18:11:37 +01:00
|
|
|
g_assert_cmpstr(str->str, ==,
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
test_cases[i].reencoded ?: test_cases[i].encoded);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_string_free(str, true);
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(qnum);
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 17:14:45 +01:00
|
|
|
static void uint_number(void)
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
const char *encoded;
|
|
|
|
uint64_t decoded;
|
|
|
|
const char *reencoded;
|
|
|
|
} test_cases[] = {
|
|
|
|
{ "9223372036854775808", (uint64_t)1 << 63 },
|
2020-12-10 17:14:46 +01:00
|
|
|
{ "18446744073709551615", UINT64_MAX },
|
2020-12-10 17:14:45 +01:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
int i;
|
|
|
|
QNum *qnum;
|
|
|
|
int64_t ival;
|
|
|
|
uint64_t uval;
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *str;
|
2020-12-10 17:14:45 +01:00
|
|
|
|
|
|
|
for (i = 0; test_cases[i].encoded; i++) {
|
|
|
|
qnum = qobject_to(QNum,
|
|
|
|
qobject_from_json(test_cases[i].encoded,
|
|
|
|
&error_abort));
|
|
|
|
g_assert(qnum);
|
|
|
|
g_assert(qnum_get_try_uint(qnum, &uval));
|
|
|
|
g_assert_cmpuint(uval, ==, test_cases[i].decoded);
|
|
|
|
g_assert(!qnum_get_try_int(qnum, &ival));
|
|
|
|
g_assert_cmpfloat(qnum_get_double(qnum), ==,
|
|
|
|
(double)test_cases[i].decoded);
|
|
|
|
|
|
|
|
str = qobject_to_json(QOBJECT(qnum));
|
2020-12-11 18:11:37 +01:00
|
|
|
g_assert_cmpstr(str->str, ==,
|
2020-12-10 17:14:45 +01:00
|
|
|
test_cases[i].reencoded ?: test_cases[i].encoded);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_string_free(str, true);
|
2020-12-10 17:14:45 +01:00
|
|
|
|
|
|
|
qobject_unref(qnum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void float_number(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
struct {
|
|
|
|
const char *encoded;
|
|
|
|
double decoded;
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
const char *reencoded;
|
2009-11-11 17:39:47 +01:00
|
|
|
} test_cases[] = {
|
|
|
|
{ "32.43", 32.43 },
|
|
|
|
{ "0.222", 0.222 },
|
qobject: Fix qnum_to_string() to use sufficient precision
We should serialize numbers to JSON so that they deserialize back to
the same number. We fail to do so.
The culprit is qnum_to_string(): it uses format %f with trailing '0'
trimmed. Results in pretty output for "nice" numbers, but is prone to
nasty rounding errors. For instance, numbers between 0 and 0.0000005
get flushed to zero.
Where exactly the incorrect rounding can bite is tiresome to gauge.
Here's my take.
* In QMP output, type 'number':
- query-blockstats value avg_rd_queue_depth
- QMP query-migrate values mbps, cache-miss-rate, encoding-rate,
busy-rate, compression-rate.
Relatively harmless, I guess.
* In tracing QMP input. Harmless.
* In qemu-ga output, type 'number': guest-get-users value login-time.
Harmless.
* In output of HMP qom-get. Harmless.
Not affected, because double values don't actually occur there (I
think):
* QMP output, type 'any':
* qom-get value
* qom-list, qom-list-properties value default-value
* query-cpu-model-comparison, query-cpu-model-baseline,
query-cpu-model-expansion value props.
* qemu-img --output json output.
* "json:" pseudo-filenames generated by bdrv_refresh_filename().
* The rbd block driver's "=keyvalue-pairs" hack.
* In -object help on property default values. Aside: use of JSON
feels inappropriate here.
* Output of HMP qom-get.
* Argument conversion to QemuOpts for qdev_device_add() and HMP with
qemu_opts_from_qdict()
QMP and HMP device_add, virtio-net failover primary creation,
xen-usb "usb-host" creation, HMP netdev_add, object_add.
* The uses of qobject_input_visitor_new_flat_confused()
As far as I can tell, none of the visited types contain double
values.
* Dumping ImageInfoSpecific with dump_qobject()
Fix by formatting with %.17g. 17 decimal digits always suffice for
IEEE double.
The change to expected test output illustrates the effect: the
rounding errors are gone, but some seemingly "nice" numbers now get
converted to not so nice strings, e.g. 0.42 to "0.41999999999999998".
This is because 0.42 is not representable exactly in double. It's
more accurate in this example than strictly necessary, though.
If ugly accuracy bothers us, we can we can try using the least number
of digits that still converts back to the same double. In this
example, "0.42" would do.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-7-armbru@redhat.com>
2020-12-10 17:14:48 +01:00
|
|
|
{ "-32.12313", -32.12313, "-32.123130000000003" },
|
|
|
|
{ "-32.20e-10", -32.20e-10, "-3.22e-09" },
|
|
|
|
{ "18446744073709551616", 0x1p64, "1.8446744073709552e+19" },
|
|
|
|
{ "-9223372036854775809", -0x1p63, "-9.2233720368547758e+18" },
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
{},
|
2009-11-11 17:39:47 +01:00
|
|
|
};
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
int i;
|
|
|
|
QNum *qnum;
|
2020-12-10 17:14:44 +01:00
|
|
|
int64_t ival;
|
|
|
|
uint64_t uval;
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *str;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
|
|
|
for (i = 0; test_cases[i].encoded; i++) {
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
qnum = qobject_to(QNum,
|
|
|
|
qobject_from_json(test_cases[i].encoded,
|
|
|
|
&error_abort));
|
2017-06-07 18:35:58 +02:00
|
|
|
g_assert(qnum);
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
g_assert_cmpfloat(qnum_get_double(qnum), ==, test_cases[i].decoded);
|
2020-12-10 17:14:44 +01:00
|
|
|
g_assert(!qnum_get_try_int(qnum, &ival));
|
|
|
|
g_assert(!qnum_get_try_uint(qnum, &uval));
|
2009-11-11 20:16:03 +01:00
|
|
|
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
str = qobject_to_json(QOBJECT(qnum));
|
2020-12-11 18:11:37 +01:00
|
|
|
g_assert_cmpstr(str->str, ==,
|
tests/check-qjson: Don't skip funny QNumber to JSON conversions
simple_number() and float_number() convert from JSON to QNumber and
back.
simple_number() tests "-0", but skips the conversion back to JSON,
because it yields "0", not "-0". Works as intended, so better cover
it: don't skip, but expect the funny result.
float_number() tests "-32.20e-10", but skips the conversion back to
JSON, because it yields "-0". This is a known bug in
qnum_to_string(), marked FIXME there. Cover the bug: don't skip, but
expect the funny result.
While there, switch from g_assert() to g_assert_cmpstr() & friends for
friendlier test failures.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201210161452.2813491-2-armbru@redhat.com>
2020-12-10 17:14:43 +01:00
|
|
|
test_cases[i].reencoded ?: test_cases[i].encoded);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_string_free(str, true);
|
2009-11-11 20:16:03 +01:00
|
|
|
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(qnum);
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void keyword_literal(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
QObject *obj;
|
|
|
|
QBool *qbool;
|
2017-06-26 13:52:24 +02:00
|
|
|
QNull *null;
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *str;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json("true", &error_abort);
|
2018-02-24 16:40:29 +01:00
|
|
|
qbool = qobject_to(QBool, obj);
|
2017-02-17 21:38:23 +01:00
|
|
|
g_assert(qbool);
|
2015-05-16 00:24:59 +02:00
|
|
|
g_assert(qbool_get_bool(qbool) == true);
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2009-11-11 20:16:03 +01:00
|
|
|
str = qobject_to_json(obj);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_assert_cmpstr(str->str, ==, "true");
|
|
|
|
g_string_free(str, true);
|
2009-11-11 20:16:03 +01:00
|
|
|
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(qbool);
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json("false", &error_abort);
|
2018-02-24 16:40:29 +01:00
|
|
|
qbool = qobject_to(QBool, obj);
|
2017-02-17 21:38:23 +01:00
|
|
|
g_assert(qbool);
|
2015-05-16 00:24:59 +02:00
|
|
|
g_assert(qbool_get_bool(qbool) == false);
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2009-11-11 20:16:03 +01:00
|
|
|
str = qobject_to_json(obj);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_assert_cmpstr(str->str, ==, "false");
|
|
|
|
g_string_free(str, true);
|
2009-11-11 20:16:03 +01:00
|
|
|
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(qbool);
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
obj = qobject_from_json("null", &error_abort);
|
|
|
|
g_assert(obj != NULL);
|
|
|
|
g_assert(qobject_type(obj) == QTYPE_QNULL);
|
|
|
|
|
|
|
|
null = qnull();
|
|
|
|
g_assert(QOBJECT(null) == obj);
|
|
|
|
|
|
|
|
qobject_unref(obj);
|
|
|
|
qobject_unref(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void interpolation_valid(void)
|
|
|
|
{
|
|
|
|
long long value_lld = 0x123456789abcdefLL;
|
2018-08-23 18:40:08 +02:00
|
|
|
int64_t value_d64 = value_lld;
|
2018-08-23 18:39:43 +02:00
|
|
|
long value_ld = (long)value_lld;
|
|
|
|
int value_d = (int)value_lld;
|
|
|
|
unsigned long long value_llu = 0xfedcba9876543210ULL;
|
2018-08-23 18:40:08 +02:00
|
|
|
uint64_t value_u64 = value_llu;
|
2018-08-23 18:39:43 +02:00
|
|
|
unsigned long value_lu = (unsigned long)value_llu;
|
|
|
|
unsigned value_u = (unsigned)value_llu;
|
|
|
|
double value_f = 2.323423423;
|
|
|
|
const char *value_s = "hello world";
|
|
|
|
QObject *value_p = QOBJECT(qnull());
|
|
|
|
QBool *qbool;
|
|
|
|
QNum *qnum;
|
|
|
|
QString *qstr;
|
|
|
|
QObject *qobj;
|
|
|
|
|
|
|
|
/* bool */
|
|
|
|
|
2018-08-06 08:53:27 +02:00
|
|
|
qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", false));
|
2017-02-17 21:38:23 +01:00
|
|
|
g_assert(qbool);
|
2015-05-16 00:24:59 +02:00
|
|
|
g_assert(qbool_get_bool(qbool) == false);
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(qbool);
|
json-parser: Accept 'null' in QMP
We document that in QMP, the client may send any json-value
for the optional "id" key, and then return that same value
on reply (both success and failures, insofar as the failure
happened after parsing the id). [Note that the output may
not be identical to the input, as whitespace may change and
since we may reorder keys within a json-object, but that this
still constitutes the same json-value]. However, we were not
handling the JSON literal null, which counts as a json-value
per RFC 7159.
Also, down the road, given the QAPI schema of {'*foo':'str'} or
{'*foo':'ComplexType'}, we could decide to allow the QMP client
to pass { "foo":null } instead of the current representation of
{ } where omitting the key is the only way to get at the default
NULL value. Such a change might be useful for argument
introspection (if a type in older qemu lacks 'foo' altogether,
then an explicit "foo":null probe will force an easily
distinguished error message for whether the optional "foo" key
is even understood in newer qemu). And if we add default values
to optional arguments, allowing an explicit null would be
required for getting a NULL value associated with an optional
string that has a non-null default. But all that can come at a
later day.
The 'check-unit' testsuite is enhanced to test that parsing
produces the same object as explicitly requesting a reference
to the special qnull object. In addition, I tested with:
$ ./x86_64-softmmu/qemu-system-x86_64 -qmp stdio -nodefaults
{"QMP": {"version": {"qemu": {"micro": 91, "minor": 2, "major": 2}, "package": ""}, "capabilities": []}}
{"execute":"qmp_capabilities","id":null}
{"return": {}, "id": null}
{"id":{"a":null,"b":[1,null]},"execute":"quit"}
{"return": {}, "id": {"a": null, "b": [1, null]}}
{"timestamp": {"seconds": 1427742379, "microseconds": 423128}, "event": "SHUTDOWN"}
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-04-29 23:35:06 +02:00
|
|
|
|
2015-05-16 00:24:59 +02:00
|
|
|
/* Test that non-zero values other than 1 get collapsed to true */
|
2018-08-06 08:53:27 +02:00
|
|
|
qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", 2));
|
2017-02-17 21:38:23 +01:00
|
|
|
g_assert(qbool);
|
2015-05-16 00:24:59 +02:00
|
|
|
g_assert(qbool_get_bool(qbool) == true);
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(qbool);
|
json-parser: Accept 'null' in QMP
We document that in QMP, the client may send any json-value
for the optional "id" key, and then return that same value
on reply (both success and failures, insofar as the failure
happened after parsing the id). [Note that the output may
not be identical to the input, as whitespace may change and
since we may reorder keys within a json-object, but that this
still constitutes the same json-value]. However, we were not
handling the JSON literal null, which counts as a json-value
per RFC 7159.
Also, down the road, given the QAPI schema of {'*foo':'str'} or
{'*foo':'ComplexType'}, we could decide to allow the QMP client
to pass { "foo":null } instead of the current representation of
{ } where omitting the key is the only way to get at the default
NULL value. Such a change might be useful for argument
introspection (if a type in older qemu lacks 'foo' altogether,
then an explicit "foo":null probe will force an easily
distinguished error message for whether the optional "foo" key
is even understood in newer qemu). And if we add default values
to optional arguments, allowing an explicit null would be
required for getting a NULL value associated with an optional
string that has a non-null default. But all that can come at a
later day.
The 'check-unit' testsuite is enhanced to test that parsing
produces the same object as explicitly requesting a reference
to the special qnull object. In addition, I tested with:
$ ./x86_64-softmmu/qemu-system-x86_64 -qmp stdio -nodefaults
{"QMP": {"version": {"qemu": {"micro": 91, "minor": 2, "major": 2}, "package": ""}, "capabilities": []}}
{"execute":"qmp_capabilities","id":null}
{"return": {}, "id": null}
{"id":{"a":null,"b":[1,null]},"execute":"quit"}
{"return": {}, "id": {"a": null, "b": [1, null]}}
{"timestamp": {"seconds": 1427742379, "microseconds": 423128}, "event": "SHUTDOWN"}
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-04-29 23:35:06 +02:00
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
/* number */
|
json-parser: Accept 'null' in QMP
We document that in QMP, the client may send any json-value
for the optional "id" key, and then return that same value
on reply (both success and failures, insofar as the failure
happened after parsing the id). [Note that the output may
not be identical to the input, as whitespace may change and
since we may reorder keys within a json-object, but that this
still constitutes the same json-value]. However, we were not
handling the JSON literal null, which counts as a json-value
per RFC 7159.
Also, down the road, given the QAPI schema of {'*foo':'str'} or
{'*foo':'ComplexType'}, we could decide to allow the QMP client
to pass { "foo":null } instead of the current representation of
{ } where omitting the key is the only way to get at the default
NULL value. Such a change might be useful for argument
introspection (if a type in older qemu lacks 'foo' altogether,
then an explicit "foo":null probe will force an easily
distinguished error message for whether the optional "foo" key
is even understood in newer qemu). And if we add default values
to optional arguments, allowing an explicit null would be
required for getting a NULL value associated with an optional
string that has a non-null default. But all that can come at a
later day.
The 'check-unit' testsuite is enhanced to test that parsing
produces the same object as explicitly requesting a reference
to the special qnull object. In addition, I tested with:
$ ./x86_64-softmmu/qemu-system-x86_64 -qmp stdio -nodefaults
{"QMP": {"version": {"qemu": {"micro": 91, "minor": 2, "major": 2}, "package": ""}, "capabilities": []}}
{"execute":"qmp_capabilities","id":null}
{"return": {}, "id": null}
{"id":{"a":null,"b":[1,null]},"execute":"quit"}
{"return": {}, "id": {"a": null, "b": [1, null]}}
{"timestamp": {"seconds": 1427742379, "microseconds": 423128}, "event": "SHUTDOWN"}
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-04-29 23:35:06 +02:00
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%d", value_d));
|
|
|
|
g_assert_cmpint(qnum_get_int(qnum), ==, value_d);
|
|
|
|
qobject_unref(qnum);
|
json-parser: Accept 'null' in QMP
We document that in QMP, the client may send any json-value
for the optional "id" key, and then return that same value
on reply (both success and failures, insofar as the failure
happened after parsing the id). [Note that the output may
not be identical to the input, as whitespace may change and
since we may reorder keys within a json-object, but that this
still constitutes the same json-value]. However, we were not
handling the JSON literal null, which counts as a json-value
per RFC 7159.
Also, down the road, given the QAPI schema of {'*foo':'str'} or
{'*foo':'ComplexType'}, we could decide to allow the QMP client
to pass { "foo":null } instead of the current representation of
{ } where omitting the key is the only way to get at the default
NULL value. Such a change might be useful for argument
introspection (if a type in older qemu lacks 'foo' altogether,
then an explicit "foo":null probe will force an easily
distinguished error message for whether the optional "foo" key
is even understood in newer qemu). And if we add default values
to optional arguments, allowing an explicit null would be
required for getting a NULL value associated with an optional
string that has a non-null default. But all that can come at a
later day.
The 'check-unit' testsuite is enhanced to test that parsing
produces the same object as explicitly requesting a reference
to the special qnull object. In addition, I tested with:
$ ./x86_64-softmmu/qemu-system-x86_64 -qmp stdio -nodefaults
{"QMP": {"version": {"qemu": {"micro": 91, "minor": 2, "major": 2}, "package": ""}, "capabilities": []}}
{"execute":"qmp_capabilities","id":null}
{"return": {}, "id": null}
{"id":{"a":null,"b":[1,null]},"execute":"quit"}
{"return": {}, "id": {"a": null, "b": [1, null]}}
{"timestamp": {"seconds": 1427742379, "microseconds": 423128}, "event": "SHUTDOWN"}
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-04-29 23:35:06 +02:00
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%ld", value_ld));
|
|
|
|
g_assert_cmpint(qnum_get_int(qnum), ==, value_ld);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lld", value_lld));
|
|
|
|
g_assert_cmpint(qnum_get_int(qnum), ==, value_lld);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
2018-08-23 18:40:08 +02:00
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRId64, value_d64));
|
|
|
|
g_assert_cmpint(qnum_get_int(qnum), ==, value_lld);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%u", value_u));
|
|
|
|
g_assert_cmpuint(qnum_get_uint(qnum), ==, value_u);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lu", value_lu));
|
|
|
|
g_assert_cmpuint(qnum_get_uint(qnum), ==, value_lu);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%llu", value_llu));
|
|
|
|
g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
2018-08-23 18:40:08 +02:00
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRIu64, value_u64));
|
|
|
|
g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%f", value_f));
|
|
|
|
g_assert(qnum_get_double(qnum) == value_f);
|
|
|
|
qobject_unref(qnum);
|
|
|
|
|
|
|
|
/* string */
|
|
|
|
|
2020-12-11 18:11:45 +01:00
|
|
|
qstr = qobject_to(QString, qobject_from_jsonf_nofail("%s", value_s));
|
|
|
|
g_assert_cmpstr(qstring_get_str(qstr), ==, value_s);
|
2018-08-23 18:39:43 +02:00
|
|
|
qobject_unref(qstr);
|
|
|
|
|
|
|
|
/* object */
|
|
|
|
|
|
|
|
qobj = qobject_from_jsonf_nofail("%p", value_p);
|
|
|
|
g_assert(qobj == value_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void interpolation_unknown(void)
|
|
|
|
{
|
|
|
|
if (g_test_subprocess()) {
|
|
|
|
qobject_from_jsonf_nofail("%x", 666);
|
|
|
|
}
|
|
|
|
g_test_trap_subprocess(NULL, 0, 0);
|
|
|
|
g_test_trap_assert_failed();
|
2018-08-23 18:40:07 +02:00
|
|
|
g_test_trap_assert_stderr("*Unexpected error*"
|
|
|
|
"invalid interpolation '%x'*");
|
2018-08-23 18:39:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void interpolation_string(void)
|
|
|
|
{
|
json: Improve safety of qobject_from_jsonf_nofail() & friends
The JSON parser optionally supports interpolation. This is used to
build QObjects by parsing string templates. The templates are C
literals, so parse errors (such as invalid interpolation
specifications) are actually programming errors. Consequently, the
functions providing parsing with interpolation
(qobject_from_jsonf_nofail(), qobject_from_vjsonf_nofail(),
qdict_from_jsonf_nofail(), qdict_from_vjsonf_nofail()) pass
&error_abort to the parser.
However, there's another, more dangerous kind of programming error:
since we use va_arg() to get the value to interpolate, behavior is
undefined when the variable argument isn't consistent with the
interpolation specification.
The same problem exists with printf()-like functions, and the solution
is to have the compiler check consistency. This is what
GCC_FMT_ATTR() is about.
To enable this type checking for interpolation as well, we carefully
chose our interpolation specifications to match printf conversion
specifications, and decorate functions parsing templates with
GCC_FMT_ATTR().
Note that this only protects against undefined behavior due to type
errors. It can't protect against use of invalid interpolation
specifications that happen to be valid printf conversion
specifications.
However, there's still a gaping hole in the type checking: GCC
recognizes '%' as start of printf conversion specification anywhere in
the template, but the parser recognizes it only outside JSON strings.
For instance, if someone were to pass a "{ '%s': %d }" template, GCC
would require a char * and an int argument, but the parser would
va_arg() only an int argument, resulting in undefined behavior.
Avoid undefined behavior by catching the programming error at run
time: have the parser recognize and reject '%' in JSON strings.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180823164025.12553-57-armbru@redhat.com>
2018-08-23 18:40:23 +02:00
|
|
|
if (g_test_subprocess()) {
|
|
|
|
qobject_from_jsonf_nofail("['%s', %s]", "eins", "zwei");
|
|
|
|
}
|
|
|
|
g_test_trap_subprocess(NULL, 0, 0);
|
|
|
|
g_test_trap_assert_failed();
|
|
|
|
g_test_trap_assert_stderr("*Unexpected error*"
|
|
|
|
"can't interpolate into string*");
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void simple_dict(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct {
|
|
|
|
const char *encoded;
|
2017-08-25 12:59:02 +02:00
|
|
|
QLitObject decoded;
|
2009-11-11 17:39:47 +01:00
|
|
|
} test_cases[] = {
|
|
|
|
{
|
2009-11-11 20:16:03 +01:00
|
|
|
.encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QDICT(((QLitDictEntry[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
{ "foo", QLIT_QNUM(42) },
|
2009-11-11 17:39:47 +01:00
|
|
|
{ "bar", QLIT_QSTR("hello world") },
|
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
}, {
|
|
|
|
.encoded = "{}",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QDICT(((QLitDictEntry[]){
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
}, {
|
2009-11-11 20:16:03 +01:00
|
|
|
.encoded = "{\"foo\": 43}",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QDICT(((QLitDictEntry[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
{ "foo", QLIT_QNUM(43) },
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; test_cases[i].encoded; i++) {
|
|
|
|
QObject *obj;
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *str;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
|
2017-08-25 12:59:05 +02:00
|
|
|
g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj));
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2009-11-11 20:16:03 +01:00
|
|
|
str = qobject_to_json(obj);
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2009-11-11 20:16:03 +01:00
|
|
|
|
2020-12-11 18:11:37 +01:00
|
|
|
obj = qobject_from_json(str->str, &error_abort);
|
2017-08-25 12:59:05 +02:00
|
|
|
g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj));
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_string_free(str, true);
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-15 20:45:44 +02:00
|
|
|
/*
|
|
|
|
* this generates json of the form:
|
|
|
|
* a(0,m) = [0, 1, ..., m-1]
|
|
|
|
* a(n,m) = {
|
|
|
|
* 'key0': a(0,m),
|
|
|
|
* 'key1': a(1,m),
|
|
|
|
* ...
|
|
|
|
* 'key(n-1)': a(n-1,m)
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
static void gen_test_json(GString *gstr, int nest_level_max,
|
|
|
|
int elem_count)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
g_assert(gstr);
|
|
|
|
if (nest_level_max == 0) {
|
|
|
|
g_string_append(gstr, "[");
|
|
|
|
for (i = 0; i < elem_count; i++) {
|
|
|
|
g_string_append_printf(gstr, "%d", i);
|
|
|
|
if (i < elem_count - 1) {
|
|
|
|
g_string_append_printf(gstr, ", ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_string_append(gstr, "]");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_string_append(gstr, "{");
|
|
|
|
for (i = 0; i < nest_level_max; i++) {
|
|
|
|
g_string_append_printf(gstr, "'key%d': ", i);
|
|
|
|
gen_test_json(gstr, i, elem_count);
|
|
|
|
if (i < nest_level_max - 1) {
|
|
|
|
g_string_append(gstr, ",");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_string_append(gstr, "}");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void large_dict(void)
|
|
|
|
{
|
|
|
|
GString *gstr = g_string_new("");
|
|
|
|
QObject *obj;
|
|
|
|
|
|
|
|
gen_test_json(gstr, 10, 100);
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json(gstr->str, &error_abort);
|
2012-08-15 20:45:44 +02:00
|
|
|
g_assert(obj != NULL);
|
|
|
|
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2012-08-15 20:45:44 +02:00
|
|
|
g_string_free(gstr, true);
|
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void simple_list(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct {
|
|
|
|
const char *encoded;
|
2017-08-25 12:59:02 +02:00
|
|
|
QLitObject decoded;
|
2009-11-11 17:39:47 +01:00
|
|
|
} test_cases[] = {
|
|
|
|
{
|
|
|
|
.encoded = "[43,42]",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QLIST(((QLitObject[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(43),
|
|
|
|
QLIT_QNUM(42),
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.encoded = "[43]",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QLIST(((QLitObject[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(43),
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.encoded = "[]",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QLIST(((QLitObject[]){
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.encoded = "[{}]",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QLIST(((QLitObject[]){
|
|
|
|
QLIT_QDICT(((QLitDictEntry[]){
|
2009-11-11 17:39:47 +01:00
|
|
|
{},
|
|
|
|
})),
|
|
|
|
{},
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; test_cases[i].encoded; i++) {
|
|
|
|
QObject *obj;
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *str;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
|
2017-08-25 12:59:05 +02:00
|
|
|
g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj));
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2009-11-11 20:16:03 +01:00
|
|
|
str = qobject_to_json(obj);
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2009-11-11 20:16:03 +01:00
|
|
|
|
2020-12-11 18:11:37 +01:00
|
|
|
obj = qobject_from_json(str->str, &error_abort);
|
2017-08-25 12:59:05 +02:00
|
|
|
g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj));
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_string_free(str, true);
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void simple_whitespace(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct {
|
|
|
|
const char *encoded;
|
2017-08-25 12:59:02 +02:00
|
|
|
QLitObject decoded;
|
2009-11-11 17:39:47 +01:00
|
|
|
} test_cases[] = {
|
|
|
|
{
|
|
|
|
.encoded = " [ 43 , 42 ]",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QLIST(((QLitObject[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(43),
|
|
|
|
QLIT_QNUM(42),
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{
|
2018-08-23 18:39:31 +02:00
|
|
|
.encoded = "\t[ 43 , { 'h' : 'b' },\r\n\t[ ], 42 ]\n",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QLIST(((QLitObject[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(43),
|
2017-08-25 12:59:02 +02:00
|
|
|
QLIT_QDICT(((QLitDictEntry[]){
|
2009-11-11 17:39:47 +01:00
|
|
|
{ "h", QLIT_QSTR("b") },
|
|
|
|
{ }})),
|
2017-08-25 12:59:02 +02:00
|
|
|
QLIT_QLIST(((QLitObject[]){
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }})),
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(42),
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
|
2017-08-25 12:59:02 +02:00
|
|
|
.decoded = QLIT_QLIST(((QLitObject[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(43),
|
2017-08-25 12:59:02 +02:00
|
|
|
QLIT_QDICT(((QLitDictEntry[]){
|
2009-11-11 17:39:47 +01:00
|
|
|
{ "h", QLIT_QSTR("b") },
|
2017-06-07 18:35:58 +02:00
|
|
|
{ "a", QLIT_QNUM(32) },
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }})),
|
2017-08-25 12:59:02 +02:00
|
|
|
QLIT_QLIST(((QLitObject[]){
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }})),
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(42),
|
2009-11-11 17:39:47 +01:00
|
|
|
{ }
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; test_cases[i].encoded; i++) {
|
|
|
|
QObject *obj;
|
2020-12-11 18:11:37 +01:00
|
|
|
GString *str;
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
|
2017-08-25 12:59:05 +02:00
|
|
|
g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj));
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2009-11-11 20:16:03 +01:00
|
|
|
str = qobject_to_json(obj);
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2009-11-11 20:16:03 +01:00
|
|
|
|
2020-12-11 18:11:37 +01:00
|
|
|
obj = qobject_from_json(str->str, &error_abort);
|
2017-08-25 12:59:05 +02:00
|
|
|
g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj));
|
2009-11-11 20:16:03 +01:00
|
|
|
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2020-12-11 18:11:37 +01:00
|
|
|
g_string_free(str, true);
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
static void simple_interpolation(void)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
|
|
|
QObject *embedded_obj;
|
|
|
|
QObject *obj;
|
2017-08-25 12:59:02 +02:00
|
|
|
QLitObject decoded = QLIT_QLIST(((QLitObject[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(1),
|
2018-08-23 18:40:24 +02:00
|
|
|
QLIT_QSTR("100%"),
|
2017-08-25 12:59:02 +02:00
|
|
|
QLIT_QLIST(((QLitObject[]){
|
2017-06-07 18:35:58 +02:00
|
|
|
QLIT_QNUM(32),
|
|
|
|
QLIT_QNUM(42),
|
2009-11-11 17:39:47 +01:00
|
|
|
{}})),
|
|
|
|
{}}));
|
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
embedded_obj = qobject_from_json("[32, 42]", &error_abort);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(embedded_obj != NULL);
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2018-08-23 18:40:24 +02:00
|
|
|
obj = qobject_from_jsonf_nofail("[%d, '100%%', %p]", 1, embedded_obj);
|
2017-08-25 12:59:05 +02:00
|
|
|
g_assert(qlit_equal_qobject(&decoded, obj));
|
2009-11-11 17:39:47 +01:00
|
|
|
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void empty_input(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2018-08-23 18:40:14 +02:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj;
|
|
|
|
|
|
|
|
obj = qobject_from_json("", &err);
|
|
|
|
error_free_or_abort(&err);
|
2018-08-23 18:39:30 +02:00
|
|
|
g_assert(obj == NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void blank_input(void)
|
|
|
|
{
|
2018-08-23 18:40:14 +02:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj;
|
|
|
|
|
|
|
|
obj = qobject_from_json("\n ", &err);
|
|
|
|
error_free_or_abort(&err);
|
2018-08-23 18:39:30 +02:00
|
|
|
g_assert(obj == NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void junk_input(void)
|
|
|
|
{
|
|
|
|
/* Note: junk within strings is covered elsewhere */
|
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj;
|
|
|
|
|
|
|
|
obj = qobject_from_json("@", &err);
|
2018-08-23 18:40:06 +02:00
|
|
|
error_free_or_abort(&err);
|
2018-08-23 18:39:30 +02:00
|
|
|
g_assert(obj == NULL);
|
|
|
|
|
2018-08-23 18:39:42 +02:00
|
|
|
obj = qobject_from_json("{\x01", &err);
|
2018-08-23 18:40:06 +02:00
|
|
|
error_free_or_abort(&err);
|
2018-08-23 18:39:42 +02:00
|
|
|
g_assert(obj == NULL);
|
|
|
|
|
2018-08-23 18:39:30 +02:00
|
|
|
obj = qobject_from_json("[0\xFF]", &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(obj == NULL);
|
|
|
|
|
|
|
|
obj = qobject_from_json("00", &err);
|
2018-08-23 18:40:06 +02:00
|
|
|
error_free_or_abort(&err);
|
2018-08-23 18:39:30 +02:00
|
|
|
g_assert(obj == NULL);
|
|
|
|
|
|
|
|
obj = qobject_from_json("[1e", &err);
|
2018-08-23 18:40:06 +02:00
|
|
|
error_free_or_abort(&err);
|
2018-08-23 18:39:30 +02:00
|
|
|
g_assert(obj == NULL);
|
|
|
|
|
|
|
|
obj = qobject_from_json("truer", &err);
|
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_string(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("\"abc", &err);
|
2018-08-23 18:40:06 +02:00
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_sq_string(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("'abc", &err);
|
2018-08-23 18:40:06 +02:00
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_escape(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("\"abc\\\"", &err);
|
2018-08-23 18:40:06 +02:00
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_array(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("[32", &err);
|
2018-08-23 18:40:12 +02:00
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_array_comma(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("[32,", &err);
|
2018-08-23 18:40:12 +02:00
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void invalid_array_comma(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("[32,}", &err);
|
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_dict(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("{'abc':32", &err);
|
2018-08-23 18:40:12 +02:00
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_dict_comma(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("{'abc':32,", &err);
|
2018-08-23 18:40:12 +02:00
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void invalid_dict_comma(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("{'abc':32,}", &err);
|
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2020-11-13 15:55:25 +01:00
|
|
|
static void invalid_dict_key(void)
|
|
|
|
{
|
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("{32:'abc'}", &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(obj == NULL);
|
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
static void unterminated_literal(void)
|
2010-05-24 09:39:51 +02:00
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj = qobject_from_json("nul", &err);
|
|
|
|
error_free_or_abort(&err);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_assert(obj == NULL);
|
2010-05-24 09:39:51 +02:00
|
|
|
}
|
|
|
|
|
2015-11-25 22:23:24 +01:00
|
|
|
static char *make_nest(char *buf, size_t cnt)
|
|
|
|
{
|
|
|
|
memset(buf, '[', cnt - 1);
|
|
|
|
buf[cnt - 1] = '{';
|
|
|
|
buf[cnt] = '}';
|
|
|
|
memset(buf + cnt + 1, ']', cnt - 1);
|
|
|
|
buf[2 * cnt] = 0;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void limits_nesting(void)
|
|
|
|
{
|
2017-02-28 22:27:00 +01:00
|
|
|
Error *err = NULL;
|
2015-11-25 22:23:24 +01:00
|
|
|
enum { max_nesting = 1024 }; /* see qobject/json-streamer.c */
|
|
|
|
char buf[2 * (max_nesting + 1) + 1];
|
|
|
|
QObject *obj;
|
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json(make_nest(buf, max_nesting), &error_abort);
|
2015-11-25 22:23:24 +01:00
|
|
|
g_assert(obj != NULL);
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(obj);
|
2015-11-25 22:23:24 +01:00
|
|
|
|
2017-02-28 22:27:00 +01:00
|
|
|
obj = qobject_from_json(make_nest(buf, max_nesting + 1), &err);
|
|
|
|
error_free_or_abort(&err);
|
2015-11-25 22:23:24 +01:00
|
|
|
g_assert(obj == NULL);
|
|
|
|
}
|
|
|
|
|
2018-08-23 18:39:29 +02:00
|
|
|
static void multiple_values(void)
|
|
|
|
{
|
|
|
|
Error *err = NULL;
|
|
|
|
QObject *obj;
|
|
|
|
|
|
|
|
obj = qobject_from_json("false true", &err);
|
qjson: Fix qobject_from_json() & friends for multiple values
qobject_from_json() & friends use the consume_json() callback to
receive either a value or an error from the parser.
When they are fed a string that contains more than either one JSON
value or one JSON syntax error, consume_json() gets called multiple
times.
When the last call receives a value, qobject_from_json() returns that
value. Any other values are leaked.
When any call receives an error, qobject_from_json() sets the first
error received. Any other errors are thrown away.
When values follow errors, qobject_from_json() returns both a value
and sets an error. That's bad. Impact:
* block.c's parse_json_protocol() ignores and leaks the value. It's
used to to parse pseudo-filenames starting with "json:". The
pseudo-filenames can come from the user or from image meta-data such
as a QCOW2 image's backing file name.
* vl.c's parse_display_qapi() ignores and leaks the error. It's used
to parse the argument of command line option -display.
* vl.c's main() case QEMU_OPTION_blockdev ignores the error and leaves
it in @err. main() will then pass a pointer to a non-null Error *
to net_init_clients(), which is forbidden. It can lead to assertion
failure or other misbehavior.
* check-qjson.c's multiple_values() demonstrates the badness.
* The other callers are not affected since they only pass strings with
exactly one JSON value or, in the case of negative tests, one
error.
The impact on the _nofail() functions is relatively harmless. They
abort when any call receives an error. Else they return the last
value, and leak the others, if any.
Fix consume_json() as follows. On the first call, save value and
error as before. On subsequent calls, if any, don't save them. If
the first call saved a value, the next call, if any, replaces the
value by an "Expecting at most one JSON value" error. Take care not
to leak values or errors that aren't saved.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180823164025.12553-44-armbru@redhat.com>
2018-08-23 18:40:10 +02:00
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(obj == NULL);
|
2018-08-23 18:39:29 +02:00
|
|
|
|
|
|
|
obj = qobject_from_json("} true", &err);
|
|
|
|
error_free_or_abort(&err);
|
qjson: Fix qobject_from_json() & friends for multiple values
qobject_from_json() & friends use the consume_json() callback to
receive either a value or an error from the parser.
When they are fed a string that contains more than either one JSON
value or one JSON syntax error, consume_json() gets called multiple
times.
When the last call receives a value, qobject_from_json() returns that
value. Any other values are leaked.
When any call receives an error, qobject_from_json() sets the first
error received. Any other errors are thrown away.
When values follow errors, qobject_from_json() returns both a value
and sets an error. That's bad. Impact:
* block.c's parse_json_protocol() ignores and leaks the value. It's
used to to parse pseudo-filenames starting with "json:". The
pseudo-filenames can come from the user or from image meta-data such
as a QCOW2 image's backing file name.
* vl.c's parse_display_qapi() ignores and leaks the error. It's used
to parse the argument of command line option -display.
* vl.c's main() case QEMU_OPTION_blockdev ignores the error and leaves
it in @err. main() will then pass a pointer to a non-null Error *
to net_init_clients(), which is forbidden. It can lead to assertion
failure or other misbehavior.
* check-qjson.c's multiple_values() demonstrates the badness.
* The other callers are not affected since they only pass strings with
exactly one JSON value or, in the case of negative tests, one
error.
The impact on the _nofail() functions is relatively harmless. They
abort when any call receives an error. Else they return the last
value, and leak the others, if any.
Fix consume_json() as follows. On the first call, save value and
error as before. On subsequent calls, if any, don't save them. If
the first call saved a value, the next call, if any, replaces the
value by an "Expecting at most one JSON value" error. Take care not
to leak values or errors that aren't saved.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180823164025.12553-44-armbru@redhat.com>
2018-08-23 18:40:10 +02:00
|
|
|
g_assert(obj == NULL);
|
2018-08-23 18:39:29 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
int main(int argc, char **argv)
|
2009-11-11 17:39:47 +01:00
|
|
|
{
|
2012-01-10 20:10:49 +01:00
|
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
|
|
|
|
g_test_add_func("/literals/string/escaped", escaped_string);
|
2018-08-23 18:39:38 +02:00
|
|
|
g_test_add_func("/literals/string/quotes", string_with_quotes);
|
2013-02-05 09:30:55 +01:00
|
|
|
g_test_add_func("/literals/string/utf8", utf8_string);
|
2012-01-10 20:10:49 +01:00
|
|
|
|
2020-12-10 17:14:45 +01:00
|
|
|
g_test_add_func("/literals/number/int", int_number);
|
|
|
|
g_test_add_func("/literals/number/uint", uint_number);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_test_add_func("/literals/number/float", float_number);
|
|
|
|
|
|
|
|
g_test_add_func("/literals/keyword", keyword_literal);
|
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
g_test_add_func("/literals/interpolation/valid", interpolation_valid);
|
2023-07-14 13:33:27 +02:00
|
|
|
g_test_add_func("/literals/interpolation/unknown", interpolation_unknown);
|
2018-08-23 18:39:43 +02:00
|
|
|
g_test_add_func("/literals/interpolation/string", interpolation_string);
|
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
g_test_add_func("/dicts/simple_dict", simple_dict);
|
2012-08-15 20:45:44 +02:00
|
|
|
g_test_add_func("/dicts/large_dict", large_dict);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_test_add_func("/lists/simple_list", simple_list);
|
|
|
|
|
2018-08-23 18:39:43 +02:00
|
|
|
g_test_add_func("/mixed/simple_whitespace", simple_whitespace);
|
|
|
|
g_test_add_func("/mixed/interpolation", simple_interpolation);
|
2012-01-10 20:10:49 +01:00
|
|
|
|
2018-08-23 18:39:30 +02:00
|
|
|
g_test_add_func("/errors/empty", empty_input);
|
|
|
|
g_test_add_func("/errors/blank", blank_input);
|
|
|
|
g_test_add_func("/errors/junk", junk_input);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_test_add_func("/errors/unterminated/string", unterminated_string);
|
|
|
|
g_test_add_func("/errors/unterminated/escape", unterminated_escape);
|
|
|
|
g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string);
|
|
|
|
g_test_add_func("/errors/unterminated/array", unterminated_array);
|
|
|
|
g_test_add_func("/errors/unterminated/array_comma", unterminated_array_comma);
|
|
|
|
g_test_add_func("/errors/unterminated/dict", unterminated_dict);
|
|
|
|
g_test_add_func("/errors/unterminated/dict_comma", unterminated_dict_comma);
|
|
|
|
g_test_add_func("/errors/invalid_array_comma", invalid_array_comma);
|
|
|
|
g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma);
|
2020-11-13 15:55:25 +01:00
|
|
|
g_test_add_func("/errors/invalid_dict_key", invalid_dict_key);
|
2012-01-10 20:10:49 +01:00
|
|
|
g_test_add_func("/errors/unterminated/literal", unterminated_literal);
|
2015-11-25 22:23:24 +01:00
|
|
|
g_test_add_func("/errors/limits/nesting", limits_nesting);
|
2018-08-23 18:39:29 +02:00
|
|
|
g_test_add_func("/errors/multiple_values", multiple_values);
|
2010-05-24 09:39:51 +02:00
|
|
|
|
2012-01-10 20:10:49 +01:00
|
|
|
return g_test_run();
|
2009-11-11 17:39:47 +01:00
|
|
|
}
|