mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-03 03:25:20 +00:00
json: reject trailing garbage
Yajl 2 has a nice feature that it can be configured whether to allow multiple JSON objects parsed from a single stream, defaulting to off. And yajl 1.0.12 at least provided a way to tell if all input bytes were parsed, or if trailing bytes remained after a valid JSON object was parsed. But we target RHEL 6 yajl 1.0.7, which has neither of these. So fake it by always parsing '[...]' instead, so that trailing garbage either trips up the array parse, or is easily detected when unwrapping the result. * src/util/virjson.c (virJSONValueFromString): With older json, wrap text to avoid trailing garbage. * tests/jsontest.c (mymain): Add tests for this. Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
54dbba5bc3
commit
7cd991b74c
@ -1597,6 +1597,7 @@ virJSONValueFromString(const char *jsonstring)
|
||||
size_t len = strlen(jsonstring);
|
||||
# ifndef WITH_YAJL2
|
||||
yajl_parser_config cfg = { 0, 1 };
|
||||
virJSONValuePtr tmp;
|
||||
# endif
|
||||
|
||||
VIR_DEBUG("string=%s", jsonstring);
|
||||
@ -1616,7 +1617,21 @@ virJSONValueFromString(const char *jsonstring)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Yajl 2 is nice enough to default to rejecting trailing garbage.
|
||||
* Yajl 1.0.12 has yajl_get_bytes_consumed to make that detection
|
||||
* simpler. But we're stuck with yajl 1.0.7 on RHEL 6, which
|
||||
* happily quits parsing at the end of a valid JSON construct,
|
||||
* with no visibility into how much more input remains. Wrapping
|
||||
* things in an array forces yajl to confess the truth. */
|
||||
# ifdef WITH_YAJL2
|
||||
rc = yajl_parse(hand, (const unsigned char *)jsonstring, len);
|
||||
# else
|
||||
rc = yajl_parse(hand, (const unsigned char *)"[", 1);
|
||||
if (VIR_YAJL_STATUS_OK(rc))
|
||||
rc = yajl_parse(hand, (const unsigned char *)jsonstring, len);
|
||||
if (VIR_YAJL_STATUS_OK(rc))
|
||||
rc = yajl_parse(hand, (const unsigned char *)"]", 1);
|
||||
# endif
|
||||
if (!VIR_YAJL_STATUS_OK(rc) ||
|
||||
yajl_complete_parse(hand) != yajl_status_ok) {
|
||||
unsigned char *errstr = yajl_get_error(hand, 1,
|
||||
@ -1638,6 +1653,18 @@ virJSONValueFromString(const char *jsonstring)
|
||||
virJSONValueFree(parser.head);
|
||||
} else {
|
||||
ret = parser.head;
|
||||
# ifndef WITH_YAJL2
|
||||
/* Undo the array wrapping above */
|
||||
tmp = ret;
|
||||
ret = NULL;
|
||||
if (virJSONValueArraySize(tmp) > 1)
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot parse json %s: too many items present"),
|
||||
jsonstring);
|
||||
else
|
||||
ret = virJSONValueArraySteal(tmp, 0);
|
||||
virJSONValueFree(tmp);
|
||||
# endif
|
||||
}
|
||||
|
||||
cleanup:
|
||||
@ -1650,7 +1677,7 @@ virJSONValueFromString(const char *jsonstring)
|
||||
VIR_FREE(parser.state);
|
||||
}
|
||||
|
||||
VIR_DEBUG("result=%p", parser.head);
|
||||
VIR_DEBUG("result=%p", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -419,6 +419,8 @@ mymain(void)
|
||||
DO_TEST_PARSE_FAIL("overdone keyword", "[ truest ]");
|
||||
DO_TEST_PARSE_FAIL("unknown keyword", "huh");
|
||||
DO_TEST_PARSE_FAIL("comments", "[ /* nope */\n1 // not this either\n]");
|
||||
DO_TEST_PARSE_FAIL("trailing garbage", "[] []");
|
||||
DO_TEST_PARSE_FAIL("list without array", "1, 1");
|
||||
|
||||
DO_TEST_PARSE_FAIL("object with numeric keys", "{ 1:1, 2:1, 3:2 }");
|
||||
DO_TEST_PARSE_FAIL("unterminated object", "{ \"1\":1, \"2\":1, \"3\":2");
|
||||
|
Loading…
Reference in New Issue
Block a user